Skip to content

Commit

Permalink
libct/cg/v1: solution to the failure of setting rt_period_us and rt_r…
Browse files Browse the repository at this point in the history
…untime_us

As reported in issue opencontainers#4094, sometimes setting rt_period_us and rt_runtime_us
at the same time will fail. The reason is that in cgroupv1, these two values
cannot be set atomically.

Sometimes setting rt_period_us and rt_runtime_us at the same time will fail.
The reason is that in cgroupv1, these two values cannot be set atomically.

When we set a new rt_period_us (called period_new), the kernel will determine
whether the current configuration of new_limit1 = old_quota/new_period exceeds
the limit. If it exceeds the limit, an error will be reported. Maybe it is reasonable
to set rt_runtime_us first, new_limit2 = new_quota/old_period.
for example:
The original state of cgv1 is rt_period_us: 10000 rt_runtime_us: 5000
The user wants to change it to rt_period_us: 1000 rt_runtime_us:300.
The new rt_runtime_us should be set first.

In the opposite case, if rt_runtime_us is set first, new_limit2 may still exceed the
limit, but new_limit1 will be valid.
for example:
The original state of cgv1 is rt_period_us: 1000 rt_runtime_us: 500
The user wants to change it to rt_period_us: 10000 rt_runtime_us:3000.
The new rt_period_us should be set first.

Therefore, new_limit1 and new_limit2 should be calculated in advance, and the smaller
corresponding setting order should be selected to set rt_period_us and rt_runtime_us.

Signed-off-by: ls-ggg <335814617@qq.com>
  • Loading branch information
ls-ggg committed Apr 1, 2024
1 parent 8e69225 commit 7e8af61
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 6 deletions.
56 changes: 50 additions & 6 deletions libcontainer/cgroups/fs/cpu.go
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"strconv"
"strings"

"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
Expand Down Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions tests/integration/update.bats
Expand Up @@ -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]" {
Expand Down

0 comments on commit 7e8af61

Please sign in to comment.