From eee425f5a0b4e10f58bf4058e97a6c570583869a Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 13 Jan 2021 17:53:14 -0800 Subject: [PATCH 1/3] libct/cg/sd/systemdVersion: don't return error As the caller of this function just logs the error, it does not make sense to pass it. Instead, log it (once) and return -1. This is a preparation for the second user. Signed-off-by: Kir Kolyshkin --- libcontainer/cgroups/systemd/common.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/libcontainer/cgroups/systemd/common.go b/libcontainer/cgroups/systemd/common.go index 9a30e4e166d..7df198caa39 100644 --- a/libcontainer/cgroups/systemd/common.go +++ b/libcontainer/cgroups/systemd/common.go @@ -34,7 +34,6 @@ var ( versionOnce sync.Once version int - versionErr error isRunningSystemdOnce sync.Once isRunningSystemd bool @@ -372,19 +371,20 @@ func stopUnit(dbusConnection *systemdDbus.Conn, unitName string) error { return nil } -func systemdVersion(conn *systemdDbus.Conn) (int, error) { +func systemdVersion(conn *systemdDbus.Conn) int { versionOnce.Do(func() { version = -1 verStr, err := conn.GetManagerProperty("Version") - if err != nil { - versionErr = err - return + if err == nil { + version, err = systemdVersionAtoi(verStr) } - version, versionErr = systemdVersionAtoi(verStr) + if err != nil { + logrus.WithError(err).Error("unable to get systemd version") + } }) - return version, versionErr + return version } func systemdVersionAtoi(verStr string) (int, error) { @@ -405,10 +405,8 @@ func systemdVersionAtoi(verStr string) (int, error) { func addCpuQuota(conn *systemdDbus.Conn, properties *[]systemdDbus.Property, quota int64, period uint64) { if period != 0 { // systemd only supports CPUQuotaPeriodUSec since v242 - sdVer, err := systemdVersion(conn) - if err != nil { - logrus.Warnf("systemdVersion: %s", err) - } else if sdVer >= 242 { + sdVer := systemdVersion(conn) + if sdVer >= 242 { *properties = append(*properties, newProp("CPUQuotaPeriodUSec", period)) } From 03b512e511c587dd77047d0bf826c541672dbd60 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 8 Jan 2021 12:22:53 -0800 Subject: [PATCH 2/3] libc/cg: convert r.CPU.Cpus/Mems to systemd props Support for systemd properties AllowedCPUs and AllowedMemoryNodes was added by commit 13afa58d0e46ceb0, but only for unified resources of systemd v2 driver. This adds support for Cpu.Cpus and Cpu.Mems resources to both systemd v1 and v2 cgroup drivers. An integration test is added to check that the settings work. [v2: check for systemd version] [v3: same in the test] Signed-off-by: Kir Kolyshkin --- libcontainer/cgroups/systemd/common.go | 32 +++++++++++++++ libcontainer/cgroups/systemd/v1.go | 5 +++ libcontainer/cgroups/systemd/v2.go | 5 +++ tests/integration/update.bats | 56 ++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/libcontainer/cgroups/systemd/common.go b/libcontainer/cgroups/systemd/common.go index 7df198caa39..61f2d69f09d 100644 --- a/libcontainer/cgroups/systemd/common.go +++ b/libcontainer/cgroups/systemd/common.go @@ -432,3 +432,35 @@ func addCpuQuota(conn *systemdDbus.Conn, properties *[]systemdDbus.Property, quo newProp("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec)) } } + +func addCpuset(conn *systemdDbus.Conn, props *[]systemdDbus.Property, cpus, mems string) error { + if cpus == "" && mems == "" { + return nil + } + + // systemd only supports AllowedCPUs/AllowedMemoryNodes since v244 + sdVer := systemdVersion(conn) + if sdVer < 244 { + return nil + } + + if cpus != "" { + bits, err := rangeToBits(cpus) + if err != nil { + return fmt.Errorf("resources.CPU.Cpus=%q conversion error: %w", + cpus, err) + } + *props = append(*props, + newProp("AllowedCPUs", bits)) + } + if mems != "" { + bits, err := rangeToBits(mems) + if err != nil { + return fmt.Errorf("resources.CPU.Mems=%q conversion error: %w", + mems, err) + } + *props = append(*props, + newProp("AllowedMemoryNodes", bits)) + } + return nil +} diff --git a/libcontainer/cgroups/systemd/v1.go b/libcontainer/cgroups/systemd/v1.go index 2679b9993ab..64af1d94b3e 100644 --- a/libcontainer/cgroups/systemd/v1.go +++ b/libcontainer/cgroups/systemd/v1.go @@ -90,6 +90,11 @@ func genV1ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]syst newProp("TasksMax", uint64(r.PidsLimit))) } + err = addCpuset(conn, &properties, r.CpusetCpus, r.CpusetMems) + if err != nil { + return nil, err + } + return properties, nil } diff --git a/libcontainer/cgroups/systemd/v2.go b/libcontainer/cgroups/systemd/v2.go index 5691b43dfb2..782e4b5b4e3 100644 --- a/libcontainer/cgroups/systemd/v2.go +++ b/libcontainer/cgroups/systemd/v2.go @@ -201,6 +201,11 @@ func genV2ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]syst newProp("TasksMax", uint64(r.PidsLimit))) } + err = addCpuset(conn, &properties, r.CpusetCpus, r.CpusetMems) + if err != nil { + return nil, err + } + // ignore r.KernelMemory // convert Resources.Unified map to systemd properties diff --git a/tests/integration/update.bats b/tests/integration/update.bats index 908ce0ccbf0..c03e7c6def0 100644 --- a/tests/integration/update.bats +++ b/tests/integration/update.bats @@ -392,7 +392,63 @@ EOF check_systemd_value "TasksMax" 10 } +@test "update cpuset parameters via resources.CPU" { + [[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup + requires smp + + local AllowedCPUs='AllowedCPUs' AllowedMemoryNodes='AllowedMemoryNodes' + # these properties require systemd >= v244 + if [ "$(systemd_version)" -lt 244 ]; then + # a hack to skip checks, see check_systemd_value() + AllowedCPUs='unsupported' + AllowedMemoryNodes='unsupported' + fi + + update_config ' .linux.resources.CPU |= { + "Cpus": "0", + "Mems": "0" + }' "${BUSYBOX_BUNDLE}" + runc run -d --console-socket "$CONSOLE_SOCKET" test_update + [ "$status" -eq 0 ] + + # check that initial values were properly set + check_systemd_value "$AllowedCPUs" 0 + check_systemd_value "$AllowedMemoryNodes" 0 + + runc update -r - test_update <= v244 [[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup requires cgroups_v2 smp From a35cad3b225f8d0ca22f26e8499bb7ad2517d049 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 13 Jan 2021 18:13:47 -0800 Subject: [PATCH 3/3] libct/cg/sd/v2: warn about old systemd 1. Add a check to unifiedResToSystemdProps that systemd is recent enough to support AllowedCPUs/AllowedMemoryNodes unit properties, and skip setting the property if it is not supported. Note that this is not an error as the setting is still applied to the underlying cgroupfs -- it's just systemd unit property that is being skipped. 2. In all the places we skip an unsupported property, warn about it. Signed-off-by: Kir Kolyshkin --- libcontainer/cgroups/systemd/common.go | 5 +++++ libcontainer/cgroups/systemd/v2.go | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libcontainer/cgroups/systemd/common.go b/libcontainer/cgroups/systemd/common.go index 61f2d69f09d..008b1c252f7 100644 --- a/libcontainer/cgroups/systemd/common.go +++ b/libcontainer/cgroups/systemd/common.go @@ -409,6 +409,9 @@ func addCpuQuota(conn *systemdDbus.Conn, properties *[]systemdDbus.Property, quo if sdVer >= 242 { *properties = append(*properties, newProp("CPUQuotaPeriodUSec", period)) + } else { + logrus.Warnf("systemd v%d is too old to support CPUQuotaPeriodSec "+ + " (setting will still be applied to cgroupfs)", sdVer) } } if quota != 0 || period != 0 { @@ -441,6 +444,8 @@ func addCpuset(conn *systemdDbus.Conn, props *[]systemdDbus.Property, cpus, mems // systemd only supports AllowedCPUs/AllowedMemoryNodes since v244 sdVer := systemdVersion(conn) if sdVer < 244 { + logrus.Warnf("systemd v%d is too old to support AllowedCPUs/AllowedMemoryNodes"+ + " (settings will still be applied to cgroupfs)", sdVer) return nil } diff --git a/libcontainer/cgroups/systemd/v2.go b/libcontainer/cgroups/systemd/v2.go index 782e4b5b4e3..c1b0d737d8e 100644 --- a/libcontainer/cgroups/systemd/v2.go +++ b/libcontainer/cgroups/systemd/v2.go @@ -102,8 +102,16 @@ func unifiedResToSystemdProps(conn *systemdDbus.Conn, res map[string]string) (pr "cpuset.cpus": "AllowedCPUs", "cpuset.mems": "AllowedMemoryNodes", } - props = append(props, - newProp(m[k], bits)) + // systemd only supports these properties since v244 + sdVer := systemdVersion(conn) + if sdVer >= 244 { + props = append(props, + newProp(m[k], bits)) + } else { + logrus.Warnf("systemd v%d is too old to support %s"+ + " (setting will still be applied to cgroupfs)", + sdVer, m[k]) + } case "memory.high", "memory.low", "memory.min", "memory.max", "memory.swap.max": num := uint64(math.MaxUint64)