cgroupv2: fix fs2 cgroup init

fs2 cgroup driver was not working because it did not enable controllers
while creating cgroup directory; instead it was merely doing MkdirAll()
and gathered the list of available controllers in NewManager().

Also, cgroup should be created in Apply(), not while creating a new
manager instance.

To fix:

1. Move the createCgroupsv2Path function from systemd driver to fs2 driver,
   renaming it to CreateCgroupPath. Use in Apply() from both fs2 and
   systemd drivers.

2. Delay available controllers map initialization to until it is needed.

With this patch:
 - NewManager() only performs minimal initialization (initializin
   m.dirPath, if not provided);
 - Apply() properly creates cgroup path, enabling the controllers;
 - m.controllers is initialized lazily on demand.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin 2020-04-05 21:02:06 -07:00
parent 60eaed2ed6
commit 813cb3eb94
3 changed files with 77 additions and 60 deletions

View File

@ -0,0 +1,49 @@
package fs2
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// CreateCgroupPath creates cgroupv2 path, enabling all the
// available controllers in the process.
func CreateCgroupPath(path string) (Err error) {
const file = UnifiedMountpoint + "/cgroup.controllers"
content, err := ioutil.ReadFile(file)
if err != nil {
return err
}
ctrs := bytes.Fields(content)
res := append([]byte("+"), bytes.Join(ctrs, []byte(" +"))...)
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 err
}
} else {
// If the directory was created, be sure it is not left around on errors.
current := current
defer func() {
if Err != nil {
os.Remove(current)
}
}()
}
}
if i < len(elements[3:])-1 {
if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), res, 0755); err != nil {
return err
}
}
}
return nil
}

View File

@ -37,38 +37,38 @@ func NewManager(config *configs.Cgroup, dirPath string, rootless bool) (cgroups.
return nil, err return nil, err
} }
} }
controllers, err := detectControllers(dirPath)
if err != nil && !rootless {
return nil, err
}
m := &manager{ m := &manager{
config: config, config: config,
dirPath: dirPath, dirPath: dirPath,
controllers: controllers,
rootless: rootless, rootless: rootless,
} }
return m, nil return m, nil
} }
func detectControllers(dirPath string) (map[string]struct{}, error) { func (m *manager) getControllers() error {
if err := os.MkdirAll(dirPath, 0755); err != nil { if m.controllers != nil {
return nil, err return nil
} }
controllersPath := filepath.Join(dirPath, "cgroup.controllers")
controllersData, err := ioutil.ReadFile(controllersPath) file := filepath.Join(m.dirPath, "cgroup.controllers")
if err != nil { data, err := ioutil.ReadFile(file)
return nil, err if err != nil && !m.rootless {
return err
} }
controllersFields := strings.Fields(string(controllersData)) fields := strings.Fields(string(data))
controllers := make(map[string]struct{}, len(controllersFields)) m.controllers = make(map[string]struct{}, len(fields))
for _, c := range controllersFields { for _, c := range fields {
controllers[c] = struct{}{} m.controllers[c] = struct{}{}
} }
return controllers, nil
return nil
} }
func (m *manager) Apply(pid int) error { func (m *manager) Apply(pid int) error {
if err := CreateCgroupPath(m.dirPath); err != nil {
return err
}
if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil && !m.rootless { if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil && !m.rootless {
return err return err
} }
@ -89,6 +89,9 @@ func (m *manager) GetStats() (*cgroups.Stats, error) {
) )
st := cgroups.NewStats() st := cgroups.NewStats()
if err := m.getControllers(); err != nil {
return st, err
}
// pids (since kernel 4.5) // pids (since kernel 4.5)
if _, ok := m.controllers["pids"]; ok { if _, ok := m.controllers["pids"]; ok {
@ -144,6 +147,7 @@ func (m *manager) Destroy() error {
// GetPaths is for compatibility purpose and should be removed in future // GetPaths is for compatibility purpose and should be removed in future
func (m *manager) GetPaths() map[string]string { func (m *manager) GetPaths() map[string]string {
_ = m.getControllers()
paths := map[string]string{ paths := map[string]string{
// pseudo-controller for compatibility // pseudo-controller for compatibility
"devices": m.dirPath, "devices": m.dirPath,
@ -163,6 +167,9 @@ func (m *manager) Set(container *configs.Config) error {
if container == nil || container.Cgroups == nil { if container == nil || container.Cgroups == nil {
return nil return nil
} }
if err := m.getControllers(); err != nil {
return err
}
var errs []error var errs []error
// pids (since kernel 4.5) // pids (since kernel 4.5)
if _, ok := m.controllers["pids"]; ok { if _, ok := m.controllers["pids"]; ok {

View File

@ -3,8 +3,6 @@
package systemd package systemd
import ( import (
"bytes"
"io/ioutil"
"math" "math"
"os" "os"
"path/filepath" "path/filepath"
@ -153,7 +151,7 @@ func (m *unifiedManager) Apply(pid int) error {
if err != nil { if err != nil {
return err return err
} }
if err := createCgroupsv2Path(m.path); err != nil { if err := fs2.CreateCgroupPath(m.path); err != nil {
return err return err
} }
return nil return nil
@ -224,43 +222,6 @@ func (m *unifiedManager) GetUnifiedPath() (string, error) {
return m.path, nil return m.path, nil
} }
func createCgroupsv2Path(path string) (Err error) {
content, err := ioutil.ReadFile("/sys/fs/cgroup/cgroup.controllers")
if err != nil {
return err
}
ctrs := bytes.Fields(content)
res := append([]byte("+"), bytes.Join(ctrs, []byte(" +"))...)
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 err
}
} else {
// If the directory was created, be sure it is not left around on errors.
current := current
defer func() {
if Err != nil {
os.Remove(current)
}
}()
}
}
if i < len(elements[3:])-1 {
if err := ioutil.WriteFile(filepath.Join(current, "cgroup.subtree_control"), res, 0755); err != nil {
return err
}
}
}
return nil
}
func (m *unifiedManager) fsManager() (cgroups.Manager, error) { func (m *unifiedManager) fsManager() (cgroups.Manager, error) {
path, err := m.GetUnifiedPath() path, err := m.GetUnifiedPath()
if err != nil { if err != nil {