Skip to content

Commit 9b32d3e

Browse files
committed
cgroups: fix incorrect cgroups cpuset via systemd
Signed-off-by: Yuta Maeda <[email protected]>
1 parent 7c29729 commit 9b32d3e

File tree

2 files changed

+89
-6
lines changed

2 files changed

+89
-6
lines changed

pkg/cgroups/cgroups_linux_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
package cgroups
44

55
import (
6+
"bytes"
67
"fmt"
8+
"math/big"
79
"os"
810
"os/exec"
911
"strconv"
@@ -87,7 +89,7 @@ func TestResources(t *testing.T) {
8789
}
8890

8991
// test CPU Quota adjustment.
90-
u, _, _, _, _ := resourcesToProps(&resources, true)
92+
u, _, b, _, _, _ := resourcesToProps(&resources, true)
9193

9294
val, ok := u["CPUQuotaPerSecUSec"]
9395
if !ok {
@@ -97,6 +99,25 @@ func TestResources(t *testing.T) {
9799
t.Fatal("CPU Quota incorrect value expected 1000000 got " + strconv.FormatUint(val, 10))
98100
}
99101

102+
bits := new(big.Int)
103+
cpuset_val := bits.SetBit(bits, 0, 1).Bytes()
104+
105+
cpus, ok := b["AllowedCPUs"]
106+
if !ok {
107+
t.Fatal("Cpuset Cpus not parsed.")
108+
}
109+
if bytes.Compare(cpus, cpuset_val) != 0 {
110+
t.Fatal("Cpuset Cpus incorrect value expected " + string(cpuset_val) + " got " + string(cpus))
111+
}
112+
113+
mems, ok := b["AllowedMemoryNodes"]
114+
if !ok {
115+
t.Fatal("Cpuset Mems not parsed.")
116+
}
117+
if bytes.Compare(mems, cpuset_val) != 0 {
118+
t.Fatal("Cpuset Mems incorrect value expected " + string(cpuset_val) + " got " + string(mems))
119+
}
120+
100121
err = os.Mkdir("/dev/foodevdir", os.ModePerm)
101122
if err != nil {
102123
t.Fatal(err)

pkg/cgroups/systemd_linux.go

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ package cgroups
44

55
import (
66
"context"
7+
"errors"
78
"fmt"
9+
"math/big"
810
"path/filepath"
11+
"strconv"
912
"strings"
1013

1114
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
@@ -53,7 +56,10 @@ func systemdCreate(resources *configs.Resources, path string, c *systemdDbus.Con
5356
properties = append(properties, p)
5457
}
5558

56-
uMap, sMap, bMap, iMap, structMap := resourcesToProps(resources, v2)
59+
uMap, sMap, bMap, iMap, structMap, res_err := resourcesToProps(resources, v2)
60+
if res_err != nil {
61+
return res_err
62+
}
5763
for k, v := range uMap {
5864
p := systemdDbus.Property{
5965
Name: k,
@@ -142,7 +148,7 @@ func systemdDestroyConn(path string, c *systemdDbus.Conn) error {
142148
return nil
143149
}
144150

145-
func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev) {
151+
func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev, error) {
146152
bMap := make(map[string][]byte)
147153
// this array is not used but will be once more resource limits are added
148154
sMap := make(map[string]string)
@@ -179,11 +185,19 @@ func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[s
179185

180186
// CPUSet
181187
if res.CpusetCpus != "" {
182-
bits := []byte(res.CpusetCpus)
188+
bits, err := rangeToBits(res.CpusetCpus)
189+
if err != nil {
190+
return nil, nil, nil, nil, nil, fmt.Errorf("resources.CpusetCpus=%q conversion error: %w",
191+
res.CpusetCpus, err)
192+
}
183193
bMap["AllowedCPUs"] = bits
184194
}
185195
if res.CpusetMems != "" {
186-
bits := []byte(res.CpusetMems)
196+
bits, err := rangeToBits(res.CpusetMems)
197+
if err != nil {
198+
return nil, nil, nil, nil, nil, fmt.Errorf("resources.CpusetMems=%q conversion error: %w",
199+
res.CpusetMems, err)
200+
}
187201
bMap["AllowedMemoryNodes"] = bits
188202
}
189203

@@ -258,5 +272,53 @@ func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[s
258272
}
259273
}
260274

261-
return uMap, sMap, bMap, iMap, structMap
275+
return uMap, sMap, bMap, iMap, structMap, nil
276+
}
277+
278+
func rangeToBits(str string) ([]byte, error) {
279+
bits := new(big.Int)
280+
281+
for _, r := range strings.Split(str, ",") {
282+
// allow extra spaces around
283+
r = strings.TrimSpace(r)
284+
// allow empty elements (extra commas)
285+
if r == "" {
286+
continue
287+
}
288+
startr, endr, ok := strings.Cut(r, "-")
289+
if ok {
290+
start, err := strconv.ParseUint(startr, 10, 32)
291+
if err != nil {
292+
return nil, err
293+
}
294+
end, err := strconv.ParseUint(endr, 10, 32)
295+
if err != nil {
296+
return nil, err
297+
}
298+
if start > end {
299+
return nil, errors.New("invalid range: " + r)
300+
}
301+
for i := start; i <= end; i++ {
302+
bits.SetBit(bits, int(i), 1)
303+
}
304+
} else {
305+
val, err := strconv.ParseUint(startr, 10, 32)
306+
if err != nil {
307+
return nil, err
308+
}
309+
bits.SetBit(bits, int(val), 1)
310+
}
311+
}
312+
313+
ret := bits.Bytes()
314+
if len(ret) == 0 {
315+
// do not allow empty values
316+
return nil, errors.New("empty value")
317+
}
318+
319+
// fit cpuset parsing order in systemd
320+
for l, r := 0, len(ret)-1; l < r; l, r = l+1, r-1 {
321+
ret[l], ret[r] = ret[r], ret[l]
322+
}
323+
return ret, nil
262324
}

0 commit comments

Comments
 (0)