From 0cb8bf67a3418996820ae3e5be9a9504baa86115 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 29 Oct 2020 16:48:52 -0700 Subject: [PATCH] Initial v2 resources.unified systemd support In case systemd is used as cgroups manager, and a user sets some resources using unified resource map (as per [1]), systemd is not aware of any parameters, so there will be a discrepancy between the cgroupfs state and systemd unit state. Let's try to fix that by converting known unified resources to systemd properties. Currently, this is only implemented for pids.max as a POC. Some other parameters (that might or might not have systemd unit property equivalents) are: $ ls -l | grep w- -rw-r--r--. 1 root root 0 Oct 10 13:57 cgroup.freeze -rw-r--r--. 1 root root 0 Oct 10 13:57 cgroup.max.depth -rw-r--r--. 1 root root 0 Oct 10 13:57 cgroup.max.descendants -rw-r--r--. 1 root root 0 Oct 10 13:57 cgroup.procs -rw-r--r--. 1 root root 0 Oct 21 09:43 cgroup.subtree_control -rw-r--r--. 1 root root 0 Oct 10 13:57 cgroup.threads -rw-r--r--. 1 root root 0 Oct 10 13:57 cgroup.type -rw-r--r--. 1 root root 0 Oct 22 10:30 cpu.max -rw-r--r--. 1 root root 0 Oct 10 13:57 cpu.pressure -rw-r--r--. 1 root root 0 Oct 22 10:30 cpuset.cpus -rw-r--r--. 1 root root 0 Oct 22 10:30 cpuset.cpus.partition -rw-r--r--. 1 root root 0 Oct 22 10:30 cpuset.mems -rw-r--r--. 1 root root 0 Oct 22 10:30 cpu.weight -rw-r--r--. 1 root root 0 Oct 22 10:30 cpu.weight.nice -rw-r--r--. 1 root root 0 Oct 22 10:30 hugetlb.1GB.max -rw-r--r--. 1 root root 0 Oct 22 10:30 hugetlb.1GB.rsvd.max -rw-r--r--. 1 root root 0 Oct 22 10:30 hugetlb.2MB.max -rw-r--r--. 1 root root 0 Oct 22 10:30 hugetlb.2MB.rsvd.max -rw-r--r--. 1 root root 0 Oct 22 10:30 io.bfq.weight -rw-r--r--. 1 root root 0 Oct 22 10:30 io.latency -rw-r--r--. 1 root root 0 Oct 22 10:30 io.max -rw-r--r--. 1 root root 0 Oct 10 13:57 io.pressure -rw-r--r--. 1 root root 0 Oct 22 10:30 io.weight -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.high -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.low -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.max -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.min -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.oom.group -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.pressure -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.swap.high -rw-r--r--. 1 root root 0 Oct 10 13:57 memory.swap.max Surely, it is a manual conversion for every such case... [1] https://github.com/opencontainers/runtime-spec/pull/1040 Signed-off-by: Kir Kolyshkin --- libcontainer/cgroups/systemd/v2.go | 56 ++++++++++++++++++++++++++++++ tests/integration/cgroups.bats | 10 +++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/libcontainer/cgroups/systemd/v2.go b/libcontainer/cgroups/systemd/v2.go index e1a6622a0..09d8200be 100644 --- a/libcontainer/cgroups/systemd/v2.go +++ b/libcontainer/cgroups/systemd/v2.go @@ -3,6 +3,8 @@ package systemd import ( + "fmt" + "math" "os" "path/filepath" "strconv" @@ -34,6 +36,51 @@ func NewUnifiedManager(config *configs.Cgroup, path string, rootless bool) cgrou } } +// unifiedResToSystemdProps tries to convert from Cgroup.Resources.Unified +// key/value map (where key is cgroupfs file name) to systemd unit properties. +// This is on a best-effort basis, so the properties that are not known +// (to this function and/or systemd) are ignored (but logged with "debug" +// log level). +// +// For the list of keys, see https://www.kernel.org/doc/Documentation/cgroup-v2.txt +// +// For the list of systemd unit properties, see systemd.resource-control(5). +func unifiedResToSystemdProps(res map[string]string) (props []systemdDbus.Property, _ error) { + for k, v := range res { + if strings.Contains(k, "/") { + return nil, fmt.Errorf("unified resource %q must be a file name (no slashes)", k) + } + sk := strings.SplitN(k, ".", 2) + if len(sk) != 2 { + return nil, fmt.Errorf("unified resource %q must be in the form CONTROLLER.PARAMETER", k) + } + // Kernel is quite forgiving to extra whitespace + // around the value, and so should we. + v = strings.TrimSpace(v) + switch k { + case "pids.max": + num := uint64(math.MaxUint64) + if v != "max" { + var err error + num, err = strconv.ParseUint(v, 10, 64) + if err != nil { + return nil, fmt.Errorf("unified resource %q value conversion error: %w", k, err) + } + } + props = append(props, + newProp("TasksAccounting", true), + newProp("TasksMax", num)) + + default: + // Ignore the unknown resource here -- will still be + // applied in Set which calls fs2.Set. + logrus.Debugf("don't know how to convert unified resource %q=%q to systemd unit property; skipping (will still be applied to cgroupfs)", k, v) + } + } + + return props, nil +} + func genV2ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]systemdDbus.Property, error) { var properties []systemdDbus.Property r := c.Resources @@ -82,6 +129,15 @@ func genV2ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]syst // ignore r.KernelMemory + // convert Resources.Unified map to systemd properties + if r.Unified != nil { + unifiedProps, err := unifiedResToSystemdProps(r.Unified) + if err != nil { + return nil, err + } + properties = append(properties, unifiedProps...) + } + return properties, nil } diff --git a/tests/integration/cgroups.bats b/tests/integration/cgroups.bats index c8e594121..bbd39ba0f 100644 --- a/tests/integration/cgroups.bats +++ b/tests/integration/cgroups.bats @@ -216,6 +216,8 @@ function setup() { echo "$output" | grep -q '^memory.max:10485760$' echo "$output" | grep -q '^pids.max:99$' echo "$output" | grep -q '^cpu.max:10000 100000$' + + check_systemd_value "TasksMax" 99 } @test "runc run (cgroup v2 resources.unified override)" { @@ -226,7 +228,8 @@ function setup() { | .linux.resources.memorySwap |= {"limit": 33554432} | .linux.resources.unified |= { "memory.min": "131072", - "memory.max": "10485760" + "memory.max": "10485760", + "pids.max": "42" }' "$BUSYBOX_BUNDLE" runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified @@ -239,4 +242,9 @@ function setup() { runc exec test_cgroups_unified cat /sys/fs/cgroup/memory.max [ "$status" -eq 0 ] [ "$output" = '10485760' ] + + runc exec test_cgroups_unified cat /sys/fs/cgroup/pids.max + [ "$status" -eq 0 ] + [ "$output" = '42' ] + check_systemd_value "TasksMax" 42 }