diff --git a/libcontainer/cgroups/fs/cpu.go b/libcontainer/cgroups/fs/cpu.go index 727f7f9184f..5a60f43ada6 100644 --- a/libcontainer/cgroups/fs/cpu.go +++ b/libcontainer/cgroups/fs/cpu.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" @@ -35,17 +36,60 @@ func (s *CpuGroup) Apply(path string, r *configs.Resources, pid int) error { } func (s *CpuGroup) SetRtSched(path string, r *configs.Resources) error { - if r.CpuRtPeriod != 0 { - if err := cgroups.WriteFile(path, "cpu.rt_period_us", strconv.FormatUint(r.CpuRtPeriod, 10)); err != nil { + const ( + filePeriod = "cpu.rt_period_us" + fileRuntime = "cpu.rt_runtime_us" + ) + + if r.CpuRtPeriod == 0 && r.CpuRtRuntime == 0 { + return nil + } + + if r.CpuRtPeriod != 0 && r.CpuRtRuntime == 0 { + return cgroups.WriteFile(path, filePeriod, strconv.FormatUint(r.CpuRtPeriod, 10)) + } else if r.CpuRtRuntime != 0 && r.CpuRtPeriod == 0 { + return cgroups.WriteFile(path, fileRuntime, strconv.FormatInt(r.CpuRtRuntime, 10)) + } + + // When neither CpuRtPeriod nor CpuRtRuntime is equal to 0, let's set them in the correct order + + sOldPeriod, err := cgroups.ReadFile(path, filePeriod) + if err != nil { + return err + } + oldPeriod, err := strconv.ParseInt(strings.TrimSpace(sOldPeriod), 10, 64) + if err != nil { + return err + } + + sOldRuntime, err := cgroups.ReadFile(path, fileRuntime) + if err != nil { + return err + } + oldRuntime, err := strconv.ParseInt(strings.TrimSpace(sOldRuntime), 10, 64) + if err != nil { + return err + } + + /* + set the cpu.rt_period_us and cpu.rt_runtime_us in the optimal order and + try to keep the ratio of the intermediate state within the limit. + */ + + // First set cpu.rt_period_us because of (oldRuntime / r.CpuRtPeriod) < (r.CpuRtRuntime / oldPeriod) + if uint64(oldRuntime*oldPeriod) < (uint64(r.CpuRtRuntime) * r.CpuRtPeriod) { + if err := cgroups.WriteFile(path, filePeriod, strconv.FormatUint(r.CpuRtPeriod, 10)); err != nil { return err } - } - if r.CpuRtRuntime != 0 { - if err := cgroups.WriteFile(path, "cpu.rt_runtime_us", strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil { + if err := cgroups.WriteFile(path, fileRuntime, strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil { return err } } - return nil + // First set cpu.rt_runtime_us because of (r.CpuRtRuntime / oldPeriod) < (oldRuntime / r.CpuRtPeriod) + if err := cgroups.WriteFile(path, fileRuntime, strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil { + return err + } + return cgroups.WriteFile(path, filePeriod, strconv.FormatUint(r.CpuRtPeriod, 10)) } func (s *CpuGroup) Set(path string, r *configs.Resources) error { diff --git a/tests/integration/update.bats b/tests/integration/update.bats index 5a3dc7f0563..985d5946f82 100644 --- a/tests/integration/update.bats +++ b/tests/integration/update.bats @@ -758,6 +758,16 @@ EOF check_cgroup_value "cpu.rt_period_us" 900001 check_cgroup_value "cpu.rt_runtime_us" 600001 + + runc update test_update_rt --cpu-rt-period 10000 --cpu-rt-runtime 3000 + [ "$status" -eq 0 ] + check_cgroup_value "cpu.rt_period_us" 10000 + check_cgroup_value "cpu.rt_runtime_us" 3000 + + runc update test_update_rt --cpu-rt-period 100000 --cpu-rt-runtime 20000 + [ "$status" -eq 0 ] + check_cgroup_value "cpu.rt_period_us" 100000 + check_cgroup_value "cpu.rt_runtime_us" 20000 } @test "update devices [minimal transition rules]" {