From fbde4a7fa3eac3e7562e0b78f3ca709158870d43 Mon Sep 17 00:00:00 2001 From: Peter Volkov Date: Fri, 29 May 2026 11:17:57 +0300 Subject: [PATCH 1/3] init.d/cgroups: tidy cgroup path and controller variables Use cgroup_path consistently for cgroup2_find_path() results, and replace generic controller loop variables with more descriptive names. No functional change. --- init.d/cgroups.in | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/init.d/cgroups.in b/init.d/cgroups.in index c478098fa..764ac5306 100644 --- a/init.d/cgroups.in +++ b/init.d/cgroups.in @@ -42,17 +42,17 @@ cgroup1_base() cgroup1_controllers() { - yesno "${rc_controller_cgroups:-YES}" && [ -e /proc/cgroups ] && + yesno "${rc_controller_cgroups:-YES}" && [ -e /proc/cgroups ] && grep -qw cgroup /proc/filesystems || return 0 while read -r name _ _ enabled _; do case "${enabled}" in 1) mountinfo -q "/sys/fs/cgroup/${name}" && continue - local x - for x in $rc_cgroup_controllers; do - [ "${name}" = "blkio" ] && [ "${x}" = "io" ] && - continue 2 - [ "${name}" = "${x}" ] && - continue 2 + local controller + for controller in ${rc_cgroup_controllers}; do + [ "${name}" = "blkio" ] && [ "${controller}" = "io" ] && + continue 2 + [ "${name}" = "${controller}" ] && + continue 2 done mkdir "/sys/fs/cgroup/${name}" mount -n -t cgroup -o "${cgroup_opts},${name}" \ @@ -69,32 +69,32 @@ cgroup1_controllers() cgroup2_base() { grep -qw cgroup2 /proc/filesystems || return 0 - local base - base="$(cgroup2_find_path)" - mkdir -p "${base}" - mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${base}" 2> /dev/null || - mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${base}" + local cgroup_path + cgroup_path="$(cgroup2_find_path)" + mkdir -p "${cgroup_path}" + mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${cgroup_path}" 2> /dev/null || + mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${cgroup_path}" return 0 } cgroup2_controllers() { grep -qw cgroup2 /proc/filesystems || return 0 - local active cgroup_path x y + local cgroup_path controller controllers rc_controller cgroup_path="$(cgroup2_find_path)" [ -z "${cgroup_path}" ] && return 0 [ ! -e "${cgroup_path}/cgroup.controllers" ] && return 0 - [ ! -e "${cgroup_path}/cgroup.subtree_control" ]&& return 0 - read -r active < "${cgroup_path}/cgroup.controllers" - for x in ${active}; do + [ ! -e "${cgroup_path}/cgroup.subtree_control" ] && return 0 + read -r controllers < "${cgroup_path}/cgroup.controllers" + for controller in ${controllers}; do case "${rc_cgroup_mode:-unified}" in unified) - echo "+${x}" > "${cgroup_path}/cgroup.subtree_control" + echo "+${controller}" > "${cgroup_path}/cgroup.subtree_control" ;; hybrid) - for y in ${rc_cgroup_controllers}; do - if [ "$x" = "$y" ]; then - echo "+${x}" > "${cgroup_path}/cgroup.subtree_control" + for rc_controller in ${rc_cgroup_controllers}; do + if [ "${controller}" = "${rc_controller}" ]; then + echo "+${controller}" > "${cgroup_path}/cgroup.subtree_control" fi done ;; From e6aa66e34b3269283d306930d3291d97ed662f78 Mon Sep 17 00:00:00 2001 From: Peter Volkov Date: Fri, 29 May 2026 11:28:56 +0300 Subject: [PATCH 2/3] init.d/cgroups: move cgroup2 root processes into rc.init When OpenRC runs in a delegated cgroup v2 hierarchy, processes can remain in the cgroup namespace root. This prevents enabling domain controllers in cgroup.subtree_control due to the "no internal processes" rule (man 7 cgroups). Create an rc.init cgroup and move existing root cgroup processes there before enabling subtree controllers in unified and hybrid mode. Do this for cgroup v2 directly instead of trying to detect containers, since OpenRC cannot reliably distinguish a delegated namespace root from the real cgroup root. Use /rc.init as the marker for initialized OpenRC cgroup v2 setup. Do not create per-service cgroups before rc.init exists, and move transient OpenRC service runner processes back to rc.init when removing temporary service cgroups, since the cgroup v2 root becomes parent-only after subtree controllers are enabled. Fixes #1013. --- init.d/cgroups.in | 23 +++++++++++++++++++++++ sh/rc-cgroup.sh | 11 +++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/init.d/cgroups.in b/init.d/cgroups.in index 764ac5306..60ac90ea2 100644 --- a/init.d/cgroups.in +++ b/init.d/cgroups.in @@ -74,6 +74,27 @@ cgroup2_base() mkdir -p "${cgroup_path}" mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${cgroup_path}" 2> /dev/null || mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${cgroup_path}" + mountinfo -q -f '^cgroup2$' "${cgroup_path}" && + mkdir -p "${cgroup_path}/rc.init" + return 0 +} + +cgroup2_move_init_procs() +{ + grep -qw cgroup2 /proc/filesystems || return 0 + local cgroup_path init_cgroup pid pids + cgroup_path="$(cgroup2_find_path)" + [ -z "${cgroup_path}" ] && return 0 + [ ! -e "${cgroup_path}/cgroup.procs" ] && return 0 + init_cgroup="${cgroup_path}/rc.init" + [ ! -e "${init_cgroup}/cgroup.procs" ] && return 0 + pids= + while read -r pid; do + [ -n "${pid}" ] && pids="${pids} ${pid}" + done < "${cgroup_path}/cgroup.procs" + for pid in ${pids}; do + printf "%s" "${pid}" > "${init_cgroup}/cgroup.procs" + done return 0 } @@ -107,6 +128,7 @@ cgroups_hybrid() { cgroup1_base cgroup2_base + cgroup2_move_init_procs cgroup2_controllers cgroup1_controllers return 0 @@ -122,6 +144,7 @@ cgroups_legacy() cgroups_unified() { cgroup2_base + cgroup2_move_init_procs cgroup2_controllers return 0 } diff --git a/sh/rc-cgroup.sh b/sh/rc-cgroup.sh index f10e328c1..acf584d3b 100644 --- a/sh/rc-cgroup.sh +++ b/sh/rc-cgroup.sh @@ -164,15 +164,17 @@ cgroup2_find_path() cgroup2_remove() { - local cgroup_path rc_cgroup_path + local cgroup_path init_cgroup rc_cgroup_path cgroup_path="$(cgroup2_find_path)" [ -z "${cgroup_path}" ] && return 0 + init_cgroup="${cgroup_path}/rc.init" + [ ! -e "${init_cgroup}/cgroup.procs" ] && return 0 rc_cgroup_path="${cgroup_path}/openrc.${RC_SVCNAME}" [ ! -d "${rc_cgroup_path}" ] || [ ! -e "${rc_cgroup_path}"/cgroup.events ] && return 0 grep -qx "$$" "${rc_cgroup_path}/cgroup.procs" && - printf "%d" 0 > "${cgroup_path}/cgroup.procs" + printf "%d" 0 > "${init_cgroup}/cgroup.procs" local key populated vvalue while read -r key value; do case "${key}" in @@ -187,10 +189,11 @@ cgroup2_remove() cgroup2_set_limits() { - local cgroup_path + local cgroup_path init_cgroup cgroup_path="$(cgroup2_find_path)" [ -z "${cgroup_path}" ] && return 0 - mountinfo -q "${cgroup_path}"|| return 0 + init_cgroup="${cgroup_path}/rc.init" + [ ! -e "${init_cgroup}/cgroup.procs" ] && return 0 rc_cgroup_path="${cgroup_path}/openrc.${RC_SVCNAME}" [ ! -d "${rc_cgroup_path}" ] && mkdir "${rc_cgroup_path}" [ -f "${rc_cgroup_path}"/cgroup.procs ] && From 751e27b27e49eb879f4813aa111c5507b1b318ff Mon Sep 17 00:00:00 2001 From: Peter Volkov Date: Sat, 30 May 2026 21:20:10 +0300 Subject: [PATCH 3/3] init.d/cgroups: skip kernel threads when moving cgroup2 root pids Kernel threads can appear in the cgroup v2 root on a real host, but they cannot be moved into a child cgroup. Skip such pids before writing to rc.init/cgroup.procs to avoid EINVAL during boot. Detect kernel threads from /proc/pid/stat flags using PF_KTHREAD, matching the approach used by systemd. --- init.d/cgroups.in | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/init.d/cgroups.in b/init.d/cgroups.in index 60ac90ea2..f8242a78b 100644 --- a/init.d/cgroups.in +++ b/init.d/cgroups.in @@ -79,6 +79,23 @@ cgroup2_base() return 0 } +cgroup2_process_is_kthread() +{ + local flags line pid + pid="$1" + + [ -r "/proc/${pid}/stat" ] || return 1 + read -r line < "/proc/${pid}/stat" || return 1 + line="${line##*) }" + set -- ${line} + # After removing pid and comm, /proc/pid/stat field 9 (flags) is $7 + flags="$7" + [ -n "${flags}" ] || return 1 + + # PF_KTHREAD is 0x00200000 = 2097152 in decimal + [ $(( flags & 2097152 )) -ne 0 ] +} + cgroup2_move_init_procs() { grep -qw cgroup2 /proc/filesystems || return 0 @@ -93,6 +110,8 @@ cgroup2_move_init_procs() [ -n "${pid}" ] && pids="${pids} ${pid}" done < "${cgroup_path}/cgroup.procs" for pid in ${pids}; do + [ -d "/proc/${pid}" ] || continue + cgroup2_process_is_kthread "${pid}" && continue printf "%s" "${pid}" > "${init_cgroup}/cgroup.procs" done return 0