Skip to content

Commit 56ced55

Browse files
committed
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 <[email protected]>
1 parent 42c236e commit 56ced55

File tree

2 files changed

+114
-11
lines changed

2 files changed

+114
-11
lines changed

container/common/helpers.go

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,43 @@ func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoF
103103
cpuRoot, ok := cgroupPaths["cpu"]
104104
if ok {
105105
if utils.FileExists(cpuRoot) {
106-
spec.HasCpu = true
107-
spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares")
108-
spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us")
109-
quota := readString(cpuRoot, "cpu.cfs_quota_us")
110-
111-
if quota != "" && quota != "-1" {
112-
val, err := strconv.ParseUint(quota, 10, 64)
113-
if err != nil {
114-
klog.Errorf("GetSpec: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err)
115-
} else {
116-
spec.Cpu.Quota = val
106+
if cgroups.IsCgroup2UnifiedMode() {
107+
spec.HasCpu = true
108+
109+
weight := readUInt64(cpuRoot, "cpu.weight")
110+
if weight > 0 {
111+
limit, err := convertCPUWeightToCPULimit(weight)
112+
if err != nil {
113+
klog.Errorf("GetSpec: Failed to read CPULimit from %q: %s", path.Join(cpuRoot, "cpu.weight"), err)
114+
} else {
115+
spec.Cpu.Limit = limit
116+
}
117+
}
118+
max := readString(cpuRoot, "cpu.max")
119+
if max != "" {
120+
splits := strings.SplitN(max, " ", 2)
121+
if len(splits) != 2 {
122+
klog.Errorf("GetSpec: Failed to parse CPUmax from %q", path.Join(cpuRoot, "cpu.max"))
123+
} else {
124+
if splits[0] != "max" {
125+
spec.Cpu.Quota = parseUint64String(splits[0])
126+
}
127+
spec.Cpu.Period = parseUint64String(splits[1])
128+
}
129+
}
130+
} else {
131+
spec.HasCpu = true
132+
spec.Cpu.Limit = readUInt64(cpuRoot, "cpu.shares")
133+
spec.Cpu.Period = readUInt64(cpuRoot, "cpu.cfs_period_us")
134+
quota := readString(cpuRoot, "cpu.cfs_quota_us")
135+
136+
if quota != "" && quota != "-1" {
137+
val, err := strconv.ParseUint(quota, 10, 64)
138+
if err != nil {
139+
klog.Errorf("GetSpec: Failed to parse CPUQuota from %q: %s", path.Join(cpuRoot, "cpu.cfs_quota_us"), err)
140+
} else {
141+
spec.Cpu.Quota = val
142+
}
117143
}
118144
}
119145
}
@@ -201,6 +227,37 @@ func readString(dirpath string, file string) string {
201227
return strings.TrimSpace(string(out))
202228
}
203229

230+
// Convert from [1-10000] to [2-262144]
231+
func convertCPUWeightToCPULimit(weight uint64) (uint64, error) {
232+
const (
233+
// minWeight is the lowest value possible for cpu.weight
234+
minWeight = 1
235+
// maxWeight is the highest value possible for cpu.weight
236+
maxWeight = 10000
237+
)
238+
if weight < minWeight || weight > maxWeight {
239+
return 0, fmt.Errorf("convertCPUWeightToCPULimit: invalid cpu weight: %v", weight)
240+
}
241+
return 2 + ((weight-1)*262142)/9999, nil
242+
}
243+
244+
func parseUint64String(strValue string) uint64 {
245+
if strValue == "max" {
246+
return math.MaxUint64
247+
}
248+
if strValue == "" {
249+
return 0
250+
}
251+
252+
val, err := strconv.ParseUint(strValue, 10, 64)
253+
if err != nil {
254+
klog.Errorf("parseUint64String: Failed to parse int %q: %s", strValue, err)
255+
return 0
256+
}
257+
258+
return val
259+
}
260+
204261
func readUInt64(dirpath string, file string) uint64 {
205262
out := readString(dirpath, file)
206263
if out == "max" {

container/common/helpers_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,49 @@ func BenchmarkListDirectories(b *testing.B) {
2626
}
2727
}
2828
}
29+
30+
func TestConvertCpuWeightToCpuLimit(t *testing.T) {
31+
limit, err := convertCPUWeightToCPULimit(1)
32+
if err != nil {
33+
t.Fatalf("Error in convertCPUWeightToCPULimit: %s", err)
34+
}
35+
if limit != 2 {
36+
t.Fatalf("convertCPUWeightToCPULimit(1) != 2")
37+
}
38+
limit, err = convertCPUWeightToCPULimit(10000)
39+
if err != nil {
40+
t.Fatalf("Error in convertCPUWeightToCPULimit: %s", err)
41+
}
42+
if limit != 262144 {
43+
t.Fatalf("convertCPUWeightToCPULimit(10000) != 262144")
44+
}
45+
_, err = convertCPUWeightToCPULimit(0)
46+
if err == nil {
47+
t.Fatalf("convertCPUWeightToCPULimit(0) must raise an error")
48+
}
49+
_, err = convertCPUWeightToCPULimit(10001)
50+
if err == nil {
51+
t.Fatalf("convertCPUWeightToCPULimit(10001) must raise an error")
52+
}
53+
}
54+
55+
func TestParseUint64String(t *testing.T) {
56+
if parseUint64String("1000") != 1000 {
57+
t.Fatalf("parseUint64String(\"1000\") != 1000")
58+
}
59+
if parseUint64String("-1") != 0 {
60+
t.Fatalf("parseUint64String(\"-1\") != 0")
61+
}
62+
if parseUint64String("0") != 0 {
63+
t.Fatalf("parseUint64String(\"0\") != 0")
64+
}
65+
if parseUint64String("not-a-number") != 0 {
66+
t.Fatalf("parseUint64String(\"not-a-number\") != 0")
67+
}
68+
if parseUint64String(" 1000 ") != 0 {
69+
t.Fatalf("parseUint64String(\" 1000 \") != 0")
70+
}
71+
if parseUint64String("18446744073709551615") != 18446744073709551615 {
72+
t.Fatalf("parseUint64String(\"18446744073709551615\") != 18446744073709551615")
73+
}
74+
}

0 commit comments

Comments
 (0)