Files
runc/libcontainer/cgroups/systemd/cpuset.go
T
Chengen, Du 77cae9addc cgroups: cpuset: fix byte order while parsing cpuset range to bits
Runc parses cpuset range to bits in the case of cgroup v2 + systemd as cgroup driver.
The byte order representation differs from systemd expectation, which will set
different cpuset range in systemd transient unit if the length of parsed byte array exceeds one.

	# cat config.json
	...
	"resources": {
		...
		"cpu": {
			"cpus": "10-23"
		}
	},
	...
	# runc --systemd-cgroup run test
	# cat /run/systemd/transient/runc-test.scope.d/50-AllowedCPUs.conf
	# This is a drop-in unit file extension, created via "systemctl set-property"
	# or an equivalent operation. Do not edit.
	[Scope]
	AllowedCPUs=0-7 10-15

The cpuset.cpus in cgroup will also be set to wrong value after reloading systemd manager configuration.

	# systemctl daemon-reload
	# cat /sys/fs/cgroup/system.slice/runc-test.scope/cpuset.cpus
	0-7,10-15

Signed-off-by: seyeongkim <seyeong.kim@canonical.com>
Signed-off-by: Chengen, Du <chengen.du@canonical.com>
2022-10-13 11:13:29 +08:00

61 lines
1.4 KiB
Go

package systemd
import (
"errors"
"math/big"
"strconv"
"strings"
)
// RangeToBits converts a text representation of a CPU mask (as written to
// or read from cgroups' cpuset.* files, e.g. "1,3-5") to a slice of bytes
// with the corresponding bits set (as consumed by systemd over dbus as
// AllowedCPUs/AllowedMemoryNodes unit property value).
func RangeToBits(str string) ([]byte, error) {
bits := new(big.Int)
for _, r := range strings.Split(str, ",") {
// allow extra spaces around
r = strings.TrimSpace(r)
// allow empty elements (extra commas)
if r == "" {
continue
}
ranges := strings.SplitN(r, "-", 2)
if len(ranges) > 1 {
start, err := strconv.ParseUint(ranges[0], 10, 32)
if err != nil {
return nil, err
}
end, err := strconv.ParseUint(ranges[1], 10, 32)
if err != nil {
return nil, err
}
if start > end {
return nil, errors.New("invalid range: " + r)
}
for i := start; i <= end; i++ {
bits.SetBit(bits, int(i), 1)
}
} else {
val, err := strconv.ParseUint(ranges[0], 10, 32)
if err != nil {
return nil, err
}
bits.SetBit(bits, int(val), 1)
}
}
ret := bits.Bytes()
if len(ret) == 0 {
// do not allow empty values
return nil, errors.New("empty value")
}
// fit cpuset parsing order in systemd
for l, r := 0, len(ret)-1; l < r; l, r = l+1, r-1 {
ret[l], ret[r] = ret[r], ret[l]
}
return ret, nil
}