From f5c5aac958510539e69997a0e139b0265a337820 Mon Sep 17 00:00:00 2001 From: Craig Furman Date: Wed, 15 Mar 2017 10:10:30 +0000 Subject: [PATCH] Create containers when cgroups already mounted Runc needs to copy certain files from the top of the cgroup cpuset hierarchy into the container's cpuset cgroup directory. Currently, runc determines which directory is the top of the hierarchy by using the parent dir of the first entry in /proc/self/mountinfo of type cgroup. This creates problems when cgroup subsystems are mounted arbitrarily in different dirs on the host. Now, we use the most deeply nested mountpoint that contains the container's cpuset cgroup directory. Signed-off-by: Konstantinos Karampogias Signed-off-by: Will Martin --- libcontainer/cgroups/fs/cpuset.go | 3 ++- libcontainer/cgroups/utils.go | 15 +++++++++++++++ libcontainer/cgroups/utils_test.go | 31 ++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/libcontainer/cgroups/fs/cpuset.go b/libcontainer/cgroups/fs/cpuset.go index 918b9a30..20c9eafa 100644 --- a/libcontainer/cgroups/fs/cpuset.go +++ b/libcontainer/cgroups/fs/cpuset.go @@ -57,10 +57,11 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro if dir == "" { return nil } - root, err := getCgroupRoot() + mountInfo, err := ioutil.ReadFile("/proc/self/mountinfo") if err != nil { return err } + root := filepath.Dir(cgroups.GetClosestMountpointAncestor(dir, string(mountInfo))) // 'ensureParent' start with parent because we don't want to // explicitly inherit from parent, it could conflict with // 'cpuset.cpu_exclusive'. diff --git a/libcontainer/cgroups/utils.go b/libcontainer/cgroups/utils.go index 52fc87eb..96006c6b 100644 --- a/libcontainer/cgroups/utils.go +++ b/libcontainer/cgroups/utils.go @@ -66,6 +66,21 @@ func isSubsystemAvailable(subsystem string) bool { return avail } +func GetClosestMountpointAncestor(dir, mountinfo string) string { + deepestMountPoint := "" + for _, mountInfoEntry := range strings.Split(mountinfo, "\n") { + mountInfoParts := strings.Fields(mountInfoEntry) + if len(mountInfoParts) < 5 { + continue + } + mountPoint := mountInfoParts[4] + if strings.HasPrefix(mountPoint, deepestMountPoint) && strings.HasPrefix(dir, mountPoint) { + deepestMountPoint = mountPoint + } + } + return deepestMountPoint +} + func FindCgroupMountpointDir() (string, error) { f, err := os.Open("/proc/self/mountinfo") if err != nil { diff --git a/libcontainer/cgroups/utils_test.go b/libcontainer/cgroups/utils_test.go index d3aa8ef4..4996aced 100644 --- a/libcontainer/cgroups/utils_test.go +++ b/libcontainer/cgroups/utils_test.go @@ -300,3 +300,34 @@ func TestIgnoreCgroup2Mount(t *testing.T) { } } } + +const fakeMountInfo = ` 18 24 0:17 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw +100 99 1:31 / /foo/bar rw,relatime - fake fake rw,fake +100 99 1:31 / /foo/bar/baz2 rw,relatime - fake fake rw,fake +100 99 1:31 / /foo/bar/baz rw,relatime - fake fake rw,fake +100 99 1:31 / /foo/bar/bazza rw,relatime - fake fake rw,fake +100 99 1:31 / /foo/bar/baz3 rw,relatime - fake fake rw,fake +100 99 1:31 / /foo rw,relatime - fake fake rw,fake +100 99 1:31 / /unrelated rw,relatime - fake fake rw,fake +100 99 1:31 / / rw,relatime - fake fake rw,fake +` + +func TestGetClosestMountpointAncestor(t *testing.T) { + testCases := []struct { + input string + mountinfos string + output string + }{ + {input: "/foo/bar/baz/a/b/c", mountinfos: fakeMountInfo, output: "/foo/bar/baz"}, + {input: "/foo/bar/baz", mountinfos: fakeMountInfo, output: "/foo/bar/baz"}, + {input: "/foo/bar/bazza", mountinfos: fakeMountInfo, output: "/foo/bar/bazza"}, + {input: "/a/b/c/d", mountinfos: fakeMountInfo, output: "/"}, + } + + for _, c := range testCases { + mountpoint := GetClosestMountpointAncestor(c.input, c.mountinfos) + if mountpoint != c.output { + t.Errorf("expected %s, got %s", c.output, mountpoint) + } + } +}