Skip to content

Commit e7deb5c

Browse files
authored
Merge pull request #2056 from mfranczy/hugepages
Introduce memory.hugepages feature
2 parents dc16a71 + 5f7c416 commit e7deb5c

File tree

5 files changed

+123
-1
lines changed

5 files changed

+123
-1
lines changed

docs/usage/customization-guide.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,9 @@ The following features are available for matching:
966966
| | | **`node_count`** | int | Number of NUMA nodes |
967967
| **`memory.swap`** | attribute | | | Swap enabled on node |
968968
| | | **`enabled`** | bool | `true` if swap partition detected, `false` otherwise |
969+
| **`memory.hugepages`** | attribute | | | Discovery of supported huge pages size on node |
970+
| | | **`enabled`** | bool | `true` if total number of huge pages (of any page size) have been configured, otherwise `false` |
971+
| | | **`hugepages-<page-size>`** | string | total number of huge pages (e.g., `hugepages-1Gi=16`) |
969972
| **`network.device`** | instance | | | Physical (non-virtual) network interfaces present in the system |
970973
| | | **`name`** | string | Name of the network interface |
971974
| | | **`<sysfs-attribute>`** | string | Sysfs network interface attribute, available attributes: `operstate`, `speed`, `sriov_numvfs`, `sriov_totalvfs` |

source/memory/memory.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ limitations under the License.
1717
package memory
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"os"
2223
"path/filepath"
2324
"strconv"
2425
"strings"
2526

27+
corev1 "k8s.io/api/core/v1"
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.
4144
const 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.
4447
const 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.
4753
type 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,57 @@ 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+
for _, entry := range subdirs {
212+
if !entry.IsDir() {
213+
continue
214+
}
215+
216+
totalPages, err := getHugePagesTotalCount(basePath, entry.Name())
217+
if err != nil {
218+
klog.ErrorS(err, "unable to read hugepages total count", "hugepages", entry.Name())
219+
}
220+
pageSize := strings.TrimRight(strings.TrimPrefix(entry.Name(), "hugepages-"), "kB")
221+
quantity, err := resource.ParseQuantity(pageSize + "Ki")
222+
if err != nil {
223+
klog.ErrorS(err, "unable to parse quantity", "hugepages", entry.Name(), "pageSize", pageSize)
224+
continue
225+
}
226+
227+
hugePages[corev1.ResourceHugePagesPrefix+quantity.String()] = totalPages
228+
if v, err := strconv.Atoi(totalPages); err == nil && v > 0 {
229+
hugePages["enabled"] = "true"
230+
}
231+
}
232+
233+
return hugePages, nil
234+
}
235+
236+
func getHugePagesTotalCount(basePath, dirname string) (string, error) {
237+
totalPagesFile := filepath.Join(basePath, dirname, "nr_hugepages")
238+
totalPages, err := os.ReadFile(totalPagesFile)
239+
if err != nil {
240+
return "", fmt.Errorf("unable to read total number of huge pages from the file: %s", totalPagesFile)
241+
}
242+
243+
return strings.TrimSpace(string(totalPages)), nil
244+
}
245+
182246
// ndDevAttrs is the list of sysfs files (under each nd device) that we're trying to read
183247
var ndDevAttrs = []string{"devtype", "mode"}
184248

source/memory/memory_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ package memory
1919
import (
2020
"testing"
2121

22+
. "github.com/smartystreets/goconvey/convey"
2223
"github.com/stretchr/testify/assert"
24+
25+
"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
2326
)
2427

2528
func TestMemorySource(t *testing.T) {
@@ -61,3 +64,53 @@ func TestGetNumberofLinesFromFile(t *testing.T) {
6164
assert.Equal(t, tc.expectedLines, actual, "lines should match")
6265
}
6366
}
67+
68+
func TestDetectHugePages(t *testing.T) {
69+
70+
Convey("With configured 1Gi huge pages size", t, func() {
71+
hostpath.SysfsDir = "testdata/hugepages"
72+
73+
expectedHugePages := map[string]string{
74+
"enabled": "true",
75+
"hugepages-1Gi": "2",
76+
"hugepages-2Mi": "0",
77+
}
78+
hugePages, err := detectHugePages()
79+
assert.Nil(t, err)
80+
assert.Equal(t, hugePages, expectedHugePages)
81+
})
82+
83+
Convey("With invalid directory structure", t, func() {
84+
hostpath.SysfsDir = "invalid-dir"
85+
86+
expectedHugePages := map[string]string{
87+
"enabled": "false",
88+
}
89+
hugePages, err := detectHugePages()
90+
assert.Nil(t, err)
91+
assert.Equal(t, hugePages, expectedHugePages)
92+
})
93+
94+
}
95+
96+
func TestGetHugePagesTotalCount(t *testing.T) {
97+
98+
Convey("With configured total huge pages", t, func() {
99+
totalPages, err := getHugePagesTotalCount("testdata/hugepages/kernel/mm/hugepages", "hugepages-1048576kB")
100+
assert.Equal(t, "2", totalPages)
101+
assert.Nil(t, err)
102+
})
103+
104+
Convey("With not configured total huge pages", t, func() {
105+
totalPages, err := getHugePagesTotalCount("testdata/hugepages/kernel/mm/hugepages", "hugepages-2048kB")
106+
assert.Equal(t, "0", totalPages)
107+
assert.Nil(t, err)
108+
})
109+
110+
Convey("With invalid huge page directory", t, func() {
111+
totalPages, err := getHugePagesTotalCount("testdata/hugepages/kernel/mm/hugepages", "hugepages-invalid")
112+
assert.Equal(t, "", totalPages)
113+
assert.NotNil(t, err)
114+
})
115+
116+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0

0 commit comments

Comments
 (0)