diff --git a/libcontainer/cgroups/fs/cpuset.go b/libcontainer/cgroups/fs/cpuset.go index fe01ba98408..2c0886e0c29 100644 --- a/libcontainer/cgroups/fs/cpuset.go +++ b/libcontainer/cgroups/fs/cpuset.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "golang.org/x/sys/unix" @@ -14,6 +15,40 @@ import ( "github.com/opencontainers/runc/libcontainer/configs" ) +var ( + cpusetLock sync.Mutex + cpusetPrefix string = "cpuset." + cpusetFastPath bool +) + +func cpusetFile(path string, name string) string { + cpusetLock.Lock() + defer cpusetLock.Unlock() + + // Only the v1 cpuset cgroup is allowed to mount with noprefix. + // See kernel source: https://github.com/torvalds/linux/blob/2e1b3cc9d7f790145a80cb705b168f05dab65df2/kernel/cgroup/cgroup-v1.c#L1070 + // Cpuset cannot be mounted with and without prefix simultaneously. + // Commonly used in Android environments. + + if cpusetFastPath { + return cpusetPrefix + name + } + + err := unix.Access(filepath.Join(path, cpusetPrefix+name), unix.F_OK) + if err == nil { + // Use the fast path only if we can access one type of mount for cpuset already + cpusetFastPath = true + } else { + err = unix.Access(filepath.Join(path, name), unix.F_OK) + if err == nil { + cpusetPrefix = "" + cpusetFastPath = true + } + } + + return cpusetPrefix + name +} + type CpusetGroup struct{} func (s *CpusetGroup) Name() string { @@ -26,12 +61,12 @@ func (s *CpusetGroup) Apply(path string, r *configs.Resources, pid int) error { func (s *CpusetGroup) Set(path string, r *configs.Resources) error { if r.CpusetCpus != "" { - if err := cgroups.WriteFile(path, "cpuset.cpus", r.CpusetCpus); err != nil { + if err := cgroups.WriteFile(path, cpusetFile(path, "cpus"), r.CpusetCpus); err != nil { return err } } if r.CpusetMems != "" { - if err := cgroups.WriteFile(path, "cpuset.mems", r.CpusetMems); err != nil { + if err := cgroups.WriteFile(path, cpusetFile(path, "mems"), r.CpusetMems); err != nil { return err } } @@ -83,57 +118,57 @@ func getCpusetStat(path string, file string) ([]uint16, error) { func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error { var err error - stats.CPUSetStats.CPUs, err = getCpusetStat(path, "cpuset.cpus") + stats.CPUSetStats.CPUs, err = getCpusetStat(path, cpusetFile(path, "cpus")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.CPUExclusive, err = fscommon.GetCgroupParamUint(path, "cpuset.cpu_exclusive") + stats.CPUSetStats.CPUExclusive, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "cpu_exclusive")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.Mems, err = getCpusetStat(path, "cpuset.mems") + stats.CPUSetStats.Mems, err = getCpusetStat(path, cpusetFile(path, "mems")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.MemHardwall, err = fscommon.GetCgroupParamUint(path, "cpuset.mem_hardwall") + stats.CPUSetStats.MemHardwall, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "mem_hardwall")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.MemExclusive, err = fscommon.GetCgroupParamUint(path, "cpuset.mem_exclusive") + stats.CPUSetStats.MemExclusive, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "mem_exclusive")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.MemoryMigrate, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_migrate") + stats.CPUSetStats.MemoryMigrate, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_migrate")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.MemorySpreadPage, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_spread_page") + stats.CPUSetStats.MemorySpreadPage, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_spread_page")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.MemorySpreadSlab, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_spread_slab") + stats.CPUSetStats.MemorySpreadSlab, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_spread_slab")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.MemoryPressure, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_pressure") + stats.CPUSetStats.MemoryPressure, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_pressure")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.SchedLoadBalance, err = fscommon.GetCgroupParamUint(path, "cpuset.sched_load_balance") + stats.CPUSetStats.SchedLoadBalance, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "sched_load_balance")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } - stats.CPUSetStats.SchedRelaxDomainLevel, err = fscommon.GetCgroupParamInt(path, "cpuset.sched_relax_domain_level") + stats.CPUSetStats.SchedRelaxDomainLevel, err = fscommon.GetCgroupParamInt(path, cpusetFile(path, "sched_relax_domain_level")) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } @@ -172,10 +207,10 @@ func (s *CpusetGroup) ApplyDir(dir string, r *configs.Resources, pid int) error } func getCpusetSubsystemSettings(parent string) (cpus, mems string, err error) { - if cpus, err = cgroups.ReadFile(parent, "cpuset.cpus"); err != nil { + if cpus, err = cgroups.ReadFile(parent, cpusetFile(parent, "cpus")); err != nil { return } - if mems, err = cgroups.ReadFile(parent, "cpuset.mems"); err != nil { + if mems, err = cgroups.ReadFile(parent, cpusetFile(parent, "mems")); err != nil { return } return cpus, mems, nil @@ -221,12 +256,12 @@ func cpusetCopyIfNeeded(current, parent string) error { } if isEmptyCpuset(currentCpus) { - if err := cgroups.WriteFile(current, "cpuset.cpus", parentCpus); err != nil { + if err := cgroups.WriteFile(current, cpusetFile(current, "cpus"), parentCpus); err != nil { return err } } if isEmptyCpuset(currentMems) { - if err := cgroups.WriteFile(current, "cpuset.mems", parentMems); err != nil { + if err := cgroups.WriteFile(current, cpusetFile(current, "mems"), parentMems); err != nil { return err } }