diff --git a/contrib/completions/bash/runc b/contrib/completions/bash/runc index 0b8bda9b6..23ff64e88 100644 --- a/contrib/completions/bash/runc +++ b/contrib/completions/bash/runc @@ -743,6 +743,7 @@ _runc_update() { --pids-limit --l3-cache-schema --mem-bw-schema + --cpu-idle " case "$prev" in diff --git a/libcontainer/cgroups/fs/cpu.go b/libcontainer/cgroups/fs/cpu.go index 6c79f899b..26a9d7c0f 100644 --- a/libcontainer/cgroups/fs/cpu.go +++ b/libcontainer/cgroups/fs/cpu.go @@ -94,6 +94,14 @@ func (s *CpuGroup) Set(path string, r *configs.Resources) error { } } } + + if r.CPUIdle != nil { + idle := strconv.FormatInt(*r.CPUIdle, 10) + if err := cgroups.WriteFile(path, "cpu.idle", idle); err != nil { + return err + } + } + return s.SetRtSched(path, r) } diff --git a/libcontainer/cgroups/fs2/cpu.go b/libcontainer/cgroups/fs2/cpu.go index bbbae4d58..3f8deb0ad 100644 --- a/libcontainer/cgroups/fs2/cpu.go +++ b/libcontainer/cgroups/fs2/cpu.go @@ -11,7 +11,7 @@ import ( ) func isCpuSet(r *configs.Resources) bool { - return r.CpuWeight != 0 || r.CpuQuota != 0 || r.CpuPeriod != 0 + return r.CpuWeight != 0 || r.CpuQuota != 0 || r.CpuPeriod != 0 || r.CPUIdle != nil } func setCpu(dirPath string, r *configs.Resources) error { @@ -19,6 +19,12 @@ func setCpu(dirPath string, r *configs.Resources) error { return nil } + if r.CPUIdle != nil { + if err := cgroups.WriteFile(dirPath, "cpu.idle", strconv.FormatInt(*r.CPUIdle, 10)); err != nil { + return err + } + } + // NOTE: .CpuShares is not used here. Conversion is the caller's responsibility. if r.CpuWeight != 0 { if err := cgroups.WriteFile(dirPath, "cpu.weight", strconv.FormatUint(r.CpuWeight, 10)); err != nil { diff --git a/libcontainer/configs/cgroup_linux.go b/libcontainer/configs/cgroup_linux.go index b5a1ebb91..1664304be 100644 --- a/libcontainer/configs/cgroup_linux.go +++ b/libcontainer/configs/cgroup_linux.go @@ -84,6 +84,9 @@ type Resources struct { // MEM to use CpusetMems string `json:"cpuset_mems"` + // cgroup SCHED_IDLE + CPUIdle *int64 `json:"cpu_idle,omitempty"` + // Process limit; set <= `0' to disable limit. PidsLimit int64 `json:"pids_limit"` diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 0d53b2027..5d57cdd02 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -748,6 +748,7 @@ func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*devices.Device) (*confi } c.Resources.CpusetCpus = r.CPU.Cpus c.Resources.CpusetMems = r.CPU.Mems + c.Resources.CPUIdle = r.CPU.Idle } if r.Pids != nil { c.Resources.PidsLimit = r.Pids.Limit diff --git a/tests/integration/cgroups.bats b/tests/integration/cgroups.bats index ffa3fb0be..14f26d2d5 100644 --- a/tests/integration/cgroups.bats +++ b/tests/integration/cgroups.bats @@ -187,6 +187,18 @@ function setup() { [[ "$weights" == *"$major:$minor 444"* ]] } +@test "runc run (cpu.idle)" { + requires cgroups_cpu_idle + [ $EUID -ne 0 ] && requires rootless_cgroup + + set_cgroups_path + update_config '.linux.resources.cpu.idle = 1' + + runc run -d --console-socket "$CONSOLE_SOCKET" test_cgroups_unified + [ "$status" -eq 0 ] + check_cgroup_value "cpu.idle" "1" +} + @test "runc run (cgroup v2 resources.unified only)" { requires root cgroups_v2 diff --git a/tests/integration/helpers.bash b/tests/integration/helpers.bash index f07996450..9f3890a8c 100644 --- a/tests/integration/helpers.bash +++ b/tests/integration/helpers.bash @@ -413,6 +413,15 @@ function requires() { skip_me=1 fi ;; + cgroups_cpu_idle) + local p + init_cgroup_paths + [ -v CGROUP_V1 ] && p="$CGROUP_CPU_BASE_PATH" + [ -v CGROUP_V2 ] && p="$CGROUP_BASE_PATH" + if [ -z "$(find "$p" -name cpu.idle -print -quit)" ]; then + skip_me=1 + fi + ;; cgroupns) if [ ! -e "/proc/self/ns/cgroup" ]; then skip_me=1 diff --git a/tests/integration/update.bats b/tests/integration/update.bats index 1407118c4..09949a1ea 100644 --- a/tests/integration/update.bats +++ b/tests/integration/update.bats @@ -425,6 +425,41 @@ EOF check_cpu_quota 3000 10000 "300ms" } +@test "update cgroup cpu.idle" { + requires cgroups_cpu_idle + [ $EUID -ne 0 ] && requires rootless_cgroup + + runc run -d --console-socket "$CONSOLE_SOCKET" test_update + [ "$status" -eq 0 ] + + check_cgroup_value "cpu.idle" "0" + + local val + for val in 1 0 1; do + runc update -r - test_update <