@@ -17,12 +17,15 @@ limitations under the License.
1717package memory
1818
1919import (
20+ "errors"
2021 "fmt"
2122 "os"
2223 "path/filepath"
24+ "regexp"
2325 "strconv"
2426 "strings"
2527
28+ "k8s.io/apimachinery/pkg/api/resource"
2629 "k8s.io/klog/v2"
2730
2831 nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
@@ -40,9 +43,12 @@ const NvFeature = "nv"
4043// NumaFeature is the name of the feature set that holds all NUMA related features.
4144const NumaFeature = "numa"
4245
43- // SwapFeature is the name of the feature set that holds all Swap related features
46+ // SwapFeature is the name of the feature set that holds all Swap related features.
4447const SwapFeature = "swap"
4548
49+ // HugePages is the name of the feature set that holds information about huge pages.
50+ const HugePages = "hugepages"
51+
4652// memorySource implements the FeatureSource and LabelSource interfaces.
4753type memorySource struct {
4854 features * nfdv1alpha1.Features
@@ -115,6 +121,13 @@ func (s *memorySource) Discover() error {
115121 s .features .Instances [NvFeature ] = nfdv1alpha1.InstanceFeatureSet {Elements : nv }
116122 }
117123
124+ // Detect Huge Pages
125+ if hp , err := detectHugePages (); err != nil {
126+ klog .ErrorS (err , "failed to detect huge pages" )
127+ } else {
128+ s .features .Attributes [HugePages ] = nfdv1alpha1.AttributeFeatureSet {Elements : hp }
129+ }
130+
118131 klog .V (3 ).InfoS ("discovered features" , "featureSource" , s .Name (), "features" , utils .DelayedDumper (s .features ))
119132
120133 return nil
@@ -179,6 +192,71 @@ func detectNv() ([]nfdv1alpha1.InstanceFeature, error) {
179192 return info , nil
180193}
181194
195+ // detectHugePages checks whether huge pages are enabled on the node
196+ // and retrieves the configured huge page sizes.
197+ func detectHugePages () (map [string ]string , error ) {
198+ hugePages := map [string ]string {
199+ "enabled" : "false" ,
200+ }
201+
202+ basePath := hostpath .SysfsDir .Path ("kernel/mm/hugepages" )
203+ subdirs , err := os .ReadDir (basePath )
204+ if err != nil {
205+ if errors .Is (err , os .ErrNotExist ) {
206+ return hugePages , nil
207+ }
208+ return nil , fmt .Errorf ("unable to read huge pages size: %w" , err )
209+ }
210+
211+ reg := regexp .MustCompile (`hugepages-(\d+)` )
212+ for _ , entry := range subdirs {
213+ if ! entry .IsDir () {
214+ continue
215+ }
216+
217+ pageSize , err := getHugePageSize (basePath , entry .Name (), reg )
218+ if err != nil {
219+ klog .ErrorS (err , "skipping huge page entry" , "entry" , entry .Name ())
220+ continue
221+ }
222+ if pageSize != "" {
223+ hugePages ["enabled" ] = "true"
224+ hugePages [pageSize ] = "true"
225+ }
226+ }
227+
228+ return hugePages , nil
229+ }
230+
231+ func getHugePageSize (basePath , dirName string , reg * regexp.Regexp ) (string , error ) {
232+ matches := reg .FindStringSubmatch (dirName )
233+ if len (matches ) != 2 {
234+ return "" , fmt .Errorf ("unexpected directory format: %s" , dirName )
235+ }
236+
237+ totalPagesFile := filepath .Join (basePath , dirName , "nr_hugepages" )
238+ totalPagesRaw , err := os .ReadFile (totalPagesFile )
239+ if err != nil {
240+ return "" , fmt .Errorf ("unable to read total number of huge pages from the file name: %s" , totalPagesFile )
241+ }
242+
243+ totalPages , err := strconv .Atoi (strings .TrimSpace (string (totalPagesRaw )))
244+ if err != nil {
245+ return "" , fmt .Errorf ("unable to convert total pages to integer - %s: %s" , totalPagesFile , totalPagesRaw )
246+ }
247+ if totalPages < 1 {
248+ return "" , nil
249+ }
250+
251+ sizeFormat := matches [1 ] + "Ki"
252+ quantity , err := resource .ParseQuantity (sizeFormat )
253+ if err != nil {
254+ return "" , fmt .Errorf ("invalid size format: %s" , sizeFormat )
255+ }
256+
257+ return "hugepages-" + quantity .String (), nil
258+ }
259+
182260// ndDevAttrs is the list of sysfs files (under each nd device) that we're trying to read
183261var ndDevAttrs = []string {"devtype" , "mode" }
184262
0 commit comments