Skip to content

Commit 13afa58

Browse files
committed
libct/cg/sd/v2: support cpuset.* / Allowed*
* cpuset.cpus -> AllowedCPUs * cpuset.mems -> AllowedMemoryNodes No test for cgroup v2 resources.unified override, as this requires a separate test case, and all the unified resources are handled uniformly so there's little sense to test all parameters. Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 5be8b97 commit 13afa58

File tree

13 files changed

+269
-213
lines changed

13 files changed

+269
-213
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ require (
2222
// NOTE: urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092
2323
github.com/urfave/cli v1.22.1
2424
github.com/vishvananda/netlink v1.1.0
25+
github.com/willf/bitset v1.1.11
2526
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f
2627
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7Zo
6262
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
6363
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM=
6464
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
65+
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
66+
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
6567
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6668
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6769
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package systemd
2+
3+
import (
4+
"encoding/binary"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/pkg/errors"
9+
"github.com/willf/bitset"
10+
)
11+
12+
// rangeToBits converts a text representation of a CPU mask (as written to
13+
// or read from cgroups' cpuset.* files, e.g. "1,3-5") to a slice of bytes
14+
// with the corresponding bits set (as consumed by systemd over dbus as
15+
// AllowedCPUs/AllowedMemoryNodes unit property value).
16+
func rangeToBits(str string) ([]byte, error) {
17+
bits := &bitset.BitSet{}
18+
19+
for _, r := range strings.Split(str, ",") {
20+
// allow extra spaces around
21+
r = strings.TrimSpace(r)
22+
// allow empty elements (extra commas)
23+
if r == "" {
24+
continue
25+
}
26+
ranges := strings.SplitN(r, "-", 2)
27+
if len(ranges) > 1 {
28+
start, err := strconv.ParseUint(ranges[0], 10, 32)
29+
if err != nil {
30+
return nil, err
31+
}
32+
end, err := strconv.ParseUint(ranges[1], 10, 32)
33+
if err != nil {
34+
return nil, err
35+
}
36+
if start > end {
37+
return nil, errors.New("invalid range: " + r)
38+
}
39+
for i := uint(start); i <= uint(end); i++ {
40+
bits.Set(i)
41+
}
42+
} else {
43+
val, err := strconv.ParseUint(ranges[0], 10, 32)
44+
if err != nil {
45+
return nil, err
46+
}
47+
bits.Set(uint(val))
48+
}
49+
}
50+
51+
val := bits.Bytes()
52+
if len(val) == 0 {
53+
// do not allow empty values
54+
return nil, errors.New("empty value")
55+
}
56+
ret := make([]byte, len(val)*8)
57+
for i := range val {
58+
// bitset uses BigEndian internally
59+
binary.BigEndian.PutUint64(ret[i*8:], val[len(val)-1-i])
60+
}
61+
// remove upper all-zero bytes
62+
for ret[0] == 0 {
63+
ret = ret[1:]
64+
}
65+
66+
return ret, nil
67+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package systemd
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestRangeToBits(t *testing.T) {
9+
testCases := []struct {
10+
in string
11+
out []byte
12+
isErr bool
13+
}{
14+
{in: "", isErr: true},
15+
{in: "0", out: []byte{1}},
16+
{in: "1", out: []byte{2}},
17+
{in: "0-1", out: []byte{3}},
18+
{in: "0,1", out: []byte{3}},
19+
{in: ",0,1,", out: []byte{3}},
20+
{in: "0-3", out: []byte{0x0f}},
21+
{in: "0,1,2-3", out: []byte{0x0f}},
22+
{in: "4-7", out: []byte{0xf0}},
23+
{in: "0-7", out: []byte{0xff}},
24+
{in: "0-15", out: []byte{0xff, 0xff}},
25+
{in: "16", out: []byte{1, 0, 0}},
26+
{in: "0-3,32-33", out: []byte{3, 0, 0, 0, 0x0f}},
27+
// extra spaces and tabs are ok
28+
{in: "1, 2, 1-2", out: []byte{6}},
29+
{in: " , 1 , 3 , 5-7, ", out: []byte{0xea}},
30+
// somewhat large values
31+
{in: "128-130,1", out: []byte{7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}},
32+
33+
{in: "-", isErr: true},
34+
{in: "1-", isErr: true},
35+
{in: "-3", isErr: true},
36+
// bad range (start > end)
37+
{in: "54-53", isErr: true},
38+
// kernel does not allow extra spaces inside a range
39+
{in: "1 - 2", isErr: true},
40+
}
41+
42+
for _, tc := range testCases {
43+
t.Logf("case: %q", tc.in)
44+
out, err := rangeToBits(tc.in)
45+
if err != nil {
46+
t.Logf(" got error: %s", err)
47+
if !tc.isErr {
48+
t.Error(" ^^^ unexpected error")
49+
}
50+
51+
continue
52+
}
53+
t.Logf(" expected %v, got %v", tc.out, out)
54+
if !bytes.Equal(out, tc.out) {
55+
t.Error(" ^^^ unexpected result")
56+
}
57+
}
58+
}

libcontainer/cgroups/systemd/v2.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ func unifiedResToSystemdProps(conn *systemdDbus.Conn, res map[string]string) (pr
9393
props = append(props,
9494
newProp("CPUWeight", num))
9595

96+
case "cpuset.cpus", "cpuset.mems":
97+
bits, err := rangeToBits(v)
98+
if err != nil {
99+
return nil, fmt.Errorf("unified resource %q=%q conversion error: %w", k, v, err)
100+
}
101+
m := map[string]string{
102+
"cpuset.cpus": "AllowedCPUs",
103+
"cpuset.mems": "AllowedMemoryNodes",
104+
}
105+
props = append(props,
106+
newProp(m[k], bits))
107+
96108
case "pids.max":
97109
num := uint64(math.MaxUint64)
98110
if v != "max" {

tests/integration/helpers.bash

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ function requires() {
357357
skip_me=1
358358
fi
359359
;;
360+
smp)
361+
local cpu_count=$(grep -c '^processor' /proc/cpuinfo)
362+
if [ "$cpu_count" -lt 2 ]; then
363+
skip_me=1
364+
fi
365+
;;
360366
systemd)
361367
if [ -z "${RUNC_USE_SYSTEMD}" ]; then
362368
skip_me=1

tests/integration/update.bats

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,53 @@ EOF
392392
check_systemd_value "TasksMax" 10
393393
}
394394

395+
@test "update cpuset parameters via v2 unified map" {
396+
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
397+
requires cgroups_v2 smp
398+
399+
update_config ' .linux.resources.unified |= {
400+
"cpuset.cpus": "0",
401+
"cpuset.mems": "0"
402+
}' "${BUSYBOX_BUNDLE}"
403+
runc run -d --console-socket "$CONSOLE_SOCKET" test_update
404+
[ "$status" -eq 0 ]
405+
406+
# check that initial values were properly set
407+
check_systemd_value "AllowedCPUs" 0
408+
check_systemd_value "AllowedMemoryNodes" 0
409+
410+
runc update -r - test_update <<EOF
411+
{
412+
"unified": {
413+
"cpuset.cpus": "1"
414+
}
415+
}
416+
EOF
417+
[ "$status" -eq 0 ]
418+
419+
# check the updated systemd unit properties
420+
check_systemd_value "AllowedCPUs" 1
421+
422+
# More than 1 numa memory node is required to test this
423+
file="/sys/fs/cgroup/cpuset.mems.effective"
424+
if ! test -r $file || grep -q '^0$' $file; then
425+
# skip the rest of it
426+
return 0
427+
fi
428+
429+
runc update -r - test_update <<EOF
430+
{
431+
"unified": {
432+
"cpuset.mems": "1"
433+
}
434+
}
435+
EOF
436+
[ "$status" -eq 0 ]
437+
438+
# check the updated systemd unit properties
439+
check_systemd_value "AllowedMemoryNodes" 1
440+
}
441+
395442
@test "update rt period and runtime" {
396443
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
397444
requires cgroups_v1 cgroups_rt no_systemd

0 commit comments

Comments
 (0)