From 950700e73c20c64deda10f944533363fda494d47 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 31 May 2016 09:01:04 +0100 Subject: [PATCH 1/2] Set the 'cpu.rt_runtime_us' and 'cpu.rt_runtime_us' values of the cpu cgroup before trying to move the process into the cgroup. This is required if runc itself is running in SCHED_RR mode, as it is not possible to add a process in SCHED_RR mode to a cgroup which hasn't been assigned any RT bandwidth. And RT bandwidth is not inherited, each new cgroup starts with 0 b/w. Signed-off-by: Ben Gray --- libcontainer/cgroups/fs/cpu.go | 51 +++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/libcontainer/cgroups/fs/cpu.go b/libcontainer/cgroups/fs/cpu.go index a4ef28a60..c941f95d6 100644 --- a/libcontainer/cgroups/fs/cpu.go +++ b/libcontainer/cgroups/fs/cpu.go @@ -22,10 +22,48 @@ func (s *CpuGroup) Name() string { func (s *CpuGroup) Apply(d *cgroupData) error { // We always want to join the cpu group, to allow fair cpu scheduling // on a container basis - _, err := d.join("cpu") + path, err := d.path("cpu") if err != nil && !cgroups.IsNotFound(err) { return err } + return s.ApplyDir(path, d.config, d.pid) +} + +func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error { + // This might happen if we have no cpu cgroup mounted. + // Just do nothing and don't fail. + if path == "" { + return nil + } + if err := os.MkdirAll(path, 0755); err != nil { + return err + } + // We should set the real-Time group scheduling settings before moving + // in the process because if the process is already in SCHED_RR mode + // and no RT bandwidth is set, adding it will fail. + if err := s.SetRtSched(path, cgroup); err != nil { + return err + } + // because we are not using d.join we need to place the pid into the procs file + // unlike the other subsystems + if err := writeFile(path, "cgroup.procs", strconv.Itoa(pid)); err != nil { + return err + } + + return nil +} + +func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error { + if cgroup.Resources.CpuRtPeriod != 0 { + if err := writeFile(path, "cpu.rt_period_us", strconv.FormatInt(cgroup.Resources.CpuRtPeriod, 10)); err != nil { + return err + } + } + if cgroup.Resources.CpuRtRuntime != 0 { + if err := writeFile(path, "cpu.rt_runtime_us", strconv.FormatInt(cgroup.Resources.CpuRtRuntime, 10)); err != nil { + return err + } + } return nil } @@ -45,15 +83,8 @@ func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error { return err } } - if cgroup.Resources.CpuRtPeriod != 0 { - if err := writeFile(path, "cpu.rt_period_us", strconv.FormatInt(cgroup.Resources.CpuRtPeriod, 10)); err != nil { - return err - } - } - if cgroup.Resources.CpuRtRuntime != 0 { - if err := writeFile(path, "cpu.rt_runtime_us", strconv.FormatInt(cgroup.Resources.CpuRtRuntime, 10)); err != nil { - return err - } + if err := s.SetRtSched(path, cgroup); err != nil { + return err } return nil From 14e55d1692fa46fb21169c6722ef1c3a0a07b791 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 4 Jul 2016 12:59:07 +0100 Subject: [PATCH 2/2] Add unit test for setting the CPU RT sched cgroups values at apply time Added a unit test to verify that 'cpu.rt_runtime_us' and 'cpu.rt_runtime_us' cgroup values are set when the cgroup is applied to a process. Signed-off-by: Ben Gray --- libcontainer/cgroups/fs/cpu_test.go | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/libcontainer/cgroups/fs/cpu_test.go b/libcontainer/cgroups/fs/cpu_test.go index 350264710..6369c91ad 100644 --- a/libcontainer/cgroups/fs/cpu_test.go +++ b/libcontainer/cgroups/fs/cpu_test.go @@ -161,3 +161,49 @@ func TestInvalidCpuStat(t *testing.T) { t.Fatal("Expected failed stat parsing.") } } + +func TestCpuSetRtSchedAtApply(t *testing.T) { + helper := NewCgroupTestUtil("cpu", t) + defer helper.cleanup() + + const ( + rtRuntimeBefore = 0 + rtRuntimeAfter = 5000 + rtPeriodBefore = 0 + rtPeriodAfter = 7000 + ) + + helper.writeFileContents(map[string]string{ + "cpu.rt_runtime_us": strconv.Itoa(rtRuntimeBefore), + "cpu.rt_period_us": strconv.Itoa(rtPeriodBefore), + }) + + helper.CgroupData.config.Resources.CpuRtRuntime = rtRuntimeAfter + helper.CgroupData.config.Resources.CpuRtPeriod = rtPeriodAfter + cpu := &CpuGroup{} + if err := cpu.ApplyDir(helper.CgroupPath, helper.CgroupData.config, 1234); err != nil { + t.Fatal(err) + } + + rtRuntime, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_runtime_us") + if err != nil { + t.Fatalf("Failed to parse cpu.rt_runtime_us - %s", err) + } + if rtRuntime != rtRuntimeAfter { + t.Fatal("Got the wrong value, set cpu.rt_runtime_us failed.") + } + rtPeriod, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_period_us") + if err != nil { + t.Fatalf("Failed to parse cpu.rt_period_us - %s", err) + } + if rtPeriod != rtPeriodAfter { + t.Fatal("Got the wrong value, set cpu.rt_period_us failed.") + } + pid, err := getCgroupParamUint(helper.CgroupPath, "cgroup.procs") + if err != nil { + t.Fatalf("Failed to parse cgroup.procs - %s", err) + } + if pid != 1234 { + t.Fatal("Got the wrong value, set cgroup.procs failed.") + } +}