summaryrefslogtreecommitdiffhomepage
path: root/runsc/cgroup
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/cgroup')
-rw-r--r--runsc/cgroup/cgroup.go63
-rw-r--r--runsc/cgroup/cgroup_test.go127
2 files changed, 166 insertions, 24 deletions
diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go
index 13c6a16a0..797c1c2bc 100644
--- a/runsc/cgroup/cgroup.go
+++ b/runsc/cgroup/cgroup.go
@@ -203,6 +203,19 @@ func LoadPaths(pid string) (map[string]string, error) {
}
func loadPathsHelper(cgroup io.Reader) (map[string]string, error) {
+ // For nested containers, in /proc/self/cgroup we see paths from host,
+ // which don't exist in container, so recover the container paths here by
+ // double-checking with /proc/pid/mountinfo
+ mountinfo, err := os.Open("/proc/self/mountinfo")
+ if err != nil {
+ return nil, err
+ }
+ defer mountinfo.Close()
+
+ return loadPathsHelperWithMountinfo(cgroup, mountinfo)
+}
+
+func loadPathsHelperWithMountinfo(cgroup, mountinfo io.Reader) (map[string]string, error) {
paths := make(map[string]string)
scanner := bufio.NewScanner(cgroup)
@@ -225,6 +238,31 @@ func loadPathsHelper(cgroup io.Reader) (map[string]string, error) {
if err := scanner.Err(); err != nil {
return nil, err
}
+
+ mfScanner := bufio.NewScanner(mountinfo)
+ for mfScanner.Scan() {
+ txt := mfScanner.Text()
+ fields := strings.Fields(txt)
+ if len(fields) < 9 || fields[len(fields)-3] != "cgroup" {
+ continue
+ }
+ for _, opt := range strings.Split(fields[len(fields)-1], ",") {
+ // Remove prefix for cgroups with no controller, eg. systemd.
+ opt = strings.TrimPrefix(opt, "name=")
+ if cgroupPath, ok := paths[opt]; ok {
+ root := fields[3]
+ relCgroupPath, err := filepath.Rel(root, cgroupPath)
+ if err != nil {
+ return nil, err
+ }
+ paths[opt] = relCgroupPath
+ }
+ }
+ }
+ if err := mfScanner.Err(); err != nil {
+ return nil, err
+ }
+
return paths, nil
}
@@ -243,8 +281,13 @@ func New(spec *specs.Spec) (*Cgroup, error) {
if spec.Linux == nil || spec.Linux.CgroupsPath == "" {
return nil, nil
}
+ return NewFromPath(spec.Linux.CgroupsPath)
+}
+
+// NewFromPath creates a new Cgroup instance.
+func NewFromPath(cgroupsPath string) (*Cgroup, error) {
var parents map[string]string
- if !filepath.IsAbs(spec.Linux.CgroupsPath) {
+ if !filepath.IsAbs(cgroupsPath) {
var err error
parents, err = LoadPaths("self")
if err != nil {
@@ -253,7 +296,7 @@ func New(spec *specs.Spec) (*Cgroup, error) {
}
own := make(map[string]bool)
return &Cgroup{
- Name: spec.Linux.CgroupsPath,
+ Name: cgroupsPath,
Parents: parents,
Own: own,
}, nil
@@ -351,6 +394,9 @@ func (c *Cgroup) Join() (func(), error) {
undo = func() {
for _, path := range undoPaths {
log.Debugf("Restoring cgroup %q", path)
+ // Writing the value 0 to a cgroup.procs file causes
+ // the writing process to be moved to the corresponding
+ // cgroup. - cgroups(7).
if err := setValue(path, "cgroup.procs", "0"); err != nil {
log.Warningf("Error restoring cgroup %q: %v", path, err)
}
@@ -361,6 +407,9 @@ func (c *Cgroup) Join() (func(), error) {
for key, cfg := range controllers {
path := c.makePath(key)
log.Debugf("Joining cgroup %q", path)
+ // Writing the value 0 to a cgroup.procs file causes the
+ // writing process to be moved to the corresponding cgroup.
+ // - cgroups(7).
if err := setValue(path, "cgroup.procs", "0"); err != nil {
if cfg.optional && os.IsNotExist(err) {
continue
@@ -388,6 +437,16 @@ func (c *Cgroup) CPUQuota() (float64, error) {
return float64(quota) / float64(period), nil
}
+// CPUUsage returns the total CPU usage of the cgroup.
+func (c *Cgroup) CPUUsage() (uint64, error) {
+ path := c.makePath("cpuacct")
+ usage, err := getValue(path, "cpuacct.usage")
+ if err != nil {
+ return 0, err
+ }
+ return strconv.ParseUint(strings.TrimSpace(usage), 10, 64)
+}
+
// NumCPU returns the number of CPUs configured in 'cpuset/cpuset.cpus'.
func (c *Cgroup) NumCPU() (int, error) {
path := c.makePath("cpuset")
diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go
index 931144cf9..48d71cfa6 100644
--- a/runsc/cgroup/cgroup_test.go
+++ b/runsc/cgroup/cgroup_test.go
@@ -25,6 +25,39 @@ import (
"gvisor.dev/gvisor/pkg/test/testutil"
)
+var debianMountinfo = `
+35 24 0:30 / /sys/fs/cgroup ro shared:9 - tmpfs tmpfs ro
+36 35 0:31 / /sys/fs/cgroup/unified rw shared:10 - cgroup2 cgroup2 rw
+37 35 0:32 / /sys/fs/cgroup/systemd rw - cgroup cgroup rw,name=systemd
+41 35 0:36 / /sys/fs/cgroup/cpu,cpuacct rw shared:16 - cgroup cgroup rw,cpu,cpuacct
+42 35 0:37 / /sys/fs/cgroup/freezer rw shared:17 - cgroup cgroup rw,freezer
+43 35 0:38 / /sys/fs/cgroup/hugetlb rw shared:18 - cgroup cgroup rw,hugetlb
+44 35 0:39 / /sys/fs/cgroup/cpuset rw shared:19 - cgroup cgroup rw,cpuset
+45 35 0:40 / /sys/fs/cgroup/net_cls,net_prio rw shared:20 - cgroup cgroup rw,net_cls,net_prio
+46 35 0:41 / /sys/fs/cgroup/pids rw shared:21 - cgroup cgroup rw,pids
+47 35 0:42 / /sys/fs/cgroup/perf_event rw shared:22 - cgroup cgroup rw,perf_event
+48 35 0:43 / /sys/fs/cgroup/memory rw shared:23 - cgroup cgroup rw,memory
+49 35 0:44 / /sys/fs/cgroup/blkio rw shared:24 - cgroup cgroup rw,blkio
+50 35 0:45 / /sys/fs/cgroup/devices rw shared:25 - cgroup cgroup rw,devices
+51 35 0:46 / /sys/fs/cgroup/rdma rw shared:26 - cgroup cgroup rw,rdma
+`
+
+var dindMountinfo = `
+1305 1304 0:64 / /sys/fs/cgroup rw - tmpfs tmpfs rw,mode=755
+1306 1305 0:32 /docker/136 /sys/fs/cgroup/systemd ro master:11 - cgroup cgroup rw,xattr,name=systemd
+1307 1305 0:36 /docker/136 /sys/fs/cgroup/cpu,cpuacct ro master:16 - cgroup cgroup rw,cpu,cpuacct
+1308 1305 0:37 /docker/136 /sys/fs/cgroup/freezer ro master:17 - cgroup cgroup rw,freezer
+1309 1305 0:38 /docker/136 /sys/fs/cgroup/hugetlb ro master:18 - cgroup cgroup rw,hugetlb
+1310 1305 0:39 /docker/136 /sys/fs/cgroup/cpuset ro master:19 - cgroup cgroup rw,cpuset
+1311 1305 0:40 /docker/136 /sys/fs/cgroup/net_cls,net_prio ro master:20 - cgroup cgroup rw,net_cls,net_prio
+1312 1305 0:41 /docker/136 /sys/fs/cgroup/pids ro master:21 - cgroup cgroup rw,pids
+1313 1305 0:42 /docker/136 /sys/fs/cgroup/perf_event ro master:22 - cgroup cgroup rw,perf_event
+1314 1305 0:43 /docker/136 /sys/fs/cgroup/memory ro master:23 - cgroup cgroup rw,memory
+1316 1305 0:44 /docker/136 /sys/fs/cgroup/blkio ro master:24 - cgroup cgroup rw,blkio
+1317 1305 0:45 /docker/136 /sys/fs/cgroup/devices ro master:25 - cgroup cgroup rw,devices
+1318 1305 0:46 / /sys/fs/cgroup/rdma ro master:26 - cgroup cgroup rw,rdma
+`
+
func TestUninstallEnoent(t *testing.T) {
c := Cgroup{
// set a non-existent name
@@ -653,60 +686,110 @@ func TestPids(t *testing.T) {
func TestLoadPaths(t *testing.T) {
for _, tc := range []struct {
- name string
- cgroups string
- want map[string]string
- err string
+ name string
+ cgroups string
+ mountinfo string
+ want map[string]string
+ err string
}{
{
- name: "abs-path",
- cgroups: "0:ctr:/path",
- want: map[string]string{"ctr": "/path"},
+ name: "abs-path-unknown-controller",
+ cgroups: "0:ctr:/path",
+ mountinfo: debianMountinfo,
+ want: map[string]string{"ctr": "/path"},
},
{
- name: "rel-path",
- cgroups: "0:ctr:rel-path",
- want: map[string]string{"ctr": "rel-path"},
+ name: "rel-path",
+ cgroups: "0:ctr:rel-path",
+ mountinfo: debianMountinfo,
+ want: map[string]string{"ctr": "rel-path"},
},
{
- name: "non-controller",
- cgroups: "0:name=systemd:/path",
- want: map[string]string{"systemd": "/path"},
+ name: "non-controller",
+ cgroups: "0:name=systemd:/path",
+ mountinfo: debianMountinfo,
+ want: map[string]string{"systemd": "path"},
},
{
- name: "empty",
+ name: "empty",
+ mountinfo: debianMountinfo,
},
{
name: "multiple",
cgroups: "0:ctr0:/path0\n" +
"1:ctr1:/path1\n" +
"2::/empty\n",
+ mountinfo: debianMountinfo,
want: map[string]string{
"ctr0": "/path0",
"ctr1": "/path1",
},
},
{
- name: "missing-field",
- cgroups: "0:nopath\n",
- err: "invalid cgroups file",
+ name: "missing-field",
+ cgroups: "0:nopath\n",
+ mountinfo: debianMountinfo,
+ err: "invalid cgroups file",
},
{
- name: "too-many-fields",
- cgroups: "0:ctr:/path:extra\n",
- err: "invalid cgroups file",
+ name: "too-many-fields",
+ cgroups: "0:ctr:/path:extra\n",
+ mountinfo: debianMountinfo,
+ err: "invalid cgroups file",
},
{
name: "multiple-malformed",
cgroups: "0:ctr0:/path0\n" +
"1:ctr1:/path1\n" +
"2:\n",
- err: "invalid cgroups file",
+ mountinfo: debianMountinfo,
+ err: "invalid cgroups file",
+ },
+ {
+ name: "nested-cgroup",
+ cgroups: `9:memory:/docker/136
+2:cpu,cpuacct:/docker/136
+1:name=systemd:/docker/136
+0::/system.slice/containerd.service`,
+ mountinfo: dindMountinfo,
+ // we want relative path to /sys/fs/cgroup inside the nested container.
+ // Subcroup inside the container will be created at /sys/fs/cgroup/cpu
+ // This will be /sys/fs/cgroup/cpu/docker/136/CGROUP_NAME
+ // outside the container
+ want: map[string]string{
+ "memory": ".",
+ "cpu": ".",
+ "cpuacct": ".",
+ "systemd": ".",
+ },
+ },
+ {
+ name: "nested-cgroup-submount",
+ cgroups: "9:memory:/docker/136/test",
+ mountinfo: dindMountinfo,
+ want: map[string]string{
+ "memory": "test",
+ },
+ },
+ {
+ name: "invalid-mount-info",
+ cgroups: "0:memory:/path",
+ mountinfo: "41 35 0:36 / /sys/fs/cgroup/memory rw shared:16 - invalid",
+ want: map[string]string{
+ "memory": "/path",
+ },
+ },
+ {
+ name: "invalid-rel-path-in-proc-cgroup",
+ cgroups: "9:memory:./invalid",
+ mountinfo: dindMountinfo,
+ err: "can't make ./invalid relative to /docker/136",
},
} {
t.Run(tc.name, func(t *testing.T) {
r := strings.NewReader(tc.cgroups)
- got, err := loadPathsHelper(r)
+ mountinfo := strings.NewReader(tc.mountinfo)
+ got, err := loadPathsHelperWithMountinfo(r, mountinfo)
if len(tc.err) == 0 {
if err != nil {
t.Fatalf("Unexpected error: %v", err)