Skip to content

Commit e1adc7f

Browse files
authored
Merge pull request #4077 from kolyshkin/1.1-4073
[1.1] libct/cg: support hugetlb rsvd
2 parents 5ba0e01 + 8214e63 commit e1adc7f

File tree

5 files changed

+174
-29
lines changed

5 files changed

+174
-29
lines changed

libcontainer/cgroups/fs/hugetlb.go

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package fs
22

33
import (
4+
"errors"
5+
"os"
46
"strconv"
57

68
"github.com/opencontainers/runc/libcontainer/cgroups"
@@ -19,8 +21,23 @@ func (s *HugetlbGroup) Apply(path string, _ *configs.Resources, pid int) error {
1921
}
2022

2123
func (s *HugetlbGroup) Set(path string, r *configs.Resources) error {
24+
const suffix = ".limit_in_bytes"
25+
skipRsvd := false
26+
2227
for _, hugetlb := range r.HugetlbLimit {
23-
if err := cgroups.WriteFile(path, "hugetlb."+hugetlb.Pagesize+".limit_in_bytes", strconv.FormatUint(hugetlb.Limit, 10)); err != nil {
28+
prefix := "hugetlb." + hugetlb.Pagesize
29+
val := strconv.FormatUint(hugetlb.Limit, 10)
30+
if err := cgroups.WriteFile(path, prefix+suffix, val); err != nil {
31+
return err
32+
}
33+
if skipRsvd {
34+
continue
35+
}
36+
if err := cgroups.WriteFile(path, prefix+".rsvd"+suffix, val); err != nil {
37+
if errors.Is(err, os.ErrNotExist) {
38+
skipRsvd = true
39+
continue
40+
}
2441
return err
2542
}
2643
}
@@ -32,24 +49,29 @@ func (s *HugetlbGroup) GetStats(path string, stats *cgroups.Stats) error {
3249
if !cgroups.PathExists(path) {
3350
return nil
3451
}
52+
rsvd := ".rsvd"
3553
hugetlbStats := cgroups.HugetlbStats{}
3654
for _, pageSize := range cgroups.HugePageSizes() {
37-
usage := "hugetlb." + pageSize + ".usage_in_bytes"
38-
value, err := fscommon.GetCgroupParamUint(path, usage)
55+
again:
56+
prefix := "hugetlb." + pageSize + rsvd
57+
58+
value, err := fscommon.GetCgroupParamUint(path, prefix+".usage_in_bytes")
3959
if err != nil {
60+
if rsvd != "" && errors.Is(err, os.ErrNotExist) {
61+
rsvd = ""
62+
goto again
63+
}
4064
return err
4165
}
4266
hugetlbStats.Usage = value
4367

44-
maxUsage := "hugetlb." + pageSize + ".max_usage_in_bytes"
45-
value, err = fscommon.GetCgroupParamUint(path, maxUsage)
68+
value, err = fscommon.GetCgroupParamUint(path, prefix+".max_usage_in_bytes")
4669
if err != nil {
4770
return err
4871
}
4972
hugetlbStats.MaxUsage = value
5073

51-
failcnt := "hugetlb." + pageSize + ".failcnt"
52-
value, err = fscommon.GetCgroupParamUint(path, failcnt)
74+
value, err = fscommon.GetCgroupParamUint(path, prefix+".failcnt")
5375
if err != nil {
5476
return err
5577
}

libcontainer/cgroups/fs/hugetlb_test.go

+36-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const (
2121
limit = "hugetlb.%s.limit_in_bytes"
2222
maxUsage = "hugetlb.%s.max_usage_in_bytes"
2323
failcnt = "hugetlb.%s.failcnt"
24+
25+
rsvdUsage = "hugetlb.%s.rsvd.usage_in_bytes"
26+
rsvdLimit = "hugetlb.%s.rsvd.limit_in_bytes"
27+
rsvdMaxUsage = "hugetlb.%s.rsvd.max_usage_in_bytes"
28+
rsvdFailcnt = "hugetlb.%s.rsvd.failcnt"
2429
)
2530

2631
func TestHugetlbSetHugetlb(t *testing.T) {
@@ -52,13 +57,15 @@ func TestHugetlbSetHugetlb(t *testing.T) {
5257
}
5358

5459
for _, pageSize := range cgroups.HugePageSizes() {
55-
limit := fmt.Sprintf(limit, pageSize)
56-
value, err := fscommon.GetCgroupParamUint(path, limit)
57-
if err != nil {
58-
t.Fatal(err)
59-
}
60-
if value != hugetlbAfter {
61-
t.Fatalf("Set hugetlb.limit_in_bytes failed. Expected: %v, Got: %v", hugetlbAfter, value)
60+
for _, f := range []string{limit, rsvdLimit} {
61+
limit := fmt.Sprintf(f, pageSize)
62+
value, err := fscommon.GetCgroupParamUint(path, limit)
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
if value != hugetlbAfter {
67+
t.Fatalf("Set %s failed. Expected: %v, Got: %v", limit, hugetlbAfter, value)
68+
}
6269
}
6370
}
6471
}
@@ -85,6 +92,28 @@ func TestHugetlbStats(t *testing.T) {
8592
}
8693
}
8794

95+
func TestHugetlbRStatsRsvd(t *testing.T) {
96+
path := tempDir(t, "hugetlb")
97+
for _, pageSize := range cgroups.HugePageSizes() {
98+
writeFileContents(t, path, map[string]string{
99+
fmt.Sprintf(rsvdUsage, pageSize): hugetlbUsageContents,
100+
fmt.Sprintf(rsvdMaxUsage, pageSize): hugetlbMaxUsageContents,
101+
fmt.Sprintf(rsvdFailcnt, pageSize): hugetlbFailcnt,
102+
})
103+
}
104+
105+
hugetlb := &HugetlbGroup{}
106+
actualStats := *cgroups.NewStats()
107+
err := hugetlb.GetStats(path, &actualStats)
108+
if err != nil {
109+
t.Fatal(err)
110+
}
111+
expectedStats := cgroups.HugetlbStats{Usage: 128, MaxUsage: 256, Failcnt: 100}
112+
for _, pageSize := range cgroups.HugePageSizes() {
113+
expectHugetlbStatEquals(t, expectedStats, actualStats.HugetlbStats[pageSize])
114+
}
115+
}
116+
88117
func TestHugetlbStatsNoUsageFile(t *testing.T) {
89118
path := tempDir(t, "hugetlb")
90119
writeFileContents(t, path, map[string]string{

libcontainer/cgroups/fs2/hugetlb.go

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package fs2
22

33
import (
4+
"errors"
5+
"os"
46
"strconv"
57

68
"github.com/opencontainers/runc/libcontainer/cgroups"
@@ -16,8 +18,22 @@ func setHugeTlb(dirPath string, r *configs.Resources) error {
1618
if !isHugeTlbSet(r) {
1719
return nil
1820
}
21+
const suffix = ".max"
22+
skipRsvd := false
1923
for _, hugetlb := range r.HugetlbLimit {
20-
if err := cgroups.WriteFile(dirPath, "hugetlb."+hugetlb.Pagesize+".max", strconv.FormatUint(hugetlb.Limit, 10)); err != nil {
24+
prefix := "hugetlb." + hugetlb.Pagesize
25+
val := strconv.FormatUint(hugetlb.Limit, 10)
26+
if err := cgroups.WriteFile(dirPath, prefix+suffix, val); err != nil {
27+
return err
28+
}
29+
if skipRsvd {
30+
continue
31+
}
32+
if err := cgroups.WriteFile(dirPath, prefix+".rsvd"+suffix, val); err != nil {
33+
if errors.Is(err, os.ErrNotExist) {
34+
skipRsvd = true
35+
continue
36+
}
2137
return err
2238
}
2339
}
@@ -27,15 +43,21 @@ func setHugeTlb(dirPath string, r *configs.Resources) error {
2743

2844
func statHugeTlb(dirPath string, stats *cgroups.Stats) error {
2945
hugetlbStats := cgroups.HugetlbStats{}
46+
rsvd := ".rsvd"
3047
for _, pagesize := range cgroups.HugePageSizes() {
31-
value, err := fscommon.GetCgroupParamUint(dirPath, "hugetlb."+pagesize+".current")
48+
again:
49+
prefix := "hugetlb." + pagesize + rsvd
50+
value, err := fscommon.GetCgroupParamUint(dirPath, prefix+".current")
3251
if err != nil {
52+
if rsvd != "" && errors.Is(err, os.ErrNotExist) {
53+
rsvd = ""
54+
goto again
55+
}
3356
return err
3457
}
3558
hugetlbStats.Usage = value
3659

37-
fileName := "hugetlb." + pagesize + ".events"
38-
value, err = fscommon.GetValueByKey(dirPath, fileName, "max")
60+
value, err = fscommon.GetValueByKey(dirPath, prefix+".events", "max")
3961
if err != nil {
4062
return err
4163
}

tests/integration/cgroups.bats

+64
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,70 @@ function setup() {
187187
[[ "$weights" == *"$major:$minor 444"* ]]
188188
}
189189

190+
# Convert size in KB to hugetlb size suffix.
191+
convert_hugetlb_size() {
192+
local size=$1
193+
local units=("KB" "MB" "GB")
194+
local idx=0
195+
196+
while ((size >= 1024)); do
197+
((size /= 1024))
198+
((idx++))
199+
done
200+
201+
echo "$size${units[$idx]}"
202+
}
203+
204+
@test "runc run (hugetlb limits)" {
205+
requires cgroups_hugetlb
206+
[ $EUID -ne 0 ] && requires rootless_cgroup
207+
# shellcheck disable=SC2012 # ls is fine here.
208+
mapfile -t sizes_kb < <(ls /sys/kernel/mm/hugepages/ | sed -e 's/.*hugepages-//' -e 's/kB$//') #
209+
if [ "${#sizes_kb[@]}" -lt 1 ]; then
210+
skip "requires hugetlb"
211+
fi
212+
213+
# Create two arrays:
214+
# - sizes: hugetlb cgroup file suffixes;
215+
# - limits: limits for each size.
216+
for size in "${sizes_kb[@]}"; do
217+
sizes+=("$(convert_hugetlb_size "$size")")
218+
# Limit to 1 page.
219+
limits+=("$((size * 1024))")
220+
done
221+
222+
# Set per-size limits.
223+
for ((i = 0; i < ${#sizes[@]}; i++)); do
224+
size="${sizes[$i]}"
225+
limit="${limits[$i]}"
226+
update_config '.linux.resources.hugepageLimits += [{ pagesize: "'"$size"'", limit: '"$limit"' }]'
227+
done
228+
229+
set_cgroups_path
230+
runc run -d --console-socket "$CONSOLE_SOCKET" test_hugetlb
231+
[ "$status" -eq 0 ]
232+
233+
lim="max"
234+
[ "$CGROUP_UNIFIED" = "no" ] && lim="limit_in_bytes"
235+
236+
optional=("")
237+
# Add rsvd, if available.
238+
if test -f "$(get_cgroup_path hugetlb)/hugetlb.${sizes[0]}.rsvd.$lim"; then
239+
optional+=(".rsvd")
240+
fi
241+
242+
# Check if the limits are as expected.
243+
for ((i = 0; i < ${#sizes[@]}; i++)); do
244+
size="${sizes[$i]}"
245+
limit="${limits[$i]}"
246+
for rsvd in "${optional[@]}"; do
247+
param="hugetlb.${size}${rsvd}.$lim"
248+
echo "checking $param"
249+
check_cgroup_value "$param" "$limit"
250+
done
251+
done
252+
}
253+
190254
@test "runc run (cgroup v2 resources.unified only)" {
191255
requires root cgroups_v2
192256

tests/integration/helpers.bash

+19-11
Original file line numberDiff line numberDiff line change
@@ -226,19 +226,27 @@ function set_cgroups_path() {
226226
update_config '.linux.cgroupsPath |= "'"${OCI_CGROUPS_PATH}"'"'
227227
}
228228

229-
# Get a value from a cgroup file.
230-
function get_cgroup_value() {
231-
local source=$1
232-
local cgroup var current
233-
229+
# Get a path to cgroup directory, based on controller name.
230+
# Parameters:
231+
# $1: controller name (like "pids") or a file name (like "pids.max").
232+
function get_cgroup_path() {
234233
if [ "$CGROUP_UNIFIED" = "yes" ]; then
235-
cgroup=$CGROUP_PATH
236-
else
237-
var=${source%%.*} # controller name (e.g. memory)
238-
var=CGROUP_${var^^}_BASE_PATH # variable name (e.g. CGROUP_MEMORY_BASE_PATH)
239-
eval cgroup=\$"${var}${REL_CGROUPS_PATH}"
234+
echo "$CGROUP_PATH"
235+
return
240236
fi
241-
cat "$cgroup/$source"
237+
238+
local var cgroup
239+
var=${1%%.*} # controller name (e.g. memory)
240+
var=CGROUP_${var^^}_BASE_PATH # variable name (e.g. CGROUP_MEMORY_BASE_PATH)
241+
eval cgroup=\$"${var}${REL_CGROUPS_PATH}"
242+
echo "$cgroup"
243+
}
244+
245+
# Get a value from a cgroup file.
246+
function get_cgroup_value() {
247+
local cgroup
248+
cgroup="$(get_cgroup_path "$1")"
249+
cat "$cgroup/$1"
242250
}
243251

244252
# Helper to check a if value in a cgroup file matches the expected one.

0 commit comments

Comments
 (0)