Merge pull request #2299 from kolyshkin/fs2-init-ctrl

cgroupv2: fix fs2 driver initialization
This commit is contained in:
Mrunal Patel 2020-04-20 21:27:42 -07:00 committed by GitHub
commit 46be7b612e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 420 additions and 277 deletions

View File

@ -36,6 +36,8 @@ matrix:
- sudo ssh default sudo podman run --privileged --cgroupns=private -v /lib/modules:/lib/modules:ro test make localunittest
# cgroupv2+systemd: test on vagrant host itself as we need systemd
- sudo ssh default -t 'cd /vagrant && sudo make localintegration RUNC_USE_SYSTEMD=yes'
# same setup but with fs2 driver instead of systemd
- sudo ssh default -t 'cd /vagrant && sudo make localintegration'
allow_failures:
- go: tip

View File

@ -13,7 +13,15 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
)
func isCpuSet(cgroup *configs.Cgroup) bool {
return cgroup.Resources.CpuWeight != 0 || cgroup.Resources.CpuMax != ""
}
func setCpu(dirPath string, cgroup *configs.Cgroup) error {
if !isCpuSet(cgroup) {
return nil
}
// NOTE: .CpuShares is not used here. Conversion is the caller's responsibility.
if cgroup.Resources.CpuWeight != 0 {
if err := fscommon.WriteFile(dirPath, "cpu.weight", strconv.FormatUint(cgroup.Resources.CpuWeight, 10)); err != nil {

View File

@ -7,7 +7,15 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
)
func isCpusetSet(cgroup *configs.Cgroup) bool {
return cgroup.Resources.CpusetCpus != "" || cgroup.Resources.CpusetMems != ""
}
func setCpuset(dirPath string, cgroup *configs.Cgroup) error {
if !isCpusetSet(cgroup) {
return nil
}
if cgroup.Resources.CpusetCpus != "" {
if err := fscommon.WriteFile(dirPath, "cpuset.cpus", cgroup.Resources.CpusetCpus); err != nil {
return err

View File

@ -0,0 +1,111 @@
package fs2
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/opencontainers/runc/libcontainer/configs"
)
// neededControllers returns the string to write to cgroup.subtree_control,
// containing the list of controllers to enable (for example, "+cpu +pids"),
// based on (1) controllers available and (2) resources that are being set.
//
// The resulting string does not include "pseudo" controllers such as
// "freezer" and "devices".
func neededControllers(cgroup *configs.Cgroup) ([]string, error) {
var list []string
if cgroup == nil {
return list, nil
}
// list of all available controllers
const file = UnifiedMountpoint + "/cgroup.controllers"
content, err := ioutil.ReadFile(file)
if err != nil {
return list, err
}
avail := make(map[string]struct{})
for _, ctr := range strings.Fields(string(content)) {
avail[ctr] = struct{}{}
}
// add the controller if available
add := func(controller string) {
if _, ok := avail[controller]; ok {
list = append(list, "+"+controller)
}
}
if isPidsSet(cgroup) {
add("pids")
}
if isMemorySet(cgroup) {
add("memory")
}
if isIoSet(cgroup) {
add("io")
}
if isCpuSet(cgroup) {
add("cpu")
}
if isCpusetSet(cgroup) {
add("cpuset")
}
if isHugeTlbSet(cgroup) {
add("hugetlb")
}
return list, nil
}
// CreateCgroupPath creates cgroupv2 path, enabling all the
// needed controllers in the process.
func CreateCgroupPath(path string, c *configs.Cgroup) (Err error) {
if !strings.HasPrefix(path, UnifiedMountpoint) {
return fmt.Errorf("invalid cgroup path %s", path)
}
ctrs, err := neededControllers(c)
if err != nil {
return err
}
allCtrs := strings.Join(ctrs, " ")
elements := strings.Split(path, "/")
elements = elements[3:]
current := "/sys/fs"
for i, e := range elements {
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)
}
}()
}
}
// enable needed controllers
if i < len(elements)-1 {
file := filepath.Join(current, "cgroup.subtree_control")
if err := ioutil.WriteFile(file, []byte(allCtrs), 0755); err != nil {
// XXX: we can enable _some_ controllers doing it one-by one
// instead of erroring out -- does it makes sense to do so?
return err
}
}
}
return nil
}

View File

@ -8,64 +8,11 @@ import (
"path/filepath"
"strings"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/pkg/errors"
)
// NewManager creates a manager for cgroup v2 unified hierarchy.
// dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope".
// If dirPath is empty, it is automatically set using config.
func NewManager(config *configs.Cgroup, dirPath string, rootless bool) (cgroups.Manager, error) {
if config == nil {
config = &configs.Cgroup{}
}
if dirPath != "" {
if filepath.Clean(dirPath) != dirPath || !filepath.IsAbs(dirPath) {
return nil, errors.Errorf("invalid dir path %q", dirPath)
}
} else {
var err error
dirPath, err = defaultDirPath(config)
if err != nil {
return nil, err
}
}
controllers, err := detectControllers(dirPath)
if err != nil && !rootless {
return nil, err
}
m := &manager{
config: config,
dirPath: dirPath,
controllers: controllers,
rootless: rootless,
}
return m, nil
}
func detectControllers(dirPath string) (map[string]struct{}, error) {
if err := os.MkdirAll(dirPath, 0755); err != nil {
return nil, err
}
controllersPath, err := securejoin.SecureJoin(dirPath, "cgroup.controllers")
if err != nil {
return nil, err
}
controllersData, err := ioutil.ReadFile(controllersPath)
if err != nil {
return nil, err
}
controllersFields := strings.Fields(string(controllersData))
controllers := make(map[string]struct{}, len(controllersFields))
for _, c := range controllersFields {
controllers[c] = struct{}{}
}
return controllers, nil
}
type manager struct {
config *configs.Cgroup
// dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope"
@ -76,7 +23,52 @@ type manager struct {
rootless bool
}
// NewManager creates a manager for cgroup v2 unified hierarchy.
// dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope".
// If dirPath is empty, it is automatically set using config.
func NewManager(config *configs.Cgroup, dirPath string, rootless bool) (cgroups.Manager, error) {
if config == nil {
config = &configs.Cgroup{}
}
if dirPath == "" {
var err error
dirPath, err = defaultDirPath(config)
if err != nil {
return nil, err
}
}
m := &manager{
config: config,
dirPath: dirPath,
rootless: rootless,
}
return m, nil
}
func (m *manager) getControllers() error {
if m.controllers != nil {
return nil
}
file := filepath.Join(m.dirPath, "cgroup.controllers")
data, err := ioutil.ReadFile(file)
if err != nil && !m.rootless {
return err
}
fields := strings.Fields(string(data))
m.controllers = make(map[string]struct{}, len(fields))
for _, c := range fields {
m.controllers[c] = struct{}{}
}
return nil
}
func (m *manager) Apply(pid int) error {
if err := CreateCgroupPath(m.dirPath, m.config); err != nil {
return err
}
if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil && !m.rootless {
return err
}
@ -97,6 +89,9 @@ func (m *manager) GetStats() (*cgroups.Stats, error) {
)
st := cgroups.NewStats()
if err := m.getControllers(); err != nil {
return st, err
}
// pids (since kernel 4.5)
if _, ok := m.controllers["pids"]; ok {
@ -147,11 +142,15 @@ func (m *manager) Freeze(state configs.FreezerState) error {
}
func (m *manager) Destroy() error {
return os.RemoveAll(m.dirPath)
if err := os.Remove(m.dirPath); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
// GetPaths is for compatibility purpose and should be removed in future
func (m *manager) GetPaths() map[string]string {
_ = m.getControllers()
paths := map[string]string{
// pseudo-controller for compatibility
"devices": m.dirPath,
@ -171,6 +170,9 @@ func (m *manager) Set(container *configs.Config) error {
if container == nil || container.Cgroups == nil {
return nil
}
if err := m.getControllers(); err != nil {
return err
}
var errs []error
// pids (since kernel 4.5)
if _, ok := m.controllers["pids"]; ok {

View File

@ -15,7 +15,14 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
)
func isHugeTlbSet(cgroup *configs.Cgroup) bool {
return len(cgroup.Resources.HugetlbLimit) > 0
}
func setHugeTlb(dirPath string, cgroup *configs.Cgroup) error {
if !isHugeTlbSet(cgroup) {
return nil
}
for _, hugetlb := range cgroup.Resources.HugetlbLimit {
if err := fscommon.WriteFile(dirPath, strings.Join([]string{"hugetlb", hugetlb.Pagesize, "max"}, "."), strconv.FormatUint(hugetlb.Limit, 10)); err != nil {
return err

View File

@ -14,7 +14,19 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
)
func isIoSet(cgroup *configs.Cgroup) bool {
return cgroup.Resources.BlkioWeight != 0 ||
len(cgroup.Resources.BlkioThrottleReadBpsDevice) > 0 ||
len(cgroup.Resources.BlkioThrottleWriteBpsDevice) > 0 ||
len(cgroup.Resources.BlkioThrottleReadIOPSDevice) > 0 ||
len(cgroup.Resources.BlkioThrottleWriteIOPSDevice) > 0
}
func setIo(dirPath string, cgroup *configs.Cgroup) error {
if !isIoSet(cgroup) {
return nil
}
if cgroup.Resources.BlkioWeight != 0 {
filename := "io.bfq.weight"
if err := fscommon.WriteFile(dirPath, filename,

View File

@ -32,7 +32,15 @@ func numToStr(value int64) (ret string) {
return ret
}
func isMemorySet(cgroup *configs.Cgroup) bool {
return cgroup.Resources.MemoryReservation != 0 ||
cgroup.Resources.Memory != 0 || cgroup.Resources.MemorySwap != 0
}
func setMemory(dirPath string, cgroup *configs.Cgroup) error {
if !isMemorySet(cgroup) {
return nil
}
swap, err := cgroups.ConvertMemorySwapToCgroupV2Value(cgroup.Resources.MemorySwap, cgroup.Resources.Memory)
if err != nil {
return err

View File

@ -14,7 +14,14 @@ import (
"golang.org/x/sys/unix"
)
func isPidsSet(cgroup *configs.Cgroup) bool {
return cgroup.Resources.PidsLimit != 0
}
func setPids(dirPath string, cgroup *configs.Cgroup) error {
if !isPidsSet(cgroup) {
return nil
}
if val := numToStr(cgroup.Resources.PidsLimit); val != "" {
if err := fscommon.WriteFile(dirPath, "pids.max", val); err != nil {
return err

View File

@ -0,0 +1,101 @@
package systemd
import (
"fmt"
"os"
"strings"
"sync"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
dbus "github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/configs"
)
var (
connOnce sync.Once
connDbus *systemdDbus.Conn
connErr error
)
// NOTE: This function comes from package github.com/coreos/go-systemd/util
// It was borrowed here to avoid a dependency on cgo.
//
// IsRunningSystemd checks whether the host was booted with systemd as its init
// system. This functions similarly to systemd's `sd_booted(3)`: internally, it
// checks whether /run/systemd/system/ exists and is a directory.
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
func IsRunningSystemd() bool {
fi, err := os.Lstat("/run/systemd/system")
if err != nil {
return false
}
return fi.IsDir()
}
// systemd represents slice hierarchy using `-`, so we need to follow suit when
// generating the path of slice. Essentially, test-a-b.slice becomes
// /test.slice/test-a.slice/test-a-b.slice.
func ExpandSlice(slice string) (string, error) {
suffix := ".slice"
// Name has to end with ".slice", but can't be just ".slice".
if len(slice) < len(suffix) || !strings.HasSuffix(slice, suffix) {
return "", fmt.Errorf("invalid slice name: %s", slice)
}
// Path-separators are not allowed.
if strings.Contains(slice, "/") {
return "", fmt.Errorf("invalid slice name: %s", slice)
}
var path, prefix string
sliceName := strings.TrimSuffix(slice, suffix)
// if input was -.slice, we should just return root now
if sliceName == "-" {
return "/", nil
}
for _, component := range strings.Split(sliceName, "-") {
// test--a.slice isn't permitted, nor is -test.slice.
if component == "" {
return "", fmt.Errorf("invalid slice name: %s", slice)
}
// Append the component to the path and to the prefix.
path += "/" + prefix + component + suffix
prefix += component + "-"
}
return path, nil
}
// getDbusConnection lazy initializes systemd dbus connection
// and returns it
func getDbusConnection() (*systemdDbus.Conn, error) {
connOnce.Do(func() {
connDbus, connErr = systemdDbus.New()
})
return connDbus, connErr
}
func newProp(name string, units interface{}) systemdDbus.Property {
return systemdDbus.Property{
Name: name,
Value: dbus.MakeVariant(units),
}
}
func getUnitName(c *configs.Cgroup) string {
// by default, we create a scope unless the user explicitly asks for a slice.
if !strings.HasSuffix(c.Name, ".slice") {
return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
}
return c.Name
}
// isUnitExists returns true if the error is that a systemd unit already exists.
func isUnitExists(err error) bool {
if err != nil {
if dbusError, ok := err.(dbus.Error); ok {
return strings.Contains(dbusError.Name, "org.freedesktop.systemd1.UnitExists")
}
}
return false
}

View File

@ -4,7 +4,6 @@ package systemd
import (
"errors"
"fmt"
"io/ioutil"
"math"
"os"
@ -14,7 +13,6 @@ import (
"time"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
dbus "github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/configs"
@ -65,63 +63,6 @@ var legacySubsystems = subsystemSet{
&fs.NameGroup{GroupName: "name=systemd"},
}
var (
connOnce sync.Once
connDbus *systemdDbus.Conn
connErr error
)
func newProp(name string, units interface{}) systemdDbus.Property {
return systemdDbus.Property{
Name: name,
Value: dbus.MakeVariant(units),
}
}
// NOTE: This function comes from package github.com/coreos/go-systemd/util
// It was borrowed here to avoid a dependency on cgo.
//
// IsRunningSystemd checks whether the host was booted with systemd as its init
// system. This functions similarly to systemd's `sd_booted(3)`: internally, it
// checks whether /run/systemd/system/ exists and is a directory.
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
func IsRunningSystemd() bool {
fi, err := os.Lstat("/run/systemd/system")
if err != nil {
return false
}
return fi.IsDir()
}
// getDbusConnection lazy initializes systemd dbus connection
// and returns it
func getDbusConnection() (*systemdDbus.Conn, error) {
connOnce.Do(func() {
connDbus, connErr = systemdDbus.New()
})
return connDbus, connErr
}
func NewSystemdCgroupsManager() (func(config *configs.Cgroup, paths map[string]string) cgroups.Manager, error) {
if !IsRunningSystemd() {
return nil, fmt.Errorf("systemd not running on this host, can't use systemd as a cgroups.Manager")
}
if cgroups.IsCgroup2UnifiedMode() {
return func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return &UnifiedManager{
Cgroups: config,
Paths: paths,
}
}, nil
}
return func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return &LegacyManager{
Cgroups: config,
Paths: paths,
}
}, nil
}
func (m *LegacyManager) Apply(pid int) error {
var (
c = m.Cgroups
@ -351,40 +292,6 @@ func joinCgroups(c *configs.Cgroup, pid int) error {
return nil
}
// systemd represents slice hierarchy using `-`, so we need to follow suit when
// generating the path of slice. Essentially, test-a-b.slice becomes
// /test.slice/test-a.slice/test-a-b.slice.
func ExpandSlice(slice string) (string, error) {
suffix := ".slice"
// Name has to end with ".slice", but can't be just ".slice".
if len(slice) < len(suffix) || !strings.HasSuffix(slice, suffix) {
return "", fmt.Errorf("invalid slice name: %s", slice)
}
// Path-separators are not allowed.
if strings.Contains(slice, "/") {
return "", fmt.Errorf("invalid slice name: %s", slice)
}
var path, prefix string
sliceName := strings.TrimSuffix(slice, suffix)
// if input was -.slice, we should just return root now
if sliceName == "-" {
return "/", nil
}
for _, component := range strings.Split(sliceName, "-") {
// test--a.slice isn't permitted, nor is -test.slice.
if component == "" {
return "", fmt.Errorf("invalid slice name: %s", slice)
}
// Append the component to the path and to the prefix.
path += "/" + prefix + component + suffix
prefix += component + "-"
}
return path, nil
}
func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
mountpoint, err := cgroups.FindCgroupMountpoint(c.Path, subsystem)
if err != nil {
@ -489,14 +396,6 @@ func (m *LegacyManager) Set(container *configs.Config) error {
return nil
}
func getUnitName(c *configs.Cgroup) string {
// by default, we create a scope unless the user explicitly asks for a slice.
if !strings.HasSuffix(c.Name, ".slice") {
return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
}
return c.Name
}
func setKernelMemory(c *configs.Cgroup) error {
path, err := getSubsystemPath(c, "memory")
if err != nil && !cgroups.IsNotFound(err) {
@ -517,17 +416,6 @@ func setKernelMemory(c *configs.Cgroup) error {
}
return fs.EnableKernelMemoryAccounting(path)
}
// isUnitExists returns true if the error is that a systemd unit already exists.
func isUnitExists(err error) bool {
if err != nil {
if dbusError, ok := err.(dbus.Error); ok {
return strings.Contains(dbusError.Name, "org.freedesktop.systemd1.UnitExists")
}
}
return false
}
func (m *LegacyManager) GetCgroups() (*configs.Cgroup, error) {
return m.Cgroups, nil
}

View File

@ -3,8 +3,6 @@
package systemd
import (
"bytes"
"io/ioutil"
"math"
"os"
"path/filepath"
@ -13,34 +11,39 @@ import (
"time"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
type UnifiedManager struct {
type unifiedManager struct {
mu sync.Mutex
Cgroups *configs.Cgroup
Paths map[string]string
cgroups *configs.Cgroup
// path is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope"
path string
rootless bool
}
func (m *UnifiedManager) Apply(pid int) error {
func NewUnifiedManager(config *configs.Cgroup, path string, rootless bool) *unifiedManager {
return &unifiedManager{
cgroups: config,
path: path,
rootless: rootless,
}
}
func (m *unifiedManager) Apply(pid int) error {
var (
c = m.Cgroups
c = m.cgroups
unitName = getUnitName(c)
slice = "system.slice"
properties []systemdDbus.Property
)
if c.Paths != nil {
paths := make(map[string]string)
for name, path := range c.Paths {
paths[name] = path
}
m.Paths = paths
return cgroups.EnterPid(m.Paths, pid)
return cgroups.WriteCgroupProc(m.path, pid)
}
if c.Parent != "" {
@ -144,27 +147,18 @@ func (m *UnifiedManager) Apply(pid int) error {
return err
}
path, err := getv2Path(m.Cgroups)
_, err = m.GetUnifiedPath()
if err != nil {
return err
}
if err := createCgroupsv2Path(path); err != nil {
if err := fs2.CreateCgroupPath(m.path, m.cgroups); err != nil {
return err
}
m.Paths = map[string]string{
"pids": path,
"memory": path,
"io": path,
"cpu": path,
"devices": path,
"cpuset": path,
"freezer": path,
}
return nil
}
func (m *UnifiedManager) Destroy() error {
if m.Cgroups.Paths != nil {
func (m *unifiedManager) Destroy() error {
if m.cgroups.Paths != nil {
return nil
}
m.mu.Lock()
@ -174,40 +168,40 @@ func (m *UnifiedManager) Destroy() error {
if err != nil {
return err
}
dbusConnection.StopUnit(getUnitName(m.Cgroups), "replace", nil)
if err := cgroups.RemovePaths(m.Paths); err != nil {
dbusConnection.StopUnit(getUnitName(m.cgroups), "replace", nil)
// XXX this is probably not needed, systemd should handle it
err = os.Remove(m.path)
if err != nil && !os.IsNotExist(err) {
return err
}
m.Paths = make(map[string]string)
return nil
}
func (m *UnifiedManager) GetPaths() map[string]string {
m.mu.Lock()
paths := m.Paths
m.mu.Unlock()
// this method is for v1 backward compatibility and will be removed
func (m *unifiedManager) GetPaths() map[string]string {
_, _ = m.GetUnifiedPath()
paths := map[string]string{
"pids": m.path,
"memory": m.path,
"io": m.path,
"cpu": m.path,
"devices": m.path,
"cpuset": m.path,
"freezer": m.path,
}
return paths
}
func (m *UnifiedManager) GetUnifiedPath() (string, error) {
unifiedPath := ""
func (m *unifiedManager) GetUnifiedPath() (string, error) {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.Paths {
if unifiedPath == "" {
unifiedPath = v
} else if v != unifiedPath {
return unifiedPath,
errors.Errorf("expected %q path to be unified path %q, got %q", k, unifiedPath, v)
}
if m.path != "" {
return m.path, nil
}
if unifiedPath == "" {
// FIXME: unified path could be detected even when no controller is available
return unifiedPath, errors.New("cannot detect unified path")
}
return unifiedPath, nil
}
func getv2Path(c *configs.Cgroup) (string, error) {
c := m.cgroups
slice := "system.slice"
if c.Parent != "" {
slice = c.Parent
@ -218,55 +212,25 @@ func getv2Path(c *configs.Cgroup) (string, error) {
return "", err
}
return filepath.Join(fs2.UnifiedMountpoint, slice, getUnitName(c)), nil
}
func createCgroupsv2Path(path string) (Err error) {
content, err := ioutil.ReadFile("/sys/fs/cgroup/cgroup.controllers")
path := filepath.Join(slice, getUnitName(c))
path, err = securejoin.SecureJoin(fs2.UnifiedMountpoint, path)
if err != nil {
return err
return "", err
}
m.path = path
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
return m.path, nil
}
func (m *UnifiedManager) fsManager() (cgroups.Manager, error) {
func (m *unifiedManager) fsManager() (cgroups.Manager, error) {
path, err := m.GetUnifiedPath()
if err != nil {
return nil, err
}
return fs2.NewManager(m.Cgroups, path, false)
return fs2.NewManager(m.cgroups, path, m.rootless)
}
func (m *UnifiedManager) Freeze(state configs.FreezerState) error {
func (m *unifiedManager) Freeze(state configs.FreezerState) error {
fsMgr, err := m.fsManager()
if err != nil {
return err
@ -274,7 +238,7 @@ func (m *UnifiedManager) Freeze(state configs.FreezerState) error {
return fsMgr.Freeze(state)
}
func (m *UnifiedManager) GetPids() ([]int, error) {
func (m *unifiedManager) GetPids() ([]int, error) {
path, err := m.GetUnifiedPath()
if err != nil {
return nil, err
@ -282,7 +246,7 @@ func (m *UnifiedManager) GetPids() ([]int, error) {
return cgroups.GetPids(path)
}
func (m *UnifiedManager) GetAllPids() ([]int, error) {
func (m *unifiedManager) GetAllPids() ([]int, error) {
path, err := m.GetUnifiedPath()
if err != nil {
return nil, err
@ -290,7 +254,7 @@ func (m *UnifiedManager) GetAllPids() ([]int, error) {
return cgroups.GetAllPids(path)
}
func (m *UnifiedManager) GetStats() (*cgroups.Stats, error) {
func (m *unifiedManager) GetStats() (*cgroups.Stats, error) {
fsMgr, err := m.fsManager()
if err != nil {
return nil, err
@ -298,7 +262,7 @@ func (m *UnifiedManager) GetStats() (*cgroups.Stats, error) {
return fsMgr.GetStats()
}
func (m *UnifiedManager) Set(container *configs.Config) error {
func (m *unifiedManager) Set(container *configs.Config) error {
fsMgr, err := m.fsManager()
if err != nil {
return err
@ -306,6 +270,6 @@ func (m *UnifiedManager) Set(container *configs.Config) error {
return fsMgr.Set(container)
}
func (m *UnifiedManager) GetCgroups() (*configs.Cgroup, error) {
return m.Cgroups, nil
func (m *unifiedManager) GetCgroups() (*configs.Cgroup, error) {
return m.cgroups, nil
}

View File

@ -50,28 +50,51 @@ func InitArgs(args ...string) func(*LinuxFactory) error {
}
}
// SystemdCgroups is an options func to configure a LinuxFactory to return
// containers that use systemd to create and manage cgroups.
func SystemdCgroups(l *LinuxFactory) error {
systemdCgroupsManager, err := systemd.NewSystemdCgroupsManager()
if err != nil {
return err
}
l.NewCgroupsManager = systemdCgroupsManager
return nil
}
func getUnifiedPath(paths map[string]string) string {
unifiedPath := ""
path := ""
for k, v := range paths {
if unifiedPath == "" {
unifiedPath = v
} else if v != unifiedPath {
panic(errors.Errorf("expected %q path to be unified path %q, got %q", k, unifiedPath, v))
if path == "" {
path = v
} else if v != path {
panic(errors.Errorf("expected %q path to be unified path %q, got %q", k, path, v))
}
}
// can be empty
return unifiedPath
if path != "" {
if filepath.Clean(path) != path || !filepath.IsAbs(path) {
panic(errors.Errorf("invalid dir path %q", path))
}
}
return path
}
func systemdCgroupV2(l *LinuxFactory, rootless bool) error {
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return systemd.NewUnifiedManager(config, getUnifiedPath(paths), rootless)
}
return nil
}
// SystemdCgroups is an options func to configure a LinuxFactory to return
// containers that use systemd to create and manage cgroups.
func SystemdCgroups(l *LinuxFactory) error {
if !systemd.IsRunningSystemd() {
return fmt.Errorf("systemd not running on this host, can't use systemd as cgroups manager")
}
if cgroups.IsCgroup2UnifiedMode() {
return systemdCgroupV2(l, false)
}
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return &systemd.LegacyManager{
Cgroups: config,
Paths: paths,
}
}
return nil
}
func cgroupfs2(l *LinuxFactory, rootless bool) error {

View File

@ -6,6 +6,8 @@ function setup() {
if [[ -n "${RUNC_USE_SYSTEMD}" ]] ; then
skip "CRIU test suite is skipped on systemd cgroup driver for now."
fi
# All checkpoint tests are currently failing on v2
requires cgroups_v1
teardown_busybox
setup_busybox