From 51a94f6a8dd38af197af4c00ae40995445bce2bd Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 25 Mar 2021 15:54:02 +0100 Subject: [PATCH] helpers: fix reading cpu stats on cgroup v2 read quota and limit from cpu.max, and convert cpu.weight to the range expected with cgroup v1. Signed-off-by: Giuseppe Scrivano (cherry picked from commit 56ced551d72ebb564477307b80e422be4e837795) Signed-off-by: Kir Kolyshkin --- container/common/helpers.go | 79 +++++++++++++++++++++++++++----- container/common/helpers_test.go | 46 +++++++++++++++++++ 2 files changed, 114 insertions(+), 11 deletions(-) diff --git a/container/common/helpers.go b/container/common/helpers.go index b043aaefb5..5c686e12fd 100644 --- a/container/common/helpers.go +++ b/container/common/helpers.go @@ -103,17 +103,43 @@ func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoF cpuRoot, ok := cgroupPaths["cpu"] if ok { if utils.FileExists(cpuRoot) { - spec.HasCpu = true - spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares") - spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us") - quota := readString(cpuRoot, "cpu.cfs_quota_us") - - if quota != "" && quota != "-1" { - val, err := strconv.ParseUint(quota, 10, 64) - if err != nil { - klog.Errorf("GetSpec: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err) - } else { - spec.Cpu.Quota = val + if cgroups.IsCgroup2UnifiedMode() { + spec.HasCpu = true + + weight := readUInt64(cpuRoot, "cpu.weight") + if weight > 0 { + limit, err := convertCPUWeightToCPULimit(weight) + if err != nil { + klog.Errorf("GetSpec: Failed to read CPULimit from %q: %s", path.Join(cpuRoot, "cpu.weight"), err) + } else { + spec.Cpu.Limit = limit + } + } + max := readString(cpuRoot, "cpu.max") + if max != "" { + splits := strings.SplitN(max, " ", 2) + if len(splits) != 2 { + klog.Errorf("GetSpec: Failed to parse CPUmax from %q", path.Join(cpuRoot, "cpu.max")) + } else { + if splits[0] != "max" { + spec.Cpu.Quota = parseUint64String(splits[0]) + } + spec.Cpu.Period = parseUint64String(splits[1]) + } + } + } else { + spec.HasCpu = true + spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares") + spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us") + quota := readString(cpuRoot, "cpu.cfs_quota_us") + + if quota != "" && quota != "-1" { + val, err := strconv.ParseUint(quota, 10, 64) + if err != nil { + klog.Errorf("GetSpec: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err) + } else { + spec.Cpu.Quota = val + } } } } @@ -201,6 +227,37 @@ func readString(dirpath string, file string) string { return strings.TrimSpace(string(out)) } +// Convert from [1-10000] to [2-262144] +func convertCPUWeightToCPULimit(weight uint64) (uint64, error) { + const ( + // minWeight is the lowest value possible for cpu.weight + minWeight = 1 + // maxWeight is the highest value possible for cpu.weight + maxWeight = 10000 + ) + if weight < minWeight || weight > maxWeight { + return 0, fmt.Errorf("convertCPUWeightToCPULimit: invalid cpu weight: %v", weight) + } + return 2 + ((weight-1)*262142)/9999, nil +} + +func parseUint64String(strValue string) uint64 { + if strValue == "max" { + return math.MaxUint64 + } + if strValue == "" { + return 0 + } + + val, err := strconv.ParseUint(strValue, 10, 64) + if err != nil { + klog.Errorf("parseUint64String: Failed to parse int %q: %s", strValue, err) + return 0 + } + + return val +} + func readUInt64(dirpath string, file string) uint64 { out := readString(dirpath, file) if out == "max" { diff --git a/container/common/helpers_test.go b/container/common/helpers_test.go index 071349a6e1..0f416a0932 100644 --- a/container/common/helpers_test.go +++ b/container/common/helpers_test.go @@ -26,3 +26,49 @@ func BenchmarkListDirectories(b *testing.B) { } } } + +func TestConvertCpuWeightToCpuLimit(t *testing.T) { + limit, err := convertCPUWeightToCPULimit(1) + if err != nil { + t.Fatalf("Error in convertCPUWeightToCPULimit: %s", err) + } + if limit != 2 { + t.Fatalf("convertCPUWeightToCPULimit(1) != 2") + } + limit, err = convertCPUWeightToCPULimit(10000) + if err != nil { + t.Fatalf("Error in convertCPUWeightToCPULimit: %s", err) + } + if limit != 262144 { + t.Fatalf("convertCPUWeightToCPULimit(10000) != 262144") + } + _, err = convertCPUWeightToCPULimit(0) + if err == nil { + t.Fatalf("convertCPUWeightToCPULimit(0) must raise an error") + } + _, err = convertCPUWeightToCPULimit(10001) + if err == nil { + t.Fatalf("convertCPUWeightToCPULimit(10001) must raise an error") + } +} + +func TestParseUint64String(t *testing.T) { + if parseUint64String("1000") != 1000 { + t.Fatalf("parseUint64String(\"1000\") != 1000") + } + if parseUint64String("-1") != 0 { + t.Fatalf("parseUint64String(\"-1\") != 0") + } + if parseUint64String("0") != 0 { + t.Fatalf("parseUint64String(\"0\") != 0") + } + if parseUint64String("not-a-number") != 0 { + t.Fatalf("parseUint64String(\"not-a-number\") != 0") + } + if parseUint64String(" 1000 ") != 0 { + t.Fatalf("parseUint64String(\" 1000 \") != 0") + } + if parseUint64String("18446744073709551615") != 18446744073709551615 { + t.Fatalf("parseUint64String(\"18446744073709551615\") != 18446744073709551615") + } +}