From 7db9930fab78cb8388516b25dc98244a0e68b439 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 25 Jul 2025 17:19:00 -0700 Subject: [PATCH] [1.4] libct: move exec sub-cgroup handling down the line Remove cgroupPaths field from struct setnsProcess, because: - we can get base cgroup paths from p.manager.GetPaths(); - we can get sub-cgroup paths from p.process.SubCgroupPaths. But mostly because we are going to need separate cgroup paths when adopting cgroups.AddPid. Signed-off-by: Kir Kolyshkin (cherry picked from commit 5730a141f1702dc60682620d4815e1cd194dda06) Signed-off-by: Aleksa Sarai --- libcontainer/container_linux.go | 31 ------------------------- libcontainer/process_linux.go | 40 ++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 7f0c51f31..554ca9b6e 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -7,7 +7,6 @@ import ( "io" "os" "os/exec" - "path" "path/filepath" "reflect" "strconv" @@ -655,40 +654,10 @@ func (c *Container) newSetnsProcess(p *Process, cmd *exec.Cmd, comm *processComm bootstrapData: data, container: c, }, - cgroupPaths: state.CgroupPaths, rootlessCgroups: c.config.RootlessCgroups, intelRdtPath: state.IntelRdtPath, initProcessPid: state.InitProcessPid, } - if len(p.SubCgroupPaths) > 0 { - if add, ok := p.SubCgroupPaths[""]; ok { - // cgroup v1: using the same path for all controllers. - // cgroup v2: the only possible way. - for k := range proc.cgroupPaths { - subPath := path.Join(proc.cgroupPaths[k], add) - if !strings.HasPrefix(subPath, proc.cgroupPaths[k]) { - return nil, fmt.Errorf("%s is not a sub cgroup path", add) - } - proc.cgroupPaths[k] = subPath - } - // cgroup v2: do not try to join init process's cgroup - // as a fallback (see (*setnsProcess).start). - proc.initProcessPid = 0 - } else { - // Per-controller paths. - for ctrl, add := range p.SubCgroupPaths { - if val, ok := proc.cgroupPaths[ctrl]; ok { - subPath := path.Join(val, add) - if !strings.HasPrefix(subPath, val) { - return nil, fmt.Errorf("%s is not a sub cgroup path", add) - } - proc.cgroupPaths[ctrl] = subPath - } else { - return nil, fmt.Errorf("unknown controller %s in SubCgroupPaths", ctrl) - } - } - } - } return proc, nil } diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index 5b4b4def6..9e6802270 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -6,12 +6,15 @@ import ( "errors" "fmt" "io" + "maps" "net" "os" "os/exec" + "path" "path/filepath" "runtime" "strconv" + "strings" "sync" "time" @@ -153,7 +156,6 @@ func (p *containerProcess) wait() (*os.ProcessState, error) { //nolint:unparam type setnsProcess struct { containerProcess - cgroupPaths map[string]string rootlessCgroups bool intelRdtPath string initProcessPid int @@ -245,7 +247,20 @@ func (p *setnsProcess) setFinalCPUAffinity() error { } func (p *setnsProcess) addIntoCgroupV1() error { - for _, path := range p.cgroupPaths { + paths := maps.Clone(p.manager.GetPaths()) + for ctrl, sub := range p.process.SubCgroupPaths { + base, ok := paths[ctrl] + if !ok { + return fmt.Errorf("unknown controller %s in SubCgroupPaths", ctrl) + } + cgPath := path.Join(base, sub) + if !strings.HasPrefix(cgPath, base) { + return fmt.Errorf("%s is not a sub cgroup path", sub) + } + paths[ctrl] = cgPath + } + + for _, path := range paths { if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) } @@ -255,19 +270,28 @@ func (p *setnsProcess) addIntoCgroupV1() error { } func (p *setnsProcess) addIntoCgroupV2() error { - path := p.cgroupPaths[""] - if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { + base := p.manager.Path("") + sub := "" + if p.process.SubCgroupPaths != nil { + sub = p.process.SubCgroupPaths[""] + } + cgPath := path.Join(base, sub) + if !strings.HasPrefix(cgPath, base) { + return fmt.Errorf("%s is not a sub cgroup path", sub) + } + + if err := cgroups.WriteCgroupProc(cgPath, p.pid()); err != nil && !p.rootlessCgroups { // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 - // Try to join the cgroup of InitProcessPid. - if p.initProcessPid != 0 { + // Try to join the cgroup of InitProcessPid, unless sub-cgroup is explicitly set. + if p.initProcessPid != 0 && sub == "" { initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid) initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile) if initCgErr == nil { if initCgPath, ok := initCg[""]; ok { initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) - logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)", - p.pid(), p.cgroupPaths, err, initCg, initCgDirpath) + logrus.Debugf("adding pid %d to cgroup %s failed (%v), attempting to join %s", + p.pid(), cgPath, err, initCgDirpath) // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) }