mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 14:13:58 +08:00
4b4bc995ad
1. Instead of enabling all available controllers, figure out which
ones are required, and only enable those.
2. Amend all setFoo() functions to call isFooSet(). While this might
seem unnecessary, it might actually help to uncover a bug.
Imagine someone:
- adds a cgroup.Resources.CpuFoo setting;
- modifies setCpu() to apply the new setting;
- but forgets to amend isCpuSet() accordingly <-- BUG
In this case, a test case modifying CpuFoo will help
to uncover the BUG. This is the reason why it's added.
This patch *could be* amended by enabling controllers on a best-effort
basis, i.e. :
- do not return an error early if we can't enable some controllers;
- if we fail to enable all controllers at once (usually because one
of them can't be enabled), try enabling them one by one.
Currently this is not implemented, and it's not clear whether this
would be a good way to go or not.
[v2: add/use is${Controller}Set() functions]
[v3: document neededControllers()]
[v4: drop "best-effort" part]
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
112 lines
2.5 KiB
Go
112 lines
2.5 KiB
Go
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
|
|
}
|