From 5032a16092d4ba9956fa364cb44e0c443f86461a 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 | 44 ++++++++++++++++++++++++++++++ tests/integration/cgroups.bats | 21 ++++++++++---- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/libcontainer/cgroups/systemd/v2.go b/libcontainer/cgroups/systemd/v2.go index e1a6622a0d3..1e641681dd6 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,39 @@ func NewUnifiedManager(config *configs.Cgroup, path string, rootless bool) cgrou } } +// unifiedResToSystemdProps tries to convert from Cgroup.Resources.Unified +// key/value map to systemd unit properties. This is on a best-effort basis, +// i.e. all the unknown parameters are ignored. +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) + } + 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", uint64(num))) + default: + logrus.Debugf("skipping unknown unified resource %q=%q", 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 +117,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 eb0d97a53e3..d6462b848dc 100644 --- a/tests/integration/cgroups.bats +++ b/tests/integration/cgroups.bats @@ -216,16 +216,22 @@ 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)" { requires root cgroups_v2 set_cgroups_path "$BUSYBOX_BUNDLE" - update_config ' .linux.resources.memory |= {"limit": 33554432} - | .linux.resources.memorySwap |= {"limit": 33554432} - | .linux.resources.unified |= - {"memory.min": "131072", "memory.max": "10485760" }' \ - "$BUSYBOX_BUNDLE" + update_config ' .linux.resources.memory |= { "limit": 33554432 } + | .linux.resources.memorySwap |= { "limit": 33554432 } + | .linux.resources.pids |= { "limit": 20 } + | .linux.resources.unified |= { + "memory.min": "131072", + "memory.max": "10485760", + "pids.max": "42" + }' "$BUSYBOX_BUNDLE" runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified [ "$status" -eq 0 ] @@ -237,4 +243,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 }