From 12a63757dbde3b0be25b49bc9e7625059088d319 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Mon, 9 Feb 2015 13:46:30 +0800 Subject: [PATCH 1/5] rename Set to Apply The name `Set` would be used to do dymanic changes of resource configs in the future. For now, `Apply` also makes more sense. Signed-off-by: Qiang Huang --- cgroups/fs/apply_raw.go | 8 ++++---- cgroups/fs/blkio.go | 2 +- cgroups/fs/cpu.go | 2 +- cgroups/fs/cpuacct.go | 2 +- cgroups/fs/cpuset.go | 6 +++--- cgroups/fs/devices.go | 2 +- cgroups/fs/freezer.go | 2 +- cgroups/fs/memory.go | 2 +- cgroups/fs/perf_event.go | 2 +- cgroups/systemd/apply_systemd.go | 2 +- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cgroups/fs/apply_raw.go b/cgroups/fs/apply_raw.go index ba09bfb7..ba7dd768 100644 --- a/cgroups/fs/apply_raw.go +++ b/cgroups/fs/apply_raw.go @@ -31,7 +31,7 @@ type subsystem interface { // Removes the cgroup represented by 'data'. Remove(*data) error // Creates and joins the cgroup represented by data. - Set(*data) error + Apply(*data) error } type Manager struct { @@ -91,7 +91,7 @@ func (m *Manager) Apply(pid int) error { } }() for name, sys := range subsystems { - if err := sys.Set(d); err != nil { + if err := sys.Apply(d); err != nil { return err } // TODO: Apply should, ideally, be reentrant or be broken up into a separate @@ -129,7 +129,7 @@ func ApplyDevices(c *configs.Cgroup, pid int) error { devices := subsystems["devices"] - return devices.Set(d) + return devices.Apply(d) } func (m *Manager) GetStats() (*cgroups.Stats, error) { @@ -159,7 +159,7 @@ func (m *Manager) Freeze(state configs.FreezerState) error { m.Cgroups.Freezer = state freezer := subsystems["freezer"] - err = freezer.Set(d) + err = freezer.Apply(d) if err != nil { m.Cgroups.Freezer = prevState return err diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index b64e4684..87d91128 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -14,7 +14,7 @@ import ( type BlkioGroup struct { } -func (s *BlkioGroup) Set(d *data) error { +func (s *BlkioGroup) Apply(d *data) error { dir, err := d.join("blkio") if err != nil && !cgroups.IsNotFound(err) { return err diff --git a/cgroups/fs/cpu.go b/cgroups/fs/cpu.go index efac9ed1..385625c7 100644 --- a/cgroups/fs/cpu.go +++ b/cgroups/fs/cpu.go @@ -12,7 +12,7 @@ import ( type CpuGroup struct { } -func (s *CpuGroup) Set(d *data) error { +func (s *CpuGroup) Apply(d *data) error { // We always want to join the cpu group, to allow fair cpu scheduling // on a container basis dir, err := d.join("cpu") diff --git a/cgroups/fs/cpuacct.go b/cgroups/fs/cpuacct.go index 14b55ccd..58378125 100644 --- a/cgroups/fs/cpuacct.go +++ b/cgroups/fs/cpuacct.go @@ -21,7 +21,7 @@ var clockTicks = uint64(system.GetClockTicks()) type CpuacctGroup struct { } -func (s *CpuacctGroup) Set(d *data) error { +func (s *CpuacctGroup) Apply(d *data) error { // we just want to join this group even though we don't set anything if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) { return err diff --git a/cgroups/fs/cpuset.go b/cgroups/fs/cpuset.go index ff67a53e..d9b3e7b7 100644 --- a/cgroups/fs/cpuset.go +++ b/cgroups/fs/cpuset.go @@ -13,12 +13,12 @@ import ( type CpusetGroup struct { } -func (s *CpusetGroup) Set(d *data) error { +func (s *CpusetGroup) Apply(d *data) error { dir, err := d.path("cpuset") if err != nil { return err } - return s.SetDir(dir, d.c.CpusetCpus, d.c.CpusetMems, d.pid) + return s.ApplyDir(dir, d.c.CpusetCpus, d.c.CpusetMems, d.pid) } func (s *CpusetGroup) Remove(d *data) error { @@ -29,7 +29,7 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } -func (s *CpusetGroup) SetDir(dir, cpus string, mems string, pid int) error { +func (s *CpusetGroup) ApplyDir(dir, cpus string, mems string, pid int) error { if err := s.ensureParent(dir); err != nil { return err } diff --git a/cgroups/fs/devices.go b/cgroups/fs/devices.go index e904e10c..d1fef46f 100644 --- a/cgroups/fs/devices.go +++ b/cgroups/fs/devices.go @@ -5,7 +5,7 @@ import "github.com/docker/libcontainer/cgroups" type DevicesGroup struct { } -func (s *DevicesGroup) Set(d *data) error { +func (s *DevicesGroup) Apply(d *data) error { dir, err := d.join("devices") if err != nil { return err diff --git a/cgroups/fs/freezer.go b/cgroups/fs/freezer.go index b881d0d4..6e9aef18 100644 --- a/cgroups/fs/freezer.go +++ b/cgroups/fs/freezer.go @@ -11,7 +11,7 @@ import ( type FreezerGroup struct { } -func (s *FreezerGroup) Set(d *data) error { +func (s *FreezerGroup) Apply(d *data) error { switch d.c.Freezer { case configs.Frozen, configs.Thawed: dir, err := d.path("freezer") diff --git a/cgroups/fs/memory.go b/cgroups/fs/memory.go index 01713fd7..573c8ab7 100644 --- a/cgroups/fs/memory.go +++ b/cgroups/fs/memory.go @@ -13,7 +13,7 @@ import ( type MemoryGroup struct { } -func (s *MemoryGroup) Set(d *data) error { +func (s *MemoryGroup) Apply(d *data) error { dir, err := d.join("memory") // only return an error for memory if it was specified if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) { diff --git a/cgroups/fs/perf_event.go b/cgroups/fs/perf_event.go index 813274d8..305b4c05 100644 --- a/cgroups/fs/perf_event.go +++ b/cgroups/fs/perf_event.go @@ -7,7 +7,7 @@ import ( type PerfEventGroup struct { } -func (s *PerfEventGroup) Set(d *data) error { +func (s *PerfEventGroup) Apply(d *data) error { // we just want to join this group even though we don't set anything if _, err := d.join("perf_event"); err != nil && !cgroups.IsNotFound(err) { return err diff --git a/cgroups/systemd/apply_systemd.go b/cgroups/systemd/apply_systemd.go index fbaa13c7..5d45fd6f 100644 --- a/cgroups/systemd/apply_systemd.go +++ b/cgroups/systemd/apply_systemd.go @@ -398,5 +398,5 @@ func joinCpuset(c *configs.Cgroup, pid int) error { s := &fs.CpusetGroup{} - return s.SetDir(path, c.CpusetCpus, c.CpusetMems, pid) + return s.ApplyDir(path, c.CpusetCpus, c.CpusetMems, pid) } From cc524f1b729cb5d7592d0a0b07cb3ff1fe6eda98 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Wed, 25 Feb 2015 17:20:01 +0800 Subject: [PATCH 2/5] add Set interface Signed-off-by: Qiang Huang --- cgroups/cgroups.go | 3 +++ cgroups/fs/apply_raw.go | 16 ++++++++++++++++ cgroups/fs/blkio.go | 11 +++++++++++ cgroups/fs/cpu.go | 21 +++++++++++++++++++++ cgroups/fs/cpuacct.go | 5 +++++ cgroups/fs/cpuset.go | 17 +++++++++++++++++ cgroups/fs/devices.go | 21 ++++++++++++++++++++- cgroups/fs/freezer.go | 22 ++++++++++++++++++++++ cgroups/fs/memory.go | 27 +++++++++++++++++++++++++++ cgroups/fs/perf_event.go | 5 +++++ cgroups/systemd/apply_nosystemd.go | 4 ++++ cgroups/systemd/apply_systemd.go | 9 ++++++++- container_linux_test.go | 4 ++++ 13 files changed, 163 insertions(+), 2 deletions(-) diff --git a/cgroups/cgroups.go b/cgroups/cgroups.go index 7ed9be81..df7bfb3c 100644 --- a/cgroups/cgroups.go +++ b/cgroups/cgroups.go @@ -31,6 +31,9 @@ type Manager interface { // Returns cgroup paths to save in a state file and to be able to // restore the object later. GetPaths() map[string]string + + // Set the cgroup as configured. + Set(container *configs.Config) error } type NotFoundError struct { diff --git a/cgroups/fs/apply_raw.go b/cgroups/fs/apply_raw.go index ba7dd768..12ece2fe 100644 --- a/cgroups/fs/apply_raw.go +++ b/cgroups/fs/apply_raw.go @@ -32,6 +32,8 @@ type subsystem interface { Remove(*data) error // Creates and joins the cgroup represented by data. Apply(*data) error + // Set the cgroup represented by cgroup. + Set(path string, cgroup *configs.Cgroup) error } type Manager struct { @@ -147,6 +149,20 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) { return stats, nil } +func (m *Manager) Set(container *configs.Config) error { + for name, path := range m.Paths { + sys, ok := subsystems[name] + if !ok || !cgroups.PathExists(path) { + continue + } + if err := sys.Set(path, container.Cgroups); err != nil { + return err + } + } + + return nil +} + // Freeze toggles the container's freezer cgroup depending on the state // provided func (m *Manager) Freeze(state configs.FreezerState) error { diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index 87d91128..275b78ed 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type BlkioGroup struct { @@ -29,6 +30,16 @@ func (s *BlkioGroup) Apply(d *data) error { return nil } +func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.BlkioWeight != 0 { + if err := writeFile(path, "blkio.weight", strconv.FormatInt(cgroup.BlkioWeight, 10)); err != nil { + return err + } + } + + return nil +} + func (s *BlkioGroup) Remove(d *data) error { return removePath(d.path("blkio")) } diff --git a/cgroups/fs/cpu.go b/cgroups/fs/cpu.go index 385625c7..0a5040af 100644 --- a/cgroups/fs/cpu.go +++ b/cgroups/fs/cpu.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type CpuGroup struct { @@ -37,6 +38,26 @@ func (s *CpuGroup) Apply(d *data) error { return nil } +func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.CpuShares != 0 { + if err := writeFile(path, "cpu.shares", strconv.FormatInt(cgroup.CpuShares, 10)); err != nil { + return err + } + } + if cgroup.CpuPeriod != 0 { + if err := writeFile(path, "cpu.cfs_period_us", strconv.FormatInt(cgroup.CpuPeriod, 10)); err != nil { + return err + } + } + if cgroup.CpuQuota != 0 { + if err := writeFile(path, "cpu.cfs_quota_us", strconv.FormatInt(cgroup.CpuQuota, 10)); err != nil { + return err + } + } + + return nil +} + func (s *CpuGroup) Remove(d *data) error { return removePath(d.path("cpu")) } diff --git a/cgroups/fs/cpuacct.go b/cgroups/fs/cpuacct.go index 58378125..decee850 100644 --- a/cgroups/fs/cpuacct.go +++ b/cgroups/fs/cpuacct.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/system" ) @@ -30,6 +31,10 @@ func (s *CpuacctGroup) Apply(d *data) error { return nil } +func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error { + return nil +} + func (s *CpuacctGroup) Remove(d *data) error { return removePath(d.path("cpuacct")) } diff --git a/cgroups/fs/cpuset.go b/cgroups/fs/cpuset.go index d9b3e7b7..54d1aee4 100644 --- a/cgroups/fs/cpuset.go +++ b/cgroups/fs/cpuset.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type CpusetGroup struct { @@ -21,6 +22,22 @@ func (s *CpusetGroup) Apply(d *data) error { return s.ApplyDir(dir, d.c.CpusetCpus, d.c.CpusetMems, d.pid) } +func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.CpusetCpus != "" { + if err := writeFile(path, "cpuset.cpus", cgroup.CpusetCpus); err != nil { + return err + } + } + + if cgroup.CpusetMems != "" { + if err := writeFile(path, "cpuset.mems", cgroup.CpusetMems); err != nil { + return err + } + } + + return nil +} + func (s *CpusetGroup) Remove(d *data) error { return removePath(d.path("cpuset")) } diff --git a/cgroups/fs/devices.go b/cgroups/fs/devices.go index d1fef46f..835cfeef 100644 --- a/cgroups/fs/devices.go +++ b/cgroups/fs/devices.go @@ -1,6 +1,9 @@ package fs -import "github.com/docker/libcontainer/cgroups" +import ( + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" +) type DevicesGroup struct { } @@ -25,6 +28,22 @@ func (s *DevicesGroup) Apply(d *data) error { return nil } +func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error { + if !cgroup.AllowAllDevices { + if err := writeFile(path, "devices.deny", "a"); err != nil { + return err + } + + for _, dev := range cgroup.AllowedDevices { + if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil { + return err + } + } + } + + return nil +} + func (s *DevicesGroup) Remove(d *data) error { return removePath(d.path("devices")) } diff --git a/cgroups/fs/freezer.go b/cgroups/fs/freezer.go index 6e9aef18..738d9c0d 100644 --- a/cgroups/fs/freezer.go +++ b/cgroups/fs/freezer.go @@ -42,6 +42,28 @@ func (s *FreezerGroup) Apply(d *data) error { return nil } +func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error { + switch cgroup.Freezer { + case configs.Frozen, configs.Thawed: + if err := writeFile(path, "freezer.state", string(cgroup.Freezer)); err != nil { + return err + } + + for { + state, err := readFile(path, "freezer.state") + if err != nil { + return err + } + if strings.TrimSpace(state) == string(cgroup.Freezer) { + break + } + time.Sleep(1 * time.Millisecond) + } + } + + return nil +} + func (s *FreezerGroup) Remove(d *data) error { return removePath(d.path("freezer")) } diff --git a/cgroups/fs/memory.go b/cgroups/fs/memory.go index 573c8ab7..828d4b20 100644 --- a/cgroups/fs/memory.go +++ b/cgroups/fs/memory.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type MemoryGroup struct { @@ -53,6 +54,32 @@ func (s *MemoryGroup) Apply(d *data) error { return nil } +func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.Memory != 0 { + if err := writeFile(path, "memory.limit_in_bytes", strconv.FormatInt(cgroup.Memory, 10)); err != nil { + return err + } + } + if cgroup.MemoryReservation != 0 { + if err := writeFile(path, "memory.soft_limit_in_bytes", strconv.FormatInt(cgroup.MemoryReservation, 10)); err != nil { + return err + } + } + // By default, MemorySwap is set to twice the size of Memory. + if cgroup.MemorySwap == 0 && cgroup.Memory != 0 { + if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.Memory*2, 10)); err != nil { + return err + } + } + if cgroup.MemorySwap > 0 { + if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.MemorySwap, 10)); err != nil { + return err + } + } + + return nil +} + func (s *MemoryGroup) Remove(d *data) error { return removePath(d.path("memory")) } diff --git a/cgroups/fs/perf_event.go b/cgroups/fs/perf_event.go index 305b4c05..ca65f734 100644 --- a/cgroups/fs/perf_event.go +++ b/cgroups/fs/perf_event.go @@ -2,6 +2,7 @@ package fs import ( "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type PerfEventGroup struct { @@ -15,6 +16,10 @@ func (s *PerfEventGroup) Apply(d *data) error { return nil } +func (s *PerfEventGroup) Set(path string, cgroup *configs.Cgroup) error { + return nil +} + func (s *PerfEventGroup) Remove(d *data) error { return removePath(d.path("perf_event")) } diff --git a/cgroups/systemd/apply_nosystemd.go b/cgroups/systemd/apply_nosystemd.go index 8a46ea8b..95ed4ea7 100644 --- a/cgroups/systemd/apply_nosystemd.go +++ b/cgroups/systemd/apply_nosystemd.go @@ -38,6 +38,10 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) { return nil, fmt.Errorf("Systemd not supported") } +func (m *Manager) Set(container *configs.Config) error { + return nil, fmt.Errorf("Systemd not supported") +} + func (m *Manager) Freeze(state configs.FreezerState) error { return fmt.Errorf("Systemd not supported") } diff --git a/cgroups/systemd/apply_systemd.go b/cgroups/systemd/apply_systemd.go index 5d45fd6f..7e8fe0a0 100644 --- a/cgroups/systemd/apply_systemd.go +++ b/cgroups/systemd/apply_systemd.go @@ -26,7 +26,10 @@ type Manager struct { } type subsystem interface { - GetStats(string, *cgroups.Stats) error + // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. + GetStats(path string, stats *cgroups.Stats) error + // Set the cgroup represented by cgroup. + Set(path string, cgroup *configs.Cgroup) error } var subsystems = map[string]subsystem{ @@ -323,6 +326,10 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) { return stats, nil } +func (m *Manager) Set(container *configs.Config) error { + panic("not implemented") +} + func getUnitName(c *configs.Cgroup) string { return fmt.Sprintf("%s-%s.scope", c.Parent, c.Name) } diff --git a/container_linux_test.go b/container_linux_test.go index 87942a8c..5f589f3d 100644 --- a/container_linux_test.go +++ b/container_linux_test.go @@ -29,6 +29,10 @@ func (m *mockCgroupManager) Apply(pid int) error { return nil } +func (m *mockCgroupManager) Set(container *configs.Config) error { + return nil +} + func (m *mockCgroupManager) Destroy() error { return nil } From 1db687f4f480c06e6cadfdb0971985df4313ddc7 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Fri, 27 Feb 2015 12:09:42 +0800 Subject: [PATCH 3/5] add Set api Signed-off-by: Qiang Huang --- cgroups/fs/blkio.go | 6 ++---- cgroups/fs/cpu.go | 18 ++++-------------- cgroups/fs/cpuset.go | 19 ++++++------------- cgroups/fs/devices.go | 13 +++---------- cgroups/fs/freezer.go | 13 +------------ cgroups/fs/memory.go | 27 +++------------------------ cgroups/systemd/apply_systemd.go | 2 +- container.go | 8 ++++++++ container_linux.go | 6 ++++++ 9 files changed, 34 insertions(+), 78 deletions(-) diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index 275b78ed..8e132643 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -21,10 +21,8 @@ func (s *BlkioGroup) Apply(d *data) error { return err } - if d.c.BlkioWeight != 0 { - if err := writeFile(dir, "blkio.weight", strconv.FormatInt(d.c.BlkioWeight, 10)); err != nil { - return err - } + if err := s.Set(dir, d.c); err != nil { + return err } return nil diff --git a/cgroups/fs/cpu.go b/cgroups/fs/cpu.go index 0a5040af..1fbf7b15 100644 --- a/cgroups/fs/cpu.go +++ b/cgroups/fs/cpu.go @@ -20,21 +20,11 @@ func (s *CpuGroup) Apply(d *data) error { if err != nil { return err } - if d.c.CpuShares != 0 { - if err := writeFile(dir, "cpu.shares", strconv.FormatInt(d.c.CpuShares, 10)); err != nil { - return err - } - } - if d.c.CpuPeriod != 0 { - if err := writeFile(dir, "cpu.cfs_period_us", strconv.FormatInt(d.c.CpuPeriod, 10)); err != nil { - return err - } - } - if d.c.CpuQuota != 0 { - if err := writeFile(dir, "cpu.cfs_quota_us", strconv.FormatInt(d.c.CpuQuota, 10)); err != nil { - return err - } + + if err := s.Set(dir, d.c); err != nil { + return err } + return nil } diff --git a/cgroups/fs/cpuset.go b/cgroups/fs/cpuset.go index 54d1aee4..3ed2f67d 100644 --- a/cgroups/fs/cpuset.go +++ b/cgroups/fs/cpuset.go @@ -19,7 +19,7 @@ func (s *CpusetGroup) Apply(d *data) error { if err != nil { return err } - return s.ApplyDir(dir, d.c.CpusetCpus, d.c.CpusetMems, d.pid) + return s.ApplyDir(dir, d.c, d.pid) } func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error { @@ -46,7 +46,7 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } -func (s *CpusetGroup) ApplyDir(dir, cpus string, mems string, pid int) error { +func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error { if err := s.ensureParent(dir); err != nil { return err } @@ -57,17 +57,10 @@ func (s *CpusetGroup) ApplyDir(dir, cpus string, mems string, pid int) error { return err } - // If we don't use --cpuset-xxx, the default value inherit from parent cgroup - // is set in s.ensureParent, otherwise, use the value we set - if cpus != "" { - if err := writeFile(dir, "cpuset.cpus", cpus); err != nil { - return err - } - } - if mems != "" { - if err := writeFile(dir, "cpuset.mems", mems); err != nil { - return err - } + // the default values inherit from parent cgroup are already set in + // s.ensureParent, cover these if we have our own + if err := s.Set(dir, cgroup); err != nil { + return err } return nil diff --git a/cgroups/fs/devices.go b/cgroups/fs/devices.go index 835cfeef..16e00b1c 100644 --- a/cgroups/fs/devices.go +++ b/cgroups/fs/devices.go @@ -14,17 +14,10 @@ func (s *DevicesGroup) Apply(d *data) error { return err } - if !d.c.AllowAllDevices { - if err := writeFile(dir, "devices.deny", "a"); err != nil { - return err - } - - for _, dev := range d.c.AllowedDevices { - if err := writeFile(dir, "devices.allow", dev.CgroupString()); err != nil { - return err - } - } + if err := s.Set(dir, d.c); err != nil { + return err } + return nil } diff --git a/cgroups/fs/freezer.go b/cgroups/fs/freezer.go index 738d9c0d..d7c0da54 100644 --- a/cgroups/fs/freezer.go +++ b/cgroups/fs/freezer.go @@ -19,20 +19,9 @@ func (s *FreezerGroup) Apply(d *data) error { return err } - if err := writeFile(dir, "freezer.state", string(d.c.Freezer)); err != nil { + if err := s.Set(dir, d.c); err != nil { return err } - - for { - state, err := readFile(dir, "freezer.state") - if err != nil { - return err - } - if strings.TrimSpace(state) == string(d.c.Freezer) { - break - } - time.Sleep(1 * time.Millisecond) - } default: if _, err := d.join("freezer"); err != nil && !cgroups.IsNotFound(err) { return err diff --git a/cgroups/fs/memory.go b/cgroups/fs/memory.go index 828d4b20..a2e4f33d 100644 --- a/cgroups/fs/memory.go +++ b/cgroups/fs/memory.go @@ -26,31 +26,10 @@ func (s *MemoryGroup) Apply(d *data) error { } }() - // Only set values if some config was specified. - if d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0 { - if d.c.Memory != 0 { - if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil { - return err - } - } - if d.c.MemoryReservation != 0 { - if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.MemoryReservation, 10)); err != nil { - return err - } - } - // By default, MemorySwap is set to twice the size of RAM. - // If you want to omit MemorySwap, set it to '-1'. - if d.c.MemorySwap == 0 { - if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil { - return err - } - } - if d.c.MemorySwap > 0 { - if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.MemorySwap, 10)); err != nil { - return err - } - } + if err := s.Set(dir, d.c); err != nil { + return err } + return nil } diff --git a/cgroups/systemd/apply_systemd.go b/cgroups/systemd/apply_systemd.go index 7e8fe0a0..f4358e1a 100644 --- a/cgroups/systemd/apply_systemd.go +++ b/cgroups/systemd/apply_systemd.go @@ -405,5 +405,5 @@ func joinCpuset(c *configs.Cgroup, pid int) error { s := &fs.CpusetGroup{} - return s.ApplyDir(path, c.CpusetCpus, c.CpusetMems, pid) + return s.ApplyDir(path, c, pid) } diff --git a/container.go b/container.go index 59a544b6..cebe8273 100644 --- a/container.go +++ b/container.go @@ -90,6 +90,14 @@ type Container interface { // Systemerror - System error. Stats() (*Stats, error) + // Set cgroup resources of container as configured + // + // We can use this to change resources when containers are running. + // + // errors: + // Systemerror - System error. + Set() error + // Start a process inside the container. Returns error if process fails to // start. You can track process lifecycle with passed Process structure. // diff --git a/container_linux.go b/container_linux.go index 3ea08db2..c238e62c 100644 --- a/container_linux.go +++ b/container_linux.go @@ -78,6 +78,12 @@ func (c *linuxContainer) Stats() (*Stats, error) { return stats, nil } +func (c *linuxContainer) Set() error { + c.m.Lock() + defer c.m.Unlock() + return c.cgroupManager.Set(c.config) +} + func (c *linuxContainer) Start(process *Process) error { c.m.Lock() defer c.m.Unlock() From 74005ed4e0cdbc87ce40c6b79edfd599ba2355e9 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Fri, 13 Feb 2015 14:03:15 +0800 Subject: [PATCH 4/5] add function to get string value from cgroup file Signed-off-by: Qiang Huang --- cgroups/fs/utils.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cgroups/fs/utils.go b/cgroups/fs/utils.go index f37a3a48..c2f75c8e 100644 --- a/cgroups/fs/utils.go +++ b/cgroups/fs/utils.go @@ -60,3 +60,13 @@ func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) { return parseUint(strings.TrimSpace(string(contents)), 10, 64) } + +// Gets a string value from the specified cgroup file +func getCgroupParamString(cgroupPath, cgroupFile string) (string, error) { + contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile)) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(contents)), nil +} From 4077c254a6ac99930d720a9b95709dbd2614bc61 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Fri, 27 Feb 2015 11:00:17 +0800 Subject: [PATCH 5/5] add test cases for Set interfaces Signed-off-by: Qiang Huang --- cgroups/fs/blkio_test.go | 30 ++++++++++++++++ cgroups/fs/cpu_test.go | 70 ++++++++++++++++++++++++++++++++++++++ cgroups/fs/cpuset_test.go | 63 ++++++++++++++++++++++++++++++++++ cgroups/fs/devices_test.go | 48 ++++++++++++++++++++++++++ cgroups/fs/memory_test.go | 41 ++++++++++++++++++++++ cgroups/fs/util_test.go | 6 +++- 6 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 cgroups/fs/cpuset_test.go create mode 100644 cgroups/fs/devices_test.go diff --git a/cgroups/fs/blkio_test.go b/cgroups/fs/blkio_test.go index 6cd38cba..9ef93fcf 100644 --- a/cgroups/fs/blkio_test.go +++ b/cgroups/fs/blkio_test.go @@ -1,6 +1,7 @@ package fs import ( + "strconv" "testing" "github.com/docker/libcontainer/cgroups" @@ -72,6 +73,35 @@ func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, min *blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op}) } +func TestBlkioSetWeight(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + const ( + weightBefore = 100 + weightAfter = 200 + ) + + helper.writeFileContents(map[string]string{ + "blkio.weight": strconv.Itoa(weightBefore), + }) + + helper.CgroupData.c.BlkioWeight = weightAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "blkio.weight") + if err != nil { + t.Fatalf("Failed to parse blkio.weight - %s", err) + } + + if value != weightAfter { + t.Fatal("Got the wrong value, set blkio.weight failed.") + } +} + func TestBlkioStats(t *testing.T) { helper := NewCgroupTestUtil("blkio", t) defer helper.cleanup() diff --git a/cgroups/fs/cpu_test.go b/cgroups/fs/cpu_test.go index 2470e689..bcf4ac4e 100644 --- a/cgroups/fs/cpu_test.go +++ b/cgroups/fs/cpu_test.go @@ -2,11 +2,81 @@ package fs import ( "fmt" + "strconv" "testing" "github.com/docker/libcontainer/cgroups" ) +func TestCpuSetShares(t *testing.T) { + helper := NewCgroupTestUtil("cpu", t) + defer helper.cleanup() + + const ( + sharesBefore = 1024 + sharesAfter = 512 + ) + + helper.writeFileContents(map[string]string{ + "cpu.shares": strconv.Itoa(sharesBefore), + }) + + helper.CgroupData.c.CpuShares = sharesAfter + cpu := &CpuGroup{} + if err := cpu.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "cpu.shares") + if err != nil { + t.Fatalf("Failed to parse cpu.shares - %s", err) + } + + if value != sharesAfter { + t.Fatal("Got the wrong value, set cpu.shares failed.") + } +} + +func TestCpuSetBandWidth(t *testing.T) { + helper := NewCgroupTestUtil("cpu", t) + defer helper.cleanup() + + const ( + quotaBefore = 8000 + quotaAfter = 5000 + periodBefore = 10000 + periodAfter = 7000 + ) + + helper.writeFileContents(map[string]string{ + "cpu.cfs_quota_us": strconv.Itoa(quotaBefore), + "cpu.cfs_period_us": strconv.Itoa(periodBefore), + }) + + helper.CgroupData.c.CpuQuota = quotaAfter + helper.CgroupData.c.CpuPeriod = periodAfter + cpu := &CpuGroup{} + if err := cpu.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + quota, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_quota_us") + if err != nil { + t.Fatalf("Failed to parse cpu.cfs_quota_us - %s", err) + } + if quota != quotaAfter { + t.Fatal("Got the wrong value, set cpu.cfs_quota_us failed.") + } + + period, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_period_us") + if err != nil { + t.Fatalf("Failed to parse cpu.cfs_period_us - %s", err) + } + if period != periodAfter { + t.Fatal("Got the wrong value, set cpu.cfs_period_us failed.") + } +} + func TestCpuStats(t *testing.T) { helper := NewCgroupTestUtil("cpu", t) defer helper.cleanup() diff --git a/cgroups/fs/cpuset_test.go b/cgroups/fs/cpuset_test.go new file mode 100644 index 00000000..7449cdca --- /dev/null +++ b/cgroups/fs/cpuset_test.go @@ -0,0 +1,63 @@ +package fs + +import ( + "testing" +) + +func TestCpusetSetCpus(t *testing.T) { + helper := NewCgroupTestUtil("cpuset", t) + defer helper.cleanup() + + const ( + cpusBefore = "0" + cpusAfter = "1-3" + ) + + helper.writeFileContents(map[string]string{ + "cpuset.cpus": cpusBefore, + }) + + helper.CgroupData.c.CpusetCpus = cpusAfter + cpuset := &CpusetGroup{} + if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "cpuset.cpus") + if err != nil { + t.Fatalf("Failed to parse cpuset.cpus - %s", err) + } + + if value != cpusAfter { + t.Fatal("Got the wrong value, set cpuset.cpus failed.") + } +} + +func TestCpusetSetMems(t *testing.T) { + helper := NewCgroupTestUtil("cpuset", t) + defer helper.cleanup() + + const ( + memsBefore = "0" + memsAfter = "1" + ) + + helper.writeFileContents(map[string]string{ + "cpuset.mems": memsBefore, + }) + + helper.CgroupData.c.CpusetMems = memsAfter + cpuset := &CpusetGroup{} + if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "cpuset.mems") + if err != nil { + t.Fatalf("Failed to parse cpuset.mems - %s", err) + } + + if value != memsAfter { + t.Fatal("Got the wrong value, set cpuset.mems failed.") + } +} diff --git a/cgroups/fs/devices_test.go b/cgroups/fs/devices_test.go new file mode 100644 index 00000000..d87d0924 --- /dev/null +++ b/cgroups/fs/devices_test.go @@ -0,0 +1,48 @@ +package fs + +import ( + "testing" + + "github.com/docker/libcontainer/configs" +) + +var ( + allowedDevices = []*configs.Device{ + { + Path: "/dev/zero", + Type: 'c', + Major: 1, + Minor: 5, + Permissions: "rwm", + FileMode: 0666, + }, + } + allowedList = "c 1:5 rwm" +) + +func TestDevicesSetAllow(t *testing.T) { + helper := NewCgroupTestUtil("devices", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "device.deny": "a", + }) + + helper.CgroupData.c.AllowAllDevices = false + helper.CgroupData.c.AllowedDevices = allowedDevices + devices := &DevicesGroup{} + if err := devices.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + // FIXME: this doesn't make sence, the file devices.allow under real cgroupfs + // is not allowed to read. Our test path don't have cgroupfs mounted. + value, err := getCgroupParamString(helper.CgroupPath, "devices.allow") + if err != nil { + t.Fatalf("Failed to parse devices.allow - %s", err) + } + + if value != allowedList { + t.Fatal("Got the wrong value, set devices.allow failed.") + } +} diff --git a/cgroups/fs/memory_test.go b/cgroups/fs/memory_test.go index a21cec75..70f17a70 100644 --- a/cgroups/fs/memory_test.go +++ b/cgroups/fs/memory_test.go @@ -1,6 +1,7 @@ package fs import ( + "strconv" "testing" "github.com/docker/libcontainer/cgroups" @@ -14,6 +15,46 @@ rss 1024` memoryFailcnt = "100\n" ) +func TestMemorySetMemory(t *testing.T) { + helper := NewCgroupTestUtil("memory", t) + defer helper.cleanup() + + const ( + memoryBefore = 314572800 // 300M + memoryAfter = 524288000 // 500M + reservationBefore = 209715200 // 200M + reservationAfter = 314572800 // 300M + ) + + helper.writeFileContents(map[string]string{ + "memory.limit_in_bytes": strconv.Itoa(memoryBefore), + "memory.soft_limit_in_bytes": strconv.Itoa(reservationBefore), + }) + + helper.CgroupData.c.Memory = memoryAfter + helper.CgroupData.c.MemoryReservation = reservationAfter + memory := &MemoryGroup{} + if err := memory.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") + if err != nil { + t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) + } + if value != memoryAfter { + t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") + } + + value, err = getCgroupParamUint(helper.CgroupPath, "memory.soft_limit_in_bytes") + if err != nil { + t.Fatalf("Failed to parse memory.soft_limit_in_bytes - %s", err) + } + if value != reservationAfter { + t.Fatal("Got the wrong value, set memory.soft_limit_in_bytes failed.") + } +} + func TestMemoryStats(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() diff --git a/cgroups/fs/util_test.go b/cgroups/fs/util_test.go index 548870a8..e0c1262d 100644 --- a/cgroups/fs/util_test.go +++ b/cgroups/fs/util_test.go @@ -10,6 +10,8 @@ import ( "io/ioutil" "os" "testing" + + "github.com/docker/libcontainer/configs" ) type cgroupTestUtil struct { @@ -26,7 +28,9 @@ type cgroupTestUtil struct { // Creates a new test util for the specified subsystem func NewCgroupTestUtil(subsystem string, t *testing.T) *cgroupTestUtil { - d := &data{} + d := &data{ + c: &configs.Cgroup{}, + } tempDir, err := ioutil.TempDir("", fmt.Sprintf("%s_cgroup_test", subsystem)) if err != nil { t.Fatal(err)