From f68f894a655ab348481776909d10342819d5099b Mon Sep 17 00:00:00 2001 From: Ace-Tang Date: Tue, 22 May 2018 11:50:23 +0800 Subject: [PATCH] cgroups: repeat copy data from parent directory copyParentCPUSet copies the cpuset.cpus and cpuset.mems from the parent, but if parent directory's file content is also zero, it will make pid write into cpuset cgroup error. So recursive copy data from parent directory until cpuset cgroup root path. Modify variable cgroupsDirPath to cgroupsRootPath(cli/oci.go), make it more reasonable and not conflict with cgroupsDirPath(cli/create.go). Fixes #330 Signed-off-by: Ace-Tang --- cli/create.go | 37 +++++++++++++++++++++++++++++++++++-- cli/create_test.go | 6 +++--- cli/oci.go | 16 ++++++++-------- cli/oci_test.go | 20 ++++++++++---------- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/cli/create.go b/cli/create.go index d7e5abec8b..f6a9f406b1 100644 --- a/cli/create.go +++ b/cli/create.go @@ -287,8 +287,13 @@ func createCgroupsFiles(containerID string, cgroupsDirPath string, cgroupsPathLi } if strings.Contains(cgroupsPath, "cpu") && cgroupsDirPath != "" { - parent := strings.TrimSuffix(cgroupsPath, cgroupsDirPath) - copyParentCPUSet(cgroupsPath, parent) + root, err := getCgroupsRootPath(procMountInfo) + if err != nil { + return err + } + if err := repeatCopy(cgroupsPath, root); err != nil { + return fmt.Errorf("Fail to copy cpuset from parent: %s", err) + } } tasksFilePath := filepath.Join(cgroupsPath, cgroupsTasksFile) @@ -348,6 +353,34 @@ func createPIDFile(pidFilePath string, pid int) error { return nil } +// repeatCopy copies data from parent directory until +// cpuset cgroup root to avoid parent has zero data. +func repeatCopy(path, root string) error { + if !fileExists(filepath.Join(path, "cpuset.cpus")) || !fileExists(filepath.Join(path, "cpuset.mems")) { + return nil + } + + parent := filepath.Dir(path) + if filepath.Clean(parent) == root { + return nil + } + + // cgroup path get error, stop the loop + if parent == path { + return fmt.Errorf("cpu cgroup gets wrong path %s", path) + } + + if err := repeatCopy(parent, root); err != nil { + return err + } + + if err := os.MkdirAll(path, cgroupsDirMode); err != nil { + return err + } + + return copyParentCPUSet(path, parent) +} + // copyParentCPUSet copies the cpuset.cpus and cpuset.mems from the parent // directory to the current directory if the file's contents are 0 func copyParentCPUSet(current, parent string) error { diff --git a/cli/create_test.go b/cli/create_test.go index 9143735467..6dd73a2b43 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -631,14 +631,14 @@ func TestCreateCreateCgroupsFilesFail(t *testing.T) { } // Override - cgroupsDirPath = filepath.Join(tmpdir, "cgroups") - err = os.MkdirAll(cgroupsDirPath, testDirMode) + cgroupsRootPath = filepath.Join(tmpdir, "cgroups") + err = os.MkdirAll(cgroupsRootPath, testDirMode) assert.NoError(err) // Set a relative path spec.Linux.CgroupsPath = "./a/relative/path" - dir := filepath.Join(cgroupsDirPath, "memory") + dir := filepath.Join(cgroupsRootPath, "memory") // Stop directory from being created err = os.MkdirAll(dir, os.FileMode(0000)) diff --git a/cli/oci.go b/cli/oci.go index 059e212d4a..ef9469b3f1 100644 --- a/cli/oci.go +++ b/cli/oci.go @@ -38,7 +38,7 @@ const ( var errNeedLinuxResource = errors.New("Linux resource cannot be empty") -var cgroupsDirPath string +var cgroupsRootPath string var procMountInfo = "/proc/self/mountinfo" @@ -189,14 +189,14 @@ func processCgroupsPathForResource(ociSpec oci.CompatOCISpec, resource string, i } var err error - cgroupsDirPath, err = getCgroupsDirPath(procMountInfo) + cgroupsRootPath, err = getCgroupsRootPath(procMountInfo) if err != nil { - return "", fmt.Errorf("get CgroupsDirPath error: %s", err) + return "", fmt.Errorf("get CgroupsRootPath error: %s", err) } // Relative cgroups path provided. if filepath.IsAbs(ociSpec.Linux.CgroupsPath) == false { - return filepath.Join(cgroupsDirPath, resource, ociSpec.Linux.CgroupsPath), nil + return filepath.Join(cgroupsRootPath, resource, ociSpec.Linux.CgroupsPath), nil } // Absolute cgroups path provided. @@ -214,7 +214,7 @@ func processCgroupsPathForResource(ociSpec oci.CompatOCISpec, resource string, i // According to the OCI spec, an absolute path should be // interpreted as relative to the system cgroup mount point // when there is no cgroup mount point. - return filepath.Join(cgroupsDirPath, resource, ociSpec.Linux.CgroupsPath), nil + return filepath.Join(cgroupsRootPath, resource, ociSpec.Linux.CgroupsPath), nil } if cgroupMount.Destination == "" { @@ -306,9 +306,9 @@ func noNeedForOutput(detach bool, tty bool) bool { return true } -func getCgroupsDirPath(mountInfoFile string) (string, error) { - if cgroupsDirPath != "" { - return cgroupsDirPath, nil +func getCgroupsRootPath(mountInfoFile string) (string, error) { + if cgroupsRootPath != "" { + return cgroupsRootPath, nil } f, err := os.Open(mountInfoFile) diff --git a/cli/oci_test.go b/cli/oci_test.go index f856df8946..cbb5a60e83 100644 --- a/cli/oci_test.go +++ b/cli/oci_test.go @@ -213,7 +213,7 @@ func TestProcessCgroupsPathEmptyResources(t *testing.T) { func TestProcessCgroupsPathRelativePathSuccessful(t *testing.T) { relativeCgroupsPath := "relative/cgroups/path" - cgroupsDirPath = "/foo/runtime/base" + cgroupsRootPath = "/foo/runtime/base" ociSpec := oci.CompatOCISpec{} @@ -224,7 +224,7 @@ func TestProcessCgroupsPathRelativePathSuccessful(t *testing.T) { for _, d := range cgroupTestData { ociSpec.Linux.Resources = d.linuxSpec - p := filepath.Join(cgroupsDirPath, d.resource, relativeCgroupsPath) + p := filepath.Join(cgroupsRootPath, d.resource, relativeCgroupsPath) testProcessCgroupsPath(t, ociSpec, []string{p}) } @@ -232,7 +232,7 @@ func TestProcessCgroupsPathRelativePathSuccessful(t *testing.T) { func TestProcessCgroupsPathAbsoluteNoCgroupMountSuccessful(t *testing.T) { absoluteCgroupsPath := "/absolute/cgroups/path" - cgroupsDirPath = "/foo/runtime/base" + cgroupsRootPath = "/foo/runtime/base" ociSpec := oci.CompatOCISpec{} @@ -243,7 +243,7 @@ func TestProcessCgroupsPathAbsoluteNoCgroupMountSuccessful(t *testing.T) { for _, d := range cgroupTestData { ociSpec.Linux.Resources = d.linuxSpec - p := filepath.Join(cgroupsDirPath, d.resource, absoluteCgroupsPath) + p := filepath.Join(cgroupsRootPath, d.resource, absoluteCgroupsPath) testProcessCgroupsPath(t, ociSpec, []string{p}) } @@ -422,12 +422,12 @@ func TestIsCgroupMounted(t *testing.T) { assert.False(isCgroupMounted(os.TempDir()), "%s is not a cgroup", os.TempDir()) - cgroupsDirPath = "" - cgroupRootPath, err := getCgroupsDirPath(procMountInfo) + cgroupsRootPath = "" + path, err := getCgroupsRootPath(procMountInfo) if err != nil { assert.NoError(err) } - memoryCgroupPath := filepath.Join(cgroupRootPath, "memory") + memoryCgroupPath := filepath.Join(path, "memory") if _, err := os.Stat(memoryCgroupPath); os.IsNotExist(err) { t.Skipf("memory cgroup does not exist: %s", memoryCgroupPath) } @@ -494,15 +494,15 @@ func TestGetCgroupsDirPath(t *testing.T) { file := filepath.Join(dir, "mountinfo") //file does not exist, should error here - _, err = getCgroupsDirPath(file) + _, err = getCgroupsRootPath(file) assert.Error(err) for _, d := range data { err := ioutil.WriteFile(file, []byte(d.contents), testFileMode) assert.NoError(err) - cgroupsDirPath = "" - path, err := getCgroupsDirPath(file) + cgroupsRootPath = "" + path, err := getCgroupsRootPath(file) if d.expectError { assert.Error(err, fmt.Sprintf("got %q, test data: %+v", path, d)) } else {