mirror of https://github.com/k3s-io/k3s
215 lines
5.4 KiB
Go
215 lines
5.4 KiB
Go
|
// +build linux
|
||
|
|
||
|
package fs2
|
||
|
|
||
|
import (
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"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"
|
||
|
dirPath string
|
||
|
// controllers is content of "cgroup.controllers" file.
|
||
|
// excludes pseudo-controllers ("devices" and "freezer").
|
||
|
controllers map[string]struct{}
|
||
|
rootless bool
|
||
|
}
|
||
|
|
||
|
func (m *manager) Apply(pid int) error {
|
||
|
if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil && !m.rootless {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *manager) GetPids() ([]int, error) {
|
||
|
return cgroups.GetPids(m.dirPath)
|
||
|
}
|
||
|
|
||
|
func (m *manager) GetAllPids() ([]int, error) {
|
||
|
return cgroups.GetAllPids(m.dirPath)
|
||
|
}
|
||
|
|
||
|
func (m *manager) GetStats() (*cgroups.Stats, error) {
|
||
|
var (
|
||
|
st cgroups.Stats
|
||
|
errs []error
|
||
|
)
|
||
|
// pids (since kernel 4.5)
|
||
|
if _, ok := m.controllers["pids"]; ok {
|
||
|
if err := statPids(m.dirPath, &st); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
} else {
|
||
|
if err := statPidsWithoutController(m.dirPath, &st); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// memory (since kenrel 4.5)
|
||
|
if _, ok := m.controllers["memory"]; ok {
|
||
|
if err := statMemory(m.dirPath, &st); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// io (since kernel 4.5)
|
||
|
if _, ok := m.controllers["io"]; ok {
|
||
|
if err := statIo(m.dirPath, &st); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// cpu (since kernel 4.15)
|
||
|
if _, ok := m.controllers["cpu"]; ok {
|
||
|
if err := statCpu(m.dirPath, &st); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
if len(errs) > 0 && !m.rootless {
|
||
|
return &st, errors.Errorf("error while statting cgroup v2: %+v", errs)
|
||
|
}
|
||
|
return &st, nil
|
||
|
}
|
||
|
|
||
|
func (m *manager) Freeze(state configs.FreezerState) error {
|
||
|
if err := setFreezer(m.dirPath, state); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
m.config.Resources.Freezer = state
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *manager) Destroy() error {
|
||
|
return os.RemoveAll(m.dirPath)
|
||
|
}
|
||
|
|
||
|
// GetPaths is for compatibility purpose and should be removed in future
|
||
|
func (m *manager) GetPaths() map[string]string {
|
||
|
paths := map[string]string{
|
||
|
// pseudo-controller for compatibility
|
||
|
"devices": m.dirPath,
|
||
|
"freezer": m.dirPath,
|
||
|
}
|
||
|
for c := range m.controllers {
|
||
|
paths[c] = m.dirPath
|
||
|
}
|
||
|
return paths
|
||
|
}
|
||
|
|
||
|
func (m *manager) GetUnifiedPath() (string, error) {
|
||
|
return m.dirPath, nil
|
||
|
}
|
||
|
|
||
|
func (m *manager) Set(container *configs.Config) error {
|
||
|
if container == nil || container.Cgroups == nil {
|
||
|
return nil
|
||
|
}
|
||
|
var errs []error
|
||
|
// pids (since kernel 4.5)
|
||
|
if _, ok := m.controllers["pids"]; ok {
|
||
|
if err := setPids(m.dirPath, container.Cgroups); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// memory (since kernel 4.5)
|
||
|
if _, ok := m.controllers["memory"]; ok {
|
||
|
if err := setMemory(m.dirPath, container.Cgroups); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// io (since kernel 4.5)
|
||
|
if _, ok := m.controllers["io"]; ok {
|
||
|
if err := setIo(m.dirPath, container.Cgroups); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// cpu (since kernel 4.15)
|
||
|
if _, ok := m.controllers["cpu"]; ok {
|
||
|
if err := setCpu(m.dirPath, container.Cgroups); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// devices (since kernel 4.15, pseudo-controller)
|
||
|
if err := setDevices(m.dirPath, container.Cgroups); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
// cpuset (since kernel 5.0)
|
||
|
if _, ok := m.controllers["cpuset"]; ok {
|
||
|
if err := setCpuset(m.dirPath, container.Cgroups); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
}
|
||
|
// freezer (since kernel 5.2, pseudo-controller)
|
||
|
if err := setFreezer(m.dirPath, container.Cgroups.Freezer); err != nil {
|
||
|
errs = append(errs, err)
|
||
|
}
|
||
|
if len(errs) > 0 && !m.rootless {
|
||
|
return errors.Errorf("error while setting cgroup v2: %+v", errs)
|
||
|
}
|
||
|
m.config = container.Cgroups
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *manager) GetCgroups() (*configs.Cgroup, error) {
|
||
|
return m.config, nil
|
||
|
}
|