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 5db37344..7c995efe 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) + } + } +}