Blob Blame History Raw
From bbe03e61a375416180432fbd9d00d23a7c2a4714 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Mon, 8 Jul 2019 10:16:13 +0200
Subject: [PATCH] cgroups: support creating cgroupsv2 paths

drop the limitation of not supporting creating new cgroups v2 paths.
Every controller enabled /sys/fs/cgroup will be propagated down to the
created path.  This won't work for rootless cgroupsv2, but it is not
an issue for now, as this code is used only by CRI-O.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
---
 pkg/cgroups/blkio.go   |  2 +-
 pkg/cgroups/cgroups.go | 52 +++++++++++++++++++++++++++++++++++++++++-
 pkg/cgroups/cpu.go     |  2 +-
 pkg/cgroups/cpuset.go  | 19 +++++++++------
 pkg/cgroups/memory.go  |  2 +-
 pkg/cgroups/pids.go    |  3 ---
 6 files changed, 66 insertions(+), 14 deletions(-)

diff --git a/pkg/cgroups/blkio.go b/pkg/cgroups/blkio.go
index ca9107d977..9c2a811d9e 100644
--- a/pkg/cgroups/blkio.go
+++ b/pkg/cgroups/blkio.go
@@ -30,7 +30,7 @@ func (c *blkioHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error
 // Create the cgroup
 func (c *blkioHandler) Create(ctr *CgroupControl) (bool, error) {
 	if ctr.cgroup2 {
-		return false, fmt.Errorf("io create not implemented for cgroup v2")
+		return false, nil
 	}
 	return ctr.createCgroupDirectory(Blkio)
 }
diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go
index d6c19212bc..1dad45d7f7 100644
--- a/pkg/cgroups/cgroups.go
+++ b/pkg/cgroups/cgroups.go
@@ -149,6 +149,51 @@ func (c *CgroupControl) getCgroupv1Path(name string) string {
 	return filepath.Join(cgroupRoot, name, c.path)
 }
 
+// createCgroupv2Path creates the cgroupv2 path and enables all the available controllers
+func createCgroupv2Path(path string) (Err error) {
+	content, err := ioutil.ReadFile("/sys/fs/cgroup/cgroup.controllers")
+	if err != nil {
+		return errors.Wrapf(err, "read /sys/fs/cgroup/cgroup.controllers")
+	}
+	if !filepath.HasPrefix(path, "/sys/fs/cgroup") {
+		return fmt.Errorf("invalid cgroup path %s", path)
+	}
+
+	res := ""
+	for i, c := range strings.Split(strings.TrimSpace(string(content)), " ") {
+		if i == 0 {
+			res = fmt.Sprintf("+%s", c)
+		} else {
+			res = res + fmt.Sprintf(" +%s", c)
+		}
+	}
+	resByte := []byte(res)
+
+	current := "/sys/fs"
+	elements := strings.Split(path, "/")
+	for i, e := range elements[3:] {
+		current = filepath.Join(current, e)
+		if i > 0 {
+			if err := os.Mkdir(current, 0755); err != nil {
+				if !os.IsExist(err) {
+					return errors.Wrapf(err, "mkdir %s", path)
+				}
+			} else {
+				// If the directory was created, be sure it is not left around on errors.
+				defer func() {
+					if Err != nil {
+						os.Remove(current)
+					}
+				}()
+			}
+		}
+		if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), resByte, 0755); err != nil {
+			return errors.Wrapf(err, "write %s", filepath.Join(current, "cgroup.subtree_control"))
+		}
+	}
+	return nil
+}
+
 // initialize initializes the specified hierarchy
 func (c *CgroupControl) initialize() (err error) {
 	createdSoFar := map[string]controllerHandler{}
@@ -161,6 +206,11 @@ func (c *CgroupControl) initialize() (err error) {
 			}
 		}
 	}()
+	if c.cgroup2 {
+		if err := createCgroupv2Path(filepath.Join(cgroupRoot, c.path)); err != nil {
+			return errors.Wrapf(err, "error creating cgroup path %s", c.path)
+		}
+	}
 	for name, handler := range handlers {
 		created, err := handler.Create(c)
 		if err != nil {
@@ -341,7 +391,7 @@ func (c *CgroupControl) AddPid(pid int) error {
 	pidString := []byte(fmt.Sprintf("%d\n", pid))
 
 	if c.cgroup2 {
-		p := filepath.Join(cgroupRoot, c.path, "tasks")
+		p := filepath.Join(cgroupRoot, c.path, "cgroup.procs")
 		if err := ioutil.WriteFile(p, pidString, 0644); err != nil {
 			return errors.Wrapf(err, "write %s", p)
 		}
diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go
index 8640d490e6..c9325946b4 100644
--- a/pkg/cgroups/cpu.go
+++ b/pkg/cgroups/cpu.go
@@ -61,7 +61,7 @@ func (c *cpuHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
 // Create the cgroup
 func (c *cpuHandler) Create(ctr *CgroupControl) (bool, error) {
 	if ctr.cgroup2 {
-		return false, fmt.Errorf("cpu create not implemented for cgroup v2")
+		return false, nil
 	}
 	return ctr.createCgroupDirectory(CPU)
 }
diff --git a/pkg/cgroups/cpuset.go b/pkg/cgroups/cpuset.go
index 9aef493c9f..25d2f7f769 100644
--- a/pkg/cgroups/cpuset.go
+++ b/pkg/cgroups/cpuset.go
@@ -14,19 +14,23 @@ import (
 type cpusetHandler struct {
 }
 
-func cpusetCopyFileFromParent(dir, file string) ([]byte, error) {
+func cpusetCopyFileFromParent(dir, file string, cgroupv2 bool) ([]byte, error) {
 	if dir == cgroupRoot {
 		return nil, fmt.Errorf("could not find parent to initialize cpuset %s", file)
 	}
 	path := filepath.Join(dir, file)
-	data, err := ioutil.ReadFile(path)
+	parentPath := path
+	if cgroupv2 {
+		parentPath = fmt.Sprintf("%s.effective", parentPath)
+	}
+	data, err := ioutil.ReadFile(parentPath)
 	if err != nil {
 		return nil, errors.Wrapf(err, "open %s", path)
 	}
 	if len(strings.Trim(string(data), "\n")) != 0 {
 		return data, nil
 	}
-	data, err = cpusetCopyFileFromParent(filepath.Dir(dir), file)
+	data, err = cpusetCopyFileFromParent(filepath.Dir(dir), file, cgroupv2)
 	if err != nil {
 		return nil, err
 	}
@@ -36,9 +40,9 @@ func cpusetCopyFileFromParent(dir, file string) ([]byte, error) {
 	return data, nil
 }
 
-func cpusetCopyFromParent(path string) error {
+func cpusetCopyFromParent(path string, cgroupv2 bool) error {
 	for _, file := range []string{"cpuset.cpus", "cpuset.mems"} {
-		if _, err := cpusetCopyFileFromParent(path, file); err != nil {
+		if _, err := cpusetCopyFileFromParent(path, file, cgroupv2); err != nil {
 			return err
 		}
 	}
@@ -60,14 +64,15 @@ func (c *cpusetHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) erro
 // Create the cgroup
 func (c *cpusetHandler) Create(ctr *CgroupControl) (bool, error) {
 	if ctr.cgroup2 {
-		return false, fmt.Errorf("cpuset create not implemented for cgroup v2")
+		path := filepath.Join(cgroupRoot, ctr.path)
+		return true, cpusetCopyFromParent(path, true)
 	}
 
 	created, err := ctr.createCgroupDirectory(CPUset)
 	if !created || err != nil {
 		return created, err
 	}
-	return true, cpusetCopyFromParent(ctr.getCgroupv1Path(CPUset))
+	return true, cpusetCopyFromParent(ctr.getCgroupv1Path(CPUset), false)
 }
 
 // Destroy the cgroup
diff --git a/pkg/cgroups/memory.go b/pkg/cgroups/memory.go
index 0505eac409..80e88d17c4 100644
--- a/pkg/cgroups/memory.go
+++ b/pkg/cgroups/memory.go
@@ -26,7 +26,7 @@ func (c *memHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
 // Create the cgroup
 func (c *memHandler) Create(ctr *CgroupControl) (bool, error) {
 	if ctr.cgroup2 {
-		return false, fmt.Errorf("memory create not implemented for cgroup v2")
+		return false, nil
 	}
 	return ctr.createCgroupDirectory(Memory)
 }
diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go
index c90dc1c020..ffbde100dd 100644
--- a/pkg/cgroups/pids.go
+++ b/pkg/cgroups/pids.go
@@ -35,9 +35,6 @@ func (c *pidHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
 
 // Create the cgroup
 func (c *pidHandler) Create(ctr *CgroupControl) (bool, error) {
-	if ctr.cgroup2 {
-		return false, fmt.Errorf("pid create not implemented for cgroup v2")
-	}
 	return ctr.createCgroupDirectory(Pids)
 }