From 734dbf69fdb2c88f53aca67349cb86b434ec6bf5 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 | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/init.d/cgroups.in b/init.d/cgroups.in index 3b4b60d0e..ae4285736 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,12 +69,12 @@ cgroup1_controllers() cgroup2_base() { grep -qw cgroup2 /proc/filesystems || return 0 - local base - base="$(cgroup2_find_path)" - mkdir -p "${base}" - if ! mountinfo -q -f '^cgroup2$' "${base}"; then - 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}" + if ! mountinfo -q -f '^cgroup2$' "${cgroup_path}"; then + mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${cgroup_path}" 2> /dev/null || + mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${cgroup_path}" fi return 0 } @@ -82,21 +82,21 @@ cgroup2_base() 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 3f36576f4e6adccfb22a975eb76f783ddde40222 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 | 15 +++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/init.d/cgroups.in b/init.d/cgroups.in index ae4285736..185364eb2 100644 --- a/init.d/cgroups.in +++ b/init.d/cgroups.in @@ -76,6 +76,27 @@ cgroup2_base() mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${cgroup_path}" 2> /dev/null || mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${cgroup_path}" fi + 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 } @@ -109,6 +130,7 @@ cgroups_hybrid() { cgroup1_base cgroup2_base + cgroup2_move_init_procs cgroup2_controllers cgroup1_controllers return 0 @@ -124,6 +146,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 547839655..5c8ac27ff 100644 --- a/sh/rc-cgroup.sh +++ b/sh/rc-cgroup.sh @@ -165,15 +165,22 @@ cgroup2_find_path() cgroup2_remove() { - local cgroup_path rc_cgroup_path + local cgroup_path rc_cgroup_path return_cgroup_procs cgroup_path="$(cgroup2_find_path)" [ -z "${cgroup_path}" ] && 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" + return_cgroup_procs="${cgroup_path}/cgroup.procs" + # rc.init is absent after a live upgrade from versions which did not + # create it, so retain the old hierarchy-root fallback in that case. + [ -e "${cgroup_path}/rc.init/cgroup.procs" ] && + return_cgroup_procs="${cgroup_path}/rc.init/cgroup.procs" + [ ! -e "${return_cgroup_procs}" ] && return 0 + if grep -qx "$$" "${rc_cgroup_path}/cgroup.procs"; then + printf "%d" 0 > "${return_cgroup_procs}" || return 0 + fi local key populated value while read -r key value; do case "${key}" in @@ -191,7 +198,7 @@ cgroup2_set_limits() local cgroup_path rc_cgroup_path key value cgroup_path="$(cgroup2_find_path)" [ -z "${cgroup_path}" ] && return 0 - mountinfo -q "${cgroup_path}"|| return 0 + mountinfo -q -f '^cgroup2$' "${cgroup_path}" || 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 b5c0e4a07507f75d7e5a6556a88fc2ad8f6ca27a 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 185364eb2..0279a829b 100644 --- a/init.d/cgroups.in +++ b/init.d/cgroups.in @@ -81,6 +81,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 @@ -95,6 +112,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