diff --git a/cgroup2/cpu.go b/cgroup2/cpu.go index dcb253db..74d50a1c 100644 --- a/cgroup2/cpu.go +++ b/cgroup2/cpu.go @@ -34,6 +34,8 @@ func NewCPUMax(quota *int64, period *uint64) CPUMax { type CPU struct { Weight *uint64 + Idle *uint64 + Burst *uint64 Max CPUMax Cpus string Mems string @@ -61,12 +63,24 @@ func (r *CPU) Values() (o []Value) { value: *r.Weight, }) } + if r.Idle != nil { + o = append(o, Value{ + filename: "cpu.idle", + value: *r.Idle, + }) + } if r.Max != "" { o = append(o, Value{ filename: "cpu.max", value: r.Max, }) } + if r.Burst != nil { + o = append(o, Value{ + filename: "cpu.max.burst", + value: *r.Burst, + }) + } if r.Cpus != "" { o = append(o, Value{ filename: "cpuset.cpus", diff --git a/cgroup2/cpuv2_test.go b/cgroup2/cpuv2_test.go index 6751d23d..c2358eb9 100644 --- a/cgroup2/cpuv2_test.go +++ b/cgroup2/cpuv2_test.go @@ -35,11 +35,14 @@ func TestCgroupv2CpuStats(t *testing.T) { quota int64 = 10000 period uint64 = 8000 weight uint64 = 100 + // The burst in the range [0, $quota]. + burst uint64 = 1000 ) c, err := NewManager(defaultCgroup2Path, groupPath, &Resources{ CPU: &CPU{ Weight: &weight, + Burst: &burst, Max: NewCPUMax("a, &period), Cpus: "0", Mems: "0", @@ -51,11 +54,34 @@ func TestCgroupv2CpuStats(t *testing.T) { }) checkFileContent(t, c.path, "cpu.weight", strconv.FormatUint(weight, 10)) + checkFileContent(t, c.path, "cpu.max.burst", strconv.FormatUint(burst, 10)) checkFileContent(t, c.path, "cpu.max", "10000 8000") checkFileContent(t, c.path, "cpuset.cpus", "0") checkFileContent(t, c.path, "cpuset.mems", "0") } +func TestCgroupv2CpuIdle(t *testing.T) { + checkCgroupMode(t) + group := "/cpu-test-cg-idle" + groupPath := fmt.Sprintf("%s-%d", group, os.Getpid()) + var ( + idle uint64 = 1 + ) + res := Resources{ + CPU: &CPU{ + Idle: &idle, + }, + } + c, err := NewManager(defaultCgroup2Path, groupPath, &res) + require.NoError(t, err, "failed to init new cgroup manager") + t.Cleanup(func() { + os.Remove(c.path) + }) + + checkFileContent(t, c.path, "cpu.weight", "0") + checkFileContent(t, c.path, "cpu.idle", "1") +} + func TestSystemdCgroupCpuController(t *testing.T) { checkCgroupMode(t) group := fmt.Sprintf("testing-cpu-%d.scope", os.Getpid()) diff --git a/cgroup2/manager.go b/cgroup2/manager.go index b446939d..b27c0982 100644 --- a/cgroup2/manager.go +++ b/cgroup2/manager.go @@ -869,9 +869,20 @@ func NewSystemd(slice, group string, pid int, resources *Resources) (*Manager, e newSystemdProperty("MemoryMax", uint64(*resources.Memory.Max))) } - if resources.CPU != nil && resources.CPU.Weight != nil && *resources.CPU.Weight != 0 { - properties = append(properties, - newSystemdProperty("CPUWeight", *resources.CPU.Weight)) + if resources.CPU != nil { + // Systemd v252 added support for setting cgroup v2 cpu.idle in systemd/systemd#23299 + // The way it works is + // if CPUWeight == 0, cpu.idle is set to 1; + // if CPUWeight != 0, cpu.idle is set to 0. + // Do not add duplicate CPUWeight property + if resources.CPU.Idle != nil && resources.CPU.Weight != nil && *resources.CPU.Weight != 0 { + properties = append(properties, + newSystemdProperty("CPUWeight", uint64(0))) + } + if resources.CPU.Weight != nil && *resources.CPU.Weight != 0 { + properties = append(properties, + newSystemdProperty("CPUWeight", *resources.CPU.Weight)) + } } if resources.CPU != nil && resources.CPU.Max != "" {