Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcontainer/cgroups/fs/apply_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var (
&NetPrioGroup{},
&PerfEventGroup{},
&FreezerGroup{},
&PidsGroup{},
}
CgroupProcesses = "cgroup.procs"
HugePageSizes, _ = cgroups.GetHugePageSize()
Expand Down
86 changes: 86 additions & 0 deletions libcontainer/cgroups/fs/pids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// +build linux

package fs

import (
"fmt"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"strconv"
)

type PidsGroup struct {
}

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

func (s *PidsGroup) Apply(d *data) error {
dir, err := d.join("pids")
if err != nil {
// since Linux 4.3
if cgroups.IsNotFound(err) {
return nil
}
return err
}

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

return nil
}

func (s *PidsGroup) Set(path string, cgroup *configs.Cgroup) error {

if cgroup.PidsMax > 0 {
if err := writeFile(path, "pids.max", fmt.Sprint(cgroup.PidsMax)); err != nil {
return err
}
} else if cgroup.PidsMax < 0 {
if err := writeFile(path, "pids.max", "max"); err != nil {
return err
}
}

for pid := range cgroup.Pids {
if err := writeFile(path, "cgroup.procs", strconv.Itoa(pid)); err != nil {
return err
}
}

return nil
}

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

func (s *PidsGroup) GetStats(path string, stats *cgroups.Stats) error {

values, err := getCgroupParamUintArray(path, "pids.current")
if err != nil {
return fmt.Errorf("failed to parse pids.current - %v", err)
}

stats.PidsStats.Current = values

value, err := getCgroupParamString(path, "pids.max")
if err != nil {
return fmt.Errorf("failed to parse pids.max - %v", err)
}

if "max" == value {
stats.PidsStats.Max = configs.MaxPids
} else {
max, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("failed to parse pids.max content %s as int", value)
}
stats.PidsStats.Max = max
}

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

package fs

import (
"os"
"strconv"
"testing"

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

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

pidsArray := make([]uint32, 1)
pidsArray[0] = uint32(os.Getpid())

helper.CgroupData.c.Pids = pidsArray
pids := &PidsGroup{}
if err := pids.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
t.Fatal(err)
}
}

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

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

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

if actualStats.PidsStats.Current == nil {
t.Fatal("Expected PidsStats to be set")
}

if len(actualStats.PidsStats.Current) != 1 {
t.Fatal("Expected PidsStats.Current to have at least one element")
}

if actualStats.PidsStats.Max != configs.MaxPids {
t.Fatal("Expected PidsStats.Max to return -1")
}
}
30 changes: 30 additions & 0 deletions libcontainer/cgroups/fs/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
package fs

import (
"bufio"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -68,6 +71,33 @@ func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) {
return res, nil
}

// Gets an array of uint64 values from the specified cgroup file.
func getCgroupParamUintArray(cgroupPath, cgroupFile string) ([]uint64, error) {

var res []uint64

fileName := filepath.Join(cgroupPath, cgroupFile)
file, err := os.Open(fileName)
if err != nil {
return res, err
}
defer file.Close()

reader := bufio.NewReader(file)
for {
line, _, err := reader.ReadLine()
if err == io.EOF {
break
}
item, err := parseUint(strings.TrimSpace(string(line)), 10, 64)
if err != nil {
return res, fmt.Errorf("unable to parse %q as a uint from Cgroup file %q", string(line), fileName)
}
res = append(res, item)
}
return res, nil
}

// Gets a string value from the specified cgroup file
func getCgroupParamString(cgroupPath, cgroupFile string) (string, error) {
contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile))
Expand Down
9 changes: 9 additions & 0 deletions libcontainer/cgroups/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,21 @@ type HugetlbStats struct {
Failcnt uint64 `json:"failcnt"`
}

type PidsStats struct {
// current pids in cgroup; the set of processes can change at any time
Current []uint64 `json:"current,omitempty"`

// maximum number of processes allowed in this cgroup (fork limit)
Max int `json:"max,omitempty"`
}

type Stats struct {
CpuStats CpuStats `json:"cpu_stats,omitempty"`
MemoryStats MemoryStats `json:"memory_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"`
PidsStats PidsStats `json:"pids_stats,omitempty"`
}

func NewStats() *Stats {
Expand Down
8 changes: 8 additions & 0 deletions libcontainer/configs/cgroup_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const (
Undefined FreezerState = ""
Frozen FreezerState = "FROZEN"
Thawed FreezerState = "THAWED"

MaxPids = -1
)

type Cgroup struct {
Expand Down Expand Up @@ -97,4 +99,10 @@ type Cgroup struct {

// Set class identifier for container's network packets
NetClsClassid string `json:"net_cls_classid"`

// max. number of processes in this cgroup
PidsMax uint32 `json:"pids_max"`

// a set of processes to add to this cgroup
Pids []uint32 `json:"pids"`
}