Skip to content

Commit d2d16c5

Browse files
committed
support cgroup v1 mounted with noprefix
Android mounts the v1 cpuset cgroup with the noprefix option. As a result, runc first attempts to access cpuset files using the prefix format (e.g., cpuset.cpus). If this fails, it falls back to accessing them without the prefix (e.g., cpus). Once a successful access method is determined, it is cached and used for all subsequent operations. 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. Signed-off-by: Tomasz Duda <[email protected]>
1 parent e075206 commit d2d16c5

File tree

1 file changed

+52
-17
lines changed

1 file changed

+52
-17
lines changed

libcontainer/cgroups/fs/cpuset.go

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"path/filepath"
77
"strconv"
88
"strings"
9+
"sync"
910

1011
"golang.org/x/sys/unix"
1112

@@ -14,6 +15,40 @@ import (
1415
"github.com/opencontainers/runc/libcontainer/configs"
1516
)
1617

18+
var (
19+
cpusetLock sync.Mutex
20+
cpusetPrefix = "cpuset."
21+
cpusetFastPath bool
22+
)
23+
24+
func cpusetFile(path string, name string) string {
25+
cpusetLock.Lock()
26+
defer cpusetLock.Unlock()
27+
28+
// Only the v1 cpuset cgroup is allowed to mount with noprefix.
29+
// See kernel source: https://github.com/torvalds/linux/blob/2e1b3cc9d7f790145a80cb705b168f05dab65df2/kernel/cgroup/cgroup-v1.c#L1070
30+
// Cpuset cannot be mounted with and without prefix simultaneously.
31+
// Commonly used in Android environments.
32+
33+
if cpusetFastPath {
34+
return cpusetPrefix + name
35+
}
36+
37+
err := unix.Access(filepath.Join(path, cpusetPrefix+name), unix.F_OK)
38+
if err == nil {
39+
// Use the fast path only if we can access one type of mount for cpuset already
40+
cpusetFastPath = true
41+
} else {
42+
err = unix.Access(filepath.Join(path, name), unix.F_OK)
43+
if err == nil {
44+
cpusetPrefix = ""
45+
cpusetFastPath = true
46+
}
47+
}
48+
49+
return cpusetPrefix + name
50+
}
51+
1752
type CpusetGroup struct{}
1853

1954
func (s *CpusetGroup) Name() string {
@@ -26,12 +61,12 @@ func (s *CpusetGroup) Apply(path string, r *configs.Resources, pid int) error {
2661

2762
func (s *CpusetGroup) Set(path string, r *configs.Resources) error {
2863
if r.CpusetCpus != "" {
29-
if err := cgroups.WriteFile(path, "cpuset.cpus", r.CpusetCpus); err != nil {
64+
if err := cgroups.WriteFile(path, cpusetFile(path, "cpus"), r.CpusetCpus); err != nil {
3065
return err
3166
}
3267
}
3368
if r.CpusetMems != "" {
34-
if err := cgroups.WriteFile(path, "cpuset.mems", r.CpusetMems); err != nil {
69+
if err := cgroups.WriteFile(path, cpusetFile(path, "mems"), r.CpusetMems); err != nil {
3570
return err
3671
}
3772
}
@@ -83,57 +118,57 @@ func getCpusetStat(path string, file string) ([]uint16, error) {
83118
func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
84119
var err error
85120

86-
stats.CPUSetStats.CPUs, err = getCpusetStat(path, "cpuset.cpus")
121+
stats.CPUSetStats.CPUs, err = getCpusetStat(path, cpusetFile(path, "cpus"))
87122
if err != nil && !errors.Is(err, os.ErrNotExist) {
88123
return err
89124
}
90125

91-
stats.CPUSetStats.CPUExclusive, err = fscommon.GetCgroupParamUint(path, "cpuset.cpu_exclusive")
126+
stats.CPUSetStats.CPUExclusive, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "cpu_exclusive"))
92127
if err != nil && !errors.Is(err, os.ErrNotExist) {
93128
return err
94129
}
95130

96-
stats.CPUSetStats.Mems, err = getCpusetStat(path, "cpuset.mems")
131+
stats.CPUSetStats.Mems, err = getCpusetStat(path, cpusetFile(path, "mems"))
97132
if err != nil && !errors.Is(err, os.ErrNotExist) {
98133
return err
99134
}
100135

101-
stats.CPUSetStats.MemHardwall, err = fscommon.GetCgroupParamUint(path, "cpuset.mem_hardwall")
136+
stats.CPUSetStats.MemHardwall, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "mem_hardwall"))
102137
if err != nil && !errors.Is(err, os.ErrNotExist) {
103138
return err
104139
}
105140

106-
stats.CPUSetStats.MemExclusive, err = fscommon.GetCgroupParamUint(path, "cpuset.mem_exclusive")
141+
stats.CPUSetStats.MemExclusive, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "mem_exclusive"))
107142
if err != nil && !errors.Is(err, os.ErrNotExist) {
108143
return err
109144
}
110145

111-
stats.CPUSetStats.MemoryMigrate, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_migrate")
146+
stats.CPUSetStats.MemoryMigrate, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_migrate"))
112147
if err != nil && !errors.Is(err, os.ErrNotExist) {
113148
return err
114149
}
115150

116-
stats.CPUSetStats.MemorySpreadPage, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_spread_page")
151+
stats.CPUSetStats.MemorySpreadPage, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_spread_page"))
117152
if err != nil && !errors.Is(err, os.ErrNotExist) {
118153
return err
119154
}
120155

121-
stats.CPUSetStats.MemorySpreadSlab, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_spread_slab")
156+
stats.CPUSetStats.MemorySpreadSlab, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_spread_slab"))
122157
if err != nil && !errors.Is(err, os.ErrNotExist) {
123158
return err
124159
}
125160

126-
stats.CPUSetStats.MemoryPressure, err = fscommon.GetCgroupParamUint(path, "cpuset.memory_pressure")
161+
stats.CPUSetStats.MemoryPressure, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_pressure"))
127162
if err != nil && !errors.Is(err, os.ErrNotExist) {
128163
return err
129164
}
130165

131-
stats.CPUSetStats.SchedLoadBalance, err = fscommon.GetCgroupParamUint(path, "cpuset.sched_load_balance")
166+
stats.CPUSetStats.SchedLoadBalance, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "sched_load_balance"))
132167
if err != nil && !errors.Is(err, os.ErrNotExist) {
133168
return err
134169
}
135170

136-
stats.CPUSetStats.SchedRelaxDomainLevel, err = fscommon.GetCgroupParamInt(path, "cpuset.sched_relax_domain_level")
171+
stats.CPUSetStats.SchedRelaxDomainLevel, err = fscommon.GetCgroupParamInt(path, cpusetFile(path, "sched_relax_domain_level"))
137172
if err != nil && !errors.Is(err, os.ErrNotExist) {
138173
return err
139174
}
@@ -172,10 +207,10 @@ func (s *CpusetGroup) ApplyDir(dir string, r *configs.Resources, pid int) error
172207
}
173208

174209
func getCpusetSubsystemSettings(parent string) (cpus, mems string, err error) {
175-
if cpus, err = cgroups.ReadFile(parent, "cpuset.cpus"); err != nil {
210+
if cpus, err = cgroups.ReadFile(parent, cpusetFile(parent, "cpus")); err != nil {
176211
return
177212
}
178-
if mems, err = cgroups.ReadFile(parent, "cpuset.mems"); err != nil {
213+
if mems, err = cgroups.ReadFile(parent, cpusetFile(parent, "mems")); err != nil {
179214
return
180215
}
181216
return cpus, mems, nil
@@ -221,12 +256,12 @@ func cpusetCopyIfNeeded(current, parent string) error {
221256
}
222257

223258
if isEmptyCpuset(currentCpus) {
224-
if err := cgroups.WriteFile(current, "cpuset.cpus", parentCpus); err != nil {
259+
if err := cgroups.WriteFile(current, cpusetFile(current, "cpus"), parentCpus); err != nil {
225260
return err
226261
}
227262
}
228263
if isEmptyCpuset(currentMems) {
229-
if err := cgroups.WriteFile(current, "cpuset.mems", parentMems); err != nil {
264+
if err := cgroups.WriteFile(current, cpusetFile(current, "mems"), parentMems); err != nil {
230265
return err
231266
}
232267
}

0 commit comments

Comments
 (0)