mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
f160352682
This function is not very efficient, does not really belong to cgroup package, and is only used once (from fs/cpuset.go). Prepare to remove it by replacing with the implementation based on the parser from github.com/moby/sys/mountinfo parser. This commit is here to make sure the proposed replacement passes the unit test. Funny, but the unit test need to be slightly modified since it supplies the wrong mountinfo (space as the first character, empty line at the end). Validated by $ go test -v -run Ance === RUN TestGetClosestMountpointAncestor --- PASS: TestGetClosestMountpointAncestor (0.00s) PASS ok github.com/opencontainers/runc/libcontainer/cgroups 0.002s Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
165 lines
4.5 KiB
Go
165 lines
4.5 KiB
Go
// +build linux
|
|
|
|
package fs
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
|
|
)
|
|
|
|
type CpusetGroup struct {
|
|
}
|
|
|
|
func (s *CpusetGroup) Name() string {
|
|
return "cpuset"
|
|
}
|
|
|
|
func (s *CpusetGroup) Apply(d *cgroupData) error {
|
|
dir, err := d.path("cpuset")
|
|
if err != nil && !cgroups.IsNotFound(err) {
|
|
return err
|
|
}
|
|
return s.ApplyDir(dir, d.config, d.pid)
|
|
}
|
|
|
|
func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
|
|
if cgroup.Resources.CpusetCpus != "" {
|
|
if err := fscommon.WriteFile(path, "cpuset.cpus", cgroup.Resources.CpusetCpus); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if cgroup.Resources.CpusetMems != "" {
|
|
if err := fscommon.WriteFile(path, "cpuset.mems", cgroup.Resources.CpusetMems); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *CpusetGroup) Remove(d *cgroupData) error {
|
|
return removePath(d.path("cpuset"))
|
|
}
|
|
|
|
func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error {
|
|
// This might happen if we have no cpuset cgroup mounted.
|
|
// Just do nothing and don't fail.
|
|
if dir == "" {
|
|
return nil
|
|
}
|
|
mountInfo, err := ioutil.ReadFile("/proc/self/mountinfo")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
root, err := cgroups.GetClosestMountpointAncestor(dir, string(mountInfo))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
root = filepath.Dir(root)
|
|
// 'ensureParent' start with parent because we don't want to
|
|
// explicitly inherit from parent, it could conflict with
|
|
// 'cpuset.cpu_exclusive'.
|
|
if err := s.ensureParent(filepath.Dir(dir), root); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return err
|
|
}
|
|
// We didn't inherit cpuset configs from parent, but we have
|
|
// to ensure cpuset configs are set before moving task into the
|
|
// cgroup.
|
|
// The logic is, if user specified cpuset configs, use these
|
|
// specified configs, otherwise, inherit from parent. This makes
|
|
// cpuset configs work correctly with 'cpuset.cpu_exclusive', and
|
|
// keep backward compatibility.
|
|
if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
|
|
return err
|
|
}
|
|
|
|
// because we are not using d.join we need to place the pid into the procs file
|
|
// unlike the other subsystems
|
|
return cgroups.WriteCgroupProc(dir, pid)
|
|
}
|
|
|
|
func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
|
|
if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
|
|
return
|
|
}
|
|
if mems, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.mems")); err != nil {
|
|
return
|
|
}
|
|
return cpus, mems, nil
|
|
}
|
|
|
|
// ensureParent makes sure that the parent directory of current is created
|
|
// and populated with the proper cpus and mems files copied from
|
|
// it's parent.
|
|
func (s *CpusetGroup) ensureParent(current, root string) error {
|
|
parent := filepath.Dir(current)
|
|
if libcontainerUtils.CleanPath(parent) == root {
|
|
return nil
|
|
}
|
|
// Avoid infinite recursion.
|
|
if parent == current {
|
|
return fmt.Errorf("cpuset: cgroup parent path outside cgroup root")
|
|
}
|
|
if err := s.ensureParent(parent, root); err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(current, 0755); err != nil {
|
|
return err
|
|
}
|
|
return s.copyIfNeeded(current, parent)
|
|
}
|
|
|
|
// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
|
|
// directory to the current directory if the file's contents are 0
|
|
func (s *CpusetGroup) copyIfNeeded(current, parent string) error {
|
|
var (
|
|
err error
|
|
currentCpus, currentMems []byte
|
|
parentCpus, parentMems []byte
|
|
)
|
|
|
|
if currentCpus, currentMems, err = s.getSubsystemSettings(current); err != nil {
|
|
return err
|
|
}
|
|
if parentCpus, parentMems, err = s.getSubsystemSettings(parent); err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.isEmpty(currentCpus) {
|
|
if err := fscommon.WriteFile(current, "cpuset.cpus", string(parentCpus)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if s.isEmpty(currentMems) {
|
|
if err := fscommon.WriteFile(current, "cpuset.mems", string(parentMems)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *CpusetGroup) isEmpty(b []byte) bool {
|
|
return len(bytes.Trim(b, "\n")) == 0
|
|
}
|
|
|
|
func (s *CpusetGroup) ensureCpusAndMems(path string, cgroup *configs.Cgroup) error {
|
|
if err := s.Set(path, cgroup); err != nil {
|
|
return err
|
|
}
|
|
return s.copyIfNeeded(path, filepath.Dir(path))
|
|
}
|