From 64e9a97981b95631be1e41705c2aea9a545a7213 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 10 Mar 2020 00:54:12 +0900 Subject: [PATCH 1/2] cgroup2: fix conversion * TestConvertCPUSharesToCgroupV2Value(0) was returning 70369281052672, while the correct value is 0 * ConvertBlkIOToCgroupV2Value(0) was returning 32, while the correct value is 0 * ConvertBlkIOToCgroupV2Value(1000) was returning 4, while the correct value is 10000 Fix #2244 Follow-up to #2212 #2213 Signed-off-by: Akihiro Suda --- libcontainer/cgroups/utils.go | 8 +++++++- libcontainer/cgroups/utils_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/libcontainer/cgroups/utils.go b/libcontainer/cgroups/utils.go index 861716e82..704941cdf 100644 --- a/libcontainer/cgroups/utils.go +++ b/libcontainer/cgroups/utils.go @@ -592,7 +592,10 @@ func isEINVAL(err error) bool { // the formula for BlkIOWeight is y = (1 + (x - 10) * 9999 / 990) // convert linearly from [10-1000] to [1-10000] func ConvertBlkIOToCgroupV2Value(blkIoWeight uint16) uint64 { - return uint64(1 + (blkIoWeight-10)*9999/990) + if blkIoWeight == 0 { + return 0 + } + return uint64(1 + (uint64(blkIoWeight)-10)*9999/990) } // Since the OCI spec is designed for cgroup v1, in some cases @@ -601,5 +604,8 @@ func ConvertBlkIOToCgroupV2Value(blkIoWeight uint16) uint64 { // convert from [2-262144] to [1-10000] // 262144 comes from Linux kernel definition "#define MAX_SHARES (1UL << 18)" func ConvertCPUSharesToCgroupV2Value(cpuShares uint64) uint64 { + if cpuShares == 0 { + return 0 + } return (1 + ((cpuShares-2)*9999)/262142) } diff --git a/libcontainer/cgroups/utils_test.go b/libcontainer/cgroups/utils_test.go index 3214b9de0..3dfa6a0d4 100644 --- a/libcontainer/cgroups/utils_test.go +++ b/libcontainer/cgroups/utils_test.go @@ -457,3 +457,31 @@ func TestGetHugePageSizeImpl(t *testing.T) { } } } + +func TestConvertBlkIOToCgroupV2Value(t *testing.T) { + cases := map[uint16]uint64{ + 0: 0, + 10: 1, + 1000: 10000, + } + for i, expected := range cases { + got := ConvertBlkIOToCgroupV2Value(i) + if got != expected { + t.Errorf("expected ConvertBlkIOToCgroupV2Value(%d) to be %d, got %d", i, expected, got) + } + } +} + +func TestConvertCPUSharesToCgroupV2Value(t *testing.T) { + cases := map[uint64]uint64{ + 0: 0, + 2: 1, + 262144: 10000, + } + for i, expected := range cases { + got := ConvertCPUSharesToCgroupV2Value(i) + if got != expected { + t.Errorf("expected ConvertCPUSharesToCgroupV2Value(%d) to be %d, got %d", i, expected, got) + } + } +} From aa269315a4b0c29668e7d661754abb3ea941c512 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 10 Mar 2020 02:20:17 +0900 Subject: [PATCH 2/2] cgroup2: add CpuMax conversion Fix #2243 Signed-off-by: Akihiro Suda --- libcontainer/cgroups/fs2/cpu.go | 2 ++ libcontainer/cgroups/utils.go | 15 ++++++++++ libcontainer/cgroups/utils_test.go | 45 +++++++++++++++++++++++++++++ libcontainer/specconv/spec_linux.go | 3 ++ update.go | 2 ++ 5 files changed, 67 insertions(+) diff --git a/libcontainer/cgroups/fs2/cpu.go b/libcontainer/cgroups/fs2/cpu.go index f0f5df09a..8ec855714 100644 --- a/libcontainer/cgroups/fs2/cpu.go +++ b/libcontainer/cgroups/fs2/cpu.go @@ -14,12 +14,14 @@ import ( ) func setCpu(dirPath string, cgroup *configs.Cgroup) error { + // NOTE: .CpuShares is not used here. Conversion is the caller's responsibility. if cgroup.Resources.CpuWeight != 0 { if err := fscommon.WriteFile(dirPath, "cpu.weight", strconv.FormatUint(cgroup.Resources.CpuWeight, 10)); err != nil { return err } } + // NOTE: .CpuQuota and .CpuPeriod are not used here. Conversion is the caller's responsibility. if cgroup.Resources.CpuMax != "" { if err := fscommon.WriteFile(dirPath, "cpu.max", cgroup.Resources.CpuMax); err != nil { return err diff --git a/libcontainer/cgroups/utils.go b/libcontainer/cgroups/utils.go index 704941cdf..1bedda046 100644 --- a/libcontainer/cgroups/utils.go +++ b/libcontainer/cgroups/utils.go @@ -609,3 +609,18 @@ func ConvertCPUSharesToCgroupV2Value(cpuShares uint64) uint64 { } return (1 + ((cpuShares-2)*9999)/262142) } + +// ConvertCPUQuotaCPUPeriodToCgroupV2Value generates cpu.max string. +func ConvertCPUQuotaCPUPeriodToCgroupV2Value(quota int64, period uint64) string { + if quota <= 0 && period == 0 { + return "" + } + if period == 0 { + // This default value is documented in https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html + period = 100000 + } + if quota <= 0 { + return fmt.Sprintf("max %d", period) + } + return fmt.Sprintf("%d %d", quota, period) +} diff --git a/libcontainer/cgroups/utils_test.go b/libcontainer/cgroups/utils_test.go index 3dfa6a0d4..14a76bf72 100644 --- a/libcontainer/cgroups/utils_test.go +++ b/libcontainer/cgroups/utils_test.go @@ -485,3 +485,48 @@ func TestConvertCPUSharesToCgroupV2Value(t *testing.T) { } } } + +func TestConvertCPUQuotaCPUPeriodToCgroupV2Value(t *testing.T) { + cases := []struct { + quota int64 + period uint64 + expected string + }{ + { + quota: 0, + period: 0, + expected: "", + }, + { + quota: -1, + period: 0, + expected: "", + }, + { + quota: 1000, + period: 5000, + expected: "1000 5000", + }, + { + quota: 0, + period: 5000, + expected: "max 5000", + }, + { + quota: -1, + period: 5000, + expected: "max 5000", + }, + { + quota: 1000, + period: 0, + expected: "1000 100000", + }, + } + for _, c := range cases { + got := ConvertCPUQuotaCPUPeriodToCgroupV2Value(c.quota, c.period) + if got != c.expected { + t.Errorf("expected ConvertCPUQuotaCPUPeriodToCgroupV2Value(%d, %d) to be %s, got %s", c.quota, c.period, c.expected, got) + } + } +} diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 90731f50b..88463e47f 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -492,6 +492,9 @@ func CreateCgroupConfig(opts *CreateOpts) (*configs.Cgroup, error) { if r.CPU.Period != nil { c.Resources.CpuPeriod = *r.CPU.Period } + //CpuMax is used for cgroupv2 and should be converted + c.Resources.CpuMax = cgroups.ConvertCPUQuotaCPUPeriodToCgroupV2Value(c.Resources.CpuQuota, c.Resources.CpuPeriod) + if r.CPU.RealtimeRuntime != nil { c.Resources.CpuRtRuntime = *r.CPU.RealtimeRuntime } diff --git a/update.go b/update.go index 84306b291..42f3ecf03 100644 --- a/update.go +++ b/update.go @@ -257,6 +257,8 @@ other options are ignored. config.Cgroups.Resources.CpuShares = *r.CPU.Shares //CpuWeight is used for cgroupv2 and should be converted config.Cgroups.Resources.CpuWeight = cgroups.ConvertCPUSharesToCgroupV2Value(*r.CPU.Shares) + //CpuMax is used for cgroupv2 and should be converted + config.Cgroups.Resources.CpuMax = cgroups.ConvertCPUQuotaCPUPeriodToCgroupV2Value(*r.CPU.Quota, *r.CPU.Period) config.Cgroups.Resources.CpuRtPeriod = *r.CPU.RealtimePeriod config.Cgroups.Resources.CpuRtRuntime = *r.CPU.RealtimeRuntime config.Cgroups.Resources.CpusetCpus = r.CPU.Cpus