Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgroups/systemd: add cgroup-v2 path to the list when using hybrid mode #2087

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libcontainer/cgroups/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ var (

var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")

func init() {
// If using cgroups-hybrid mode then add a "" controller indicating
// it should join the cgroups v2.
if cgroups.IsCgroup2HybridMode() {
subsystems = append(subsystems, &NameGroup{GroupName: "", Join: true})
}
}

type subsystem interface {
// Name returns the name of the subsystem.
Name() string
Expand Down
13 changes: 13 additions & 0 deletions libcontainer/cgroups/systemd/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ func (m *legacyManager) Apply(pid int) error {
}
paths[s.Name()] = subsystemPath
}

// If systemd is using cgroups-hybrid mode then add the slice path of
// this container to the paths so the following process executed with
// "runc exec" joins that cgroup as well.
if cgroups.IsCgroup2HybridMode() {
// "" means cgroup-hybrid path
cgroupsHybridPath, err := getSubsystemPath(m.cgroups, "")
if err != nil && cgroups.IsNotFound(err) {
return err
}
paths[""] = cgroupsHybridPath
}

m.paths = paths

if err := m.joinCgroups(pid); err != nil {
Expand Down
21 changes: 21 additions & 0 deletions libcontainer/cgroups/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ import (
const (
CgroupProcesses = "cgroup.procs"
unifiedMountpoint = "/sys/fs/cgroup"
hybridMountpoint = "/sys/fs/cgroup/unified"
)

var (
isUnifiedOnce sync.Once
isUnified bool
isHybridOnce sync.Once
isHybrid bool
)

// IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode.
Expand All @@ -50,6 +53,24 @@ func IsCgroup2UnifiedMode() bool {
return isUnified
}

// IsCgroup2HybridMode returns whether we are running in cgroup v2 hybrid mode.
func IsCgroup2HybridMode() bool {
isHybridOnce.Do(func() {
var st unix.Statfs_t
err := unix.Statfs(hybridMountpoint, &st)
if err != nil {
if os.IsNotExist(err) {
// ignore the "not found" error
isHybrid = false
return
}
panic(fmt.Sprintf("cannot statfs cgroup root: %s", err))
}
isHybrid = st.Type == unix.CGROUP2_SUPER_MAGIC
})
return isHybrid
}

type Mount struct {
Mountpoint string
Root string
Expand Down
12 changes: 12 additions & 0 deletions libcontainer/cgroups/v1_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) {
return "", errUnified
}

// If subsystem is empty it means that we are looking for the
// cgroups2 path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say "cgroup2 hybrid path" instead.

if len(subsystem) == 0 {
return hybridMountpoint, nil
}

// Avoid parsing mountinfo by trying the default path first, if possible.
if path := tryDefaultPath(cgroupPath, subsystem); path != "" {
return path, nil
Expand Down Expand Up @@ -226,6 +232,12 @@ func GetOwnCgroupPath(subsystem string) (string, error) {
return "", err
}

// If subsystem is empty it means that we are looking for the
// cgroups2 path
if len(subsystem) == 0 {
return hybridMountpoint, nil
}

return getCgroupPathHelper(subsystem, cgroup)
}

Expand Down
23 changes: 23 additions & 0 deletions tests/integration/cgroups.bats
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,26 @@ function setup() {

check_cpu_weight 42
}

@test "runc exec (cgroup v2 hybrid joins correct cgroup)" {
requires root cgroups_hybrid

set_cgroups_path "$BUSYBOX_BUNDLE"
set_cgroup_mount_writable "$BUSYBOX_BUNDLE"

runc run --pid-file pid.txt -d --console-socket "$CONSOLE_SOCKET" test_cgroups_group
[ "$status" -eq 0 ]

pid=$(cat pid.txt)
run_cgroup=$(tail -1 </proc/"$pid"/cgroup)
[[ $run_cgroup == *"runc-cgroups-integration-test"* ]]

runc exec test_cgroups_group cat /proc/self/cgroup
[ "$status" -eq 0 ]

exec_cgroup=${lines[-1]}
[[ $exec_cgroup == *"runc-cgroups-integration-test"* ]]

# check that the cgroups v2 path is the same for both processes
[[ "$run_cgroup" == "$exec_cgroup" ]]
}
9 changes: 9 additions & 0 deletions tests/integration/helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ function init_cgroup_paths() {
CGROUP_SUBSYSTEMS+=" freezer"
fi
else
if stat -f -c %t /sys/fs/cgroup/unified | grep -qFw 63677270; then
CGROUP_HYBRID=yes
fi
CGROUP_UNIFIED=no
CGROUP_SUBSYSTEMS=$(awk '!/^#/ {print $1}' /proc/cgroups)
for g in ${CGROUP_SUBSYSTEMS}; do
Expand Down Expand Up @@ -352,6 +355,12 @@ function requires() {
skip_me=1
fi
;;
cgroups_hybrid)
init_cgroup_paths
if [ "$CGROUP_HYBRID" != "yes" ]; then
skip_me=1
fi
;;
smp)
local cpu_count=$(grep -c '^processor' /proc/cpuinfo)
if [ "$cpu_count" -lt 2 ]; then
Expand Down