Skip to content

Commit

Permalink
Merge pull request #446 from cyphar/18-add-pids-controller
Browse files Browse the repository at this point in the history
cgroup: add PIDs cgroup controller support
  • Loading branch information
Mrunal Patel committed Jan 12, 2016
2 parents 421ebfd + 103853e commit 4c767d7
Show file tree
Hide file tree
Showing 22 changed files with 415 additions and 190 deletions.
35 changes: 23 additions & 12 deletions libcontainer/cgroups/fs/apply_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var (
&MemoryGroup{},
&CpuGroup{},
&CpuacctGroup{},
&PidsGroup{},
&BlkioGroup{},
&HugetlbGroup{},
&NetClsGroup{},
Expand Down Expand Up @@ -105,8 +106,6 @@ func (m *Manager) Apply(pid int) (err error) {
return nil
}

var c = m.Cgroups

d, err := getCgroupData(m.Cgroups, pid)
if err != nil {
return err
Expand Down Expand Up @@ -135,13 +134,6 @@ func (m *Manager) Apply(pid int) (err error) {
paths[sys.Name()] = p
}
m.Paths = paths

if paths["cpu"] != "" {
if err := CheckCpushares(paths["cpu"], c.Resources.CpuShares); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -179,15 +171,34 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) {
}

func (m *Manager) Set(container *configs.Config) error {
for name, path := range m.Paths {
sys, err := subsystems.Get(name)
if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
for _, sys := range subsystems {
// We can't set this here, because after being applied, memcg doesn't
// allow a non-empty cgroup from having its limits changed.
if sys.Name() == "memory" {
continue
}

// Generate fake cgroup data.
d, err := getCgroupData(container.Cgroups, -1)
if err != nil {
return err
}
// Get the path, but don't error out if the cgroup wasn't found.
path, err := d.path(sys.Name())
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := sys.Set(path, container.Cgroups); err != nil {
return err
}
}

if m.Paths["cpu"] != "" {
if err := CheckCpushares(m.Paths["cpu"], container.Cgroups.Resources.CpuShares); err != nil {
return err
}
}
return nil
}

Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/blkio.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ func (s *BlkioGroup) Name() string {
}

func (s *BlkioGroup) Apply(d *cgroupData) error {
dir, err := d.join("blkio")
_, err := d.join("blkio")
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ 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
dir, err := d.join("cpu")
_, err := d.join("cpu")
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
5 changes: 0 additions & 5 deletions libcontainer/cgroups/fs/cpuset.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro
if err := s.ensureParent(dir, root); 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
}
// because we are not using d.join we need to place the pid into the procs file
// unlike the other subsystems
if err := writeFile(dir, "cgroup.procs", strconv.Itoa(pid)); err != nil {
Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@ func (s *DevicesGroup) Name() string {
}

func (s *DevicesGroup) Apply(d *cgroupData) error {
dir, err := d.join("devices")
_, err := d.join("devices")
if err != nil {
// We will return error even it's `not found` error, devices
// cgroup is hard requirement for container's security.
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/freezer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,10 @@ func (s *FreezerGroup) Name() string {
}

func (s *FreezerGroup) Apply(d *cgroupData) error {
dir, err := d.join("freezer")
_, err := d.join("freezer")
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/hugetlb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,10 @@ func (s *HugetlbGroup) Name() string {
}

func (s *HugetlbGroup) Apply(d *cgroupData) error {
dir, err := d.join("hugetlb")
_, err := d.join("hugetlb")
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
1 change: 0 additions & 1 deletion libcontainer/cgroups/fs/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
return err
}
}

if err := s.Set(path, d.config); err != nil {
return err
}
Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/net_cls.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,10 @@ func (s *NetClsGroup) Name() string {
}

func (s *NetClsGroup) Apply(d *cgroupData) error {
dir, err := d.join("net_cls")
_, err := d.join("net_cls")
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
7 changes: 1 addition & 6 deletions libcontainer/cgroups/fs/net_prio.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,10 @@ func (s *NetPrioGroup) Name() string {
}

func (s *NetPrioGroup) Apply(d *cgroupData) error {
dir, err := d.join("net_prio")
_, err := d.join("net_prio")
if err != nil && !cgroups.IsNotFound(err) {
return err
}

if err := s.Set(dir, d.config); err != nil {
return err
}

return nil
}

Expand Down
57 changes: 57 additions & 0 deletions libcontainer/cgroups/fs/pids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// +build linux

package fs

import (
"fmt"
"strconv"

"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
)

type PidsGroup struct {
}

func (s *PidsGroup) Name() string {
return "pids"
}

func (s *PidsGroup) Apply(d *cgroupData) error {
_, err := d.join("pids")
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
}

func (s *PidsGroup) Set(path string, cgroup *configs.Cgroup) error {
if cgroup.Resources.PidsLimit != 0 {
// "max" is the fallback value.
limit := "max"

if cgroup.Resources.PidsLimit > 0 {
limit = strconv.FormatInt(cgroup.Resources.PidsLimit, 10)
}

if err := writeFile(path, "pids.max", limit); err != nil {
return err
}
}

return nil
}

func (s *PidsGroup) Remove(d *cgroupData) error {
return removePath(d.path("pids"))
}

func (s *PidsGroup) GetStats(path string, stats *cgroups.Stats) error {
value, err := getCgroupParamUint(path, "pids.current")
if err != nil {
return fmt.Errorf("failed to parse pids.current - %s", err)
}

stats.PidsStats.Current = value
return nil
}
83 changes: 83 additions & 0 deletions libcontainer/cgroups/fs/pids_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// +build linux

package fs

import (
"strconv"
"testing"

"github.com/opencontainers/runc/libcontainer/cgroups"
)

const (
maxUnlimited = -1
maxLimited = 1024
)

func TestPidsSetMax(t *testing.T) {
helper := NewCgroupTestUtil("pids", t)
defer helper.cleanup()

helper.writeFileContents(map[string]string{
"pids.max": "max",
})

helper.CgroupData.config.Resources.PidsLimit = maxLimited
pids := &PidsGroup{}
if err := pids.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
t.Fatal(err)
}

value, err := getCgroupParamUint(helper.CgroupPath, "pids.max")
if err != nil {
t.Fatalf("Failed to parse pids.max - %s", err)
}

if value != maxLimited {
t.Fatalf("Expected %d, got %d for setting pids.max - limited", maxLimited, value)
}
}

func TestPidsSetUnlimited(t *testing.T) {
helper := NewCgroupTestUtil("pids", t)
defer helper.cleanup()

helper.writeFileContents(map[string]string{
"pids.max": strconv.Itoa(maxLimited),
})

helper.CgroupData.config.Resources.PidsLimit = maxUnlimited
pids := &PidsGroup{}
if err := pids.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
t.Fatal(err)
}

value, err := getCgroupParamString(helper.CgroupPath, "pids.max")
if err != nil {
t.Fatalf("Failed to parse pids.max - %s", err)
}

if value != "max" {
t.Fatalf("Expected %s, got %s for setting pids.max - unlimited", "max", value)
}
}

func TestPidsStats(t *testing.T) {
helper := NewCgroupTestUtil("pids", t)
defer helper.cleanup()

helper.writeFileContents(map[string]string{
"pids.current": strconv.Itoa(1337),
"pids.max": strconv.Itoa(maxLimited),
})

pids := &PidsGroup{}
stats := *cgroups.NewStats()
if err := pids.GetStats(helper.CgroupPath, &stats); err != nil {
t.Fatal(err)
}

if stats.PidsStats.Current != 1337 {
t.Fatalf("Expected %d, got %d for pids.current", 1337, stats.PidsStats.Current)
}
}
6 changes: 6 additions & 0 deletions libcontainer/cgroups/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type MemoryStats struct {
Stats map[string]uint64 `json:"stats,omitempty"`
}

type PidsStats struct {
// number of pids in the cgroup
Current uint64 `json:"current,omitempty"`
}

type BlkioStatEntry struct {
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
Expand Down Expand Up @@ -80,6 +85,7 @@ type HugetlbStats struct {
type Stats struct {
CpuStats CpuStats `json:"cpu_stats,omitempty"`
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
PidsStats PidsStats `json:"pids_stats,omitempty"`
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
// the map is in the format "size of hugepage: stats of the hugepage"
HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"`
Expand Down
Loading

0 comments on commit 4c767d7

Please sign in to comment.