5a249b
From bbe03e61a375416180432fbd9d00d23a7c2a4714 Mon Sep 17 00:00:00 2001
5a249b
From: Giuseppe Scrivano <gscrivan@redhat.com>
5a249b
Date: Mon, 8 Jul 2019 10:16:13 +0200
5a249b
Subject: [PATCH] cgroups: support creating cgroupsv2 paths
5a249b
5a249b
drop the limitation of not supporting creating new cgroups v2 paths.
5a249b
Every controller enabled /sys/fs/cgroup will be propagated down to the
5a249b
created path.  This won't work for rootless cgroupsv2, but it is not
5a249b
an issue for now, as this code is used only by CRI-O.
5a249b
5a249b
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
5a249b
---
5a249b
 pkg/cgroups/blkio.go   |  2 +-
5a249b
 pkg/cgroups/cgroups.go | 52 +++++++++++++++++++++++++++++++++++++++++-
5a249b
 pkg/cgroups/cpu.go     |  2 +-
5a249b
 pkg/cgroups/cpuset.go  | 19 +++++++++------
5a249b
 pkg/cgroups/memory.go  |  2 +-
5a249b
 pkg/cgroups/pids.go    |  3 ---
5a249b
 6 files changed, 66 insertions(+), 14 deletions(-)
5a249b
5a249b
diff --git a/pkg/cgroups/blkio.go b/pkg/cgroups/blkio.go
5a249b
index ca9107d977..9c2a811d9e 100644
5a249b
--- a/pkg/cgroups/blkio.go
5a249b
+++ b/pkg/cgroups/blkio.go
5a249b
@@ -30,7 +30,7 @@ func (c *blkioHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error
5a249b
 // Create the cgroup
5a249b
 func (c *blkioHandler) Create(ctr *CgroupControl) (bool, error) {
5a249b
 	if ctr.cgroup2 {
5a249b
-		return false, fmt.Errorf("io create not implemented for cgroup v2")
5a249b
+		return false, nil
5a249b
 	}
5a249b
 	return ctr.createCgroupDirectory(Blkio)
5a249b
 }
5a249b
diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go
5a249b
index d6c19212bc..1dad45d7f7 100644
5a249b
--- a/pkg/cgroups/cgroups.go
5a249b
+++ b/pkg/cgroups/cgroups.go
5a249b
@@ -149,6 +149,51 @@ func (c *CgroupControl) getCgroupv1Path(name string) string {
5a249b
 	return filepath.Join(cgroupRoot, name, c.path)
5a249b
 }
5a249b
 
5a249b
+// createCgroupv2Path creates the cgroupv2 path and enables all the available controllers
5a249b
+func createCgroupv2Path(path string) (Err error) {
5a249b
+	content, err := ioutil.ReadFile("/sys/fs/cgroup/cgroup.controllers")
5a249b
+	if err != nil {
5a249b
+		return errors.Wrapf(err, "read /sys/fs/cgroup/cgroup.controllers")
5a249b
+	}
5a249b
+	if !filepath.HasPrefix(path, "/sys/fs/cgroup") {
5a249b
+		return fmt.Errorf("invalid cgroup path %s", path)
5a249b
+	}
5a249b
+
5a249b
+	res := ""
5a249b
+	for i, c := range strings.Split(strings.TrimSpace(string(content)), " ") {
5a249b
+		if i == 0 {
5a249b
+			res = fmt.Sprintf("+%s", c)
5a249b
+		} else {
5a249b
+			res = res + fmt.Sprintf(" +%s", c)
5a249b
+		}
5a249b
+	}
5a249b
+	resByte := []byte(res)
5a249b
+
5a249b
+	current := "/sys/fs"
5a249b
+	elements := strings.Split(path, "/")
5a249b
+	for i, e := range elements[3:] {
5a249b
+		current = filepath.Join(current, e)
5a249b
+		if i > 0 {
5a249b
+			if err := os.Mkdir(current, 0755); err != nil {
5a249b
+				if !os.IsExist(err) {
5a249b
+					return errors.Wrapf(err, "mkdir %s", path)
5a249b
+				}
5a249b
+			} else {
5a249b
+				// If the directory was created, be sure it is not left around on errors.
5a249b
+				defer func() {
5a249b
+					if Err != nil {
5a249b
+						os.Remove(current)
5a249b
+					}
5a249b
+				}()
5a249b
+			}
5a249b
+		}
5a249b
+		if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), resByte, 0755); err != nil {
5a249b
+			return errors.Wrapf(err, "write %s", filepath.Join(current, "cgroup.subtree_control"))
5a249b
+		}
5a249b
+	}
5a249b
+	return nil
5a249b
+}
5a249b
+
5a249b
 // initialize initializes the specified hierarchy
5a249b
 func (c *CgroupControl) initialize() (err error) {
5a249b
 	createdSoFar := map[string]controllerHandler{}
5a249b
@@ -161,6 +206,11 @@ func (c *CgroupControl) initialize() (err error) {
5a249b
 			}
5a249b
 		}
5a249b
 	}()
5a249b
+	if c.cgroup2 {
5a249b
+		if err := createCgroupv2Path(filepath.Join(cgroupRoot, c.path)); err != nil {
5a249b
+			return errors.Wrapf(err, "error creating cgroup path %s", c.path)
5a249b
+		}
5a249b
+	}
5a249b
 	for name, handler := range handlers {
5a249b
 		created, err := handler.Create(c)
5a249b
 		if err != nil {
5a249b
@@ -341,7 +391,7 @@ func (c *CgroupControl) AddPid(pid int) error {
5a249b
 	pidString := []byte(fmt.Sprintf("%d\n", pid))
5a249b
 
5a249b
 	if c.cgroup2 {
5a249b
-		p := filepath.Join(cgroupRoot, c.path, "tasks")
5a249b
+		p := filepath.Join(cgroupRoot, c.path, "cgroup.procs")
5a249b
 		if err := ioutil.WriteFile(p, pidString, 0644); err != nil {
5a249b
 			return errors.Wrapf(err, "write %s", p)
5a249b
 		}
5a249b
diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go
5a249b
index 8640d490e6..c9325946b4 100644
5a249b
--- a/pkg/cgroups/cpu.go
5a249b
+++ b/pkg/cgroups/cpu.go
5a249b
@@ -61,7 +61,7 @@ func (c *cpuHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
5a249b
 // Create the cgroup
5a249b
 func (c *cpuHandler) Create(ctr *CgroupControl) (bool, error) {
5a249b
 	if ctr.cgroup2 {
5a249b
-		return false, fmt.Errorf("cpu create not implemented for cgroup v2")
5a249b
+		return false, nil
5a249b
 	}
5a249b
 	return ctr.createCgroupDirectory(CPU)
5a249b
 }
5a249b
diff --git a/pkg/cgroups/cpuset.go b/pkg/cgroups/cpuset.go
5a249b
index 9aef493c9f..25d2f7f769 100644
5a249b
--- a/pkg/cgroups/cpuset.go
5a249b
+++ b/pkg/cgroups/cpuset.go
5a249b
@@ -14,19 +14,23 @@ import (
5a249b
 type cpusetHandler struct {
5a249b
 }
5a249b
 
5a249b
-func cpusetCopyFileFromParent(dir, file string) ([]byte, error) {
5a249b
+func cpusetCopyFileFromParent(dir, file string, cgroupv2 bool) ([]byte, error) {
5a249b
 	if dir == cgroupRoot {
5a249b
 		return nil, fmt.Errorf("could not find parent to initialize cpuset %s", file)
5a249b
 	}
5a249b
 	path := filepath.Join(dir, file)
5a249b
-	data, err := ioutil.ReadFile(path)
5a249b
+	parentPath := path
5a249b
+	if cgroupv2 {
5a249b
+		parentPath = fmt.Sprintf("%s.effective", parentPath)
5a249b
+	}
5a249b
+	data, err := ioutil.ReadFile(parentPath)
5a249b
 	if err != nil {
5a249b
 		return nil, errors.Wrapf(err, "open %s", path)
5a249b
 	}
5a249b
 	if len(strings.Trim(string(data), "\n")) != 0 {
5a249b
 		return data, nil
5a249b
 	}
5a249b
-	data, err = cpusetCopyFileFromParent(filepath.Dir(dir), file)
5a249b
+	data, err = cpusetCopyFileFromParent(filepath.Dir(dir), file, cgroupv2)
5a249b
 	if err != nil {
5a249b
 		return nil, err
5a249b
 	}
5a249b
@@ -36,9 +40,9 @@ func cpusetCopyFileFromParent(dir, file string) ([]byte, error) {
5a249b
 	return data, nil
5a249b
 }
5a249b
 
5a249b
-func cpusetCopyFromParent(path string) error {
5a249b
+func cpusetCopyFromParent(path string, cgroupv2 bool) error {
5a249b
 	for _, file := range []string{"cpuset.cpus", "cpuset.mems"} {
5a249b
-		if _, err := cpusetCopyFileFromParent(path, file); err != nil {
5a249b
+		if _, err := cpusetCopyFileFromParent(path, file, cgroupv2); err != nil {
5a249b
 			return err
5a249b
 		}
5a249b
 	}
5a249b
@@ -60,14 +64,15 @@ func (c *cpusetHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) erro
5a249b
 // Create the cgroup
5a249b
 func (c *cpusetHandler) Create(ctr *CgroupControl) (bool, error) {
5a249b
 	if ctr.cgroup2 {
5a249b
-		return false, fmt.Errorf("cpuset create not implemented for cgroup v2")
5a249b
+		path := filepath.Join(cgroupRoot, ctr.path)
5a249b
+		return true, cpusetCopyFromParent(path, true)
5a249b
 	}
5a249b
 
5a249b
 	created, err := ctr.createCgroupDirectory(CPUset)
5a249b
 	if !created || err != nil {
5a249b
 		return created, err
5a249b
 	}
5a249b
-	return true, cpusetCopyFromParent(ctr.getCgroupv1Path(CPUset))
5a249b
+	return true, cpusetCopyFromParent(ctr.getCgroupv1Path(CPUset), false)
5a249b
 }
5a249b
 
5a249b
 // Destroy the cgroup
5a249b
diff --git a/pkg/cgroups/memory.go b/pkg/cgroups/memory.go
5a249b
index 0505eac409..80e88d17c4 100644
5a249b
--- a/pkg/cgroups/memory.go
5a249b
+++ b/pkg/cgroups/memory.go
5a249b
@@ -26,7 +26,7 @@ func (c *memHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
5a249b
 // Create the cgroup
5a249b
 func (c *memHandler) Create(ctr *CgroupControl) (bool, error) {
5a249b
 	if ctr.cgroup2 {
5a249b
-		return false, fmt.Errorf("memory create not implemented for cgroup v2")
5a249b
+		return false, nil
5a249b
 	}
5a249b
 	return ctr.createCgroupDirectory(Memory)
5a249b
 }
5a249b
diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go
5a249b
index c90dc1c020..ffbde100dd 100644
5a249b
--- a/pkg/cgroups/pids.go
5a249b
+++ b/pkg/cgroups/pids.go
5a249b
@@ -35,9 +35,6 @@ func (c *pidHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
5a249b
 
5a249b
 // Create the cgroup
5a249b
 func (c *pidHandler) Create(ctr *CgroupControl) (bool, error) {
5a249b
-	if ctr.cgroup2 {
5a249b
-		return false, fmt.Errorf("pid create not implemented for cgroup v2")
5a249b
-	}
5a249b
 	return ctr.createCgroupDirectory(Pids)
5a249b
 }
5a249b