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