FAILED: patch "[PATCH] cgroup: Defer css percpu_ref kill on rmdir until cgroup is" failed to apply to 7.0-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 7.0-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.0.y
git checkout FETCH_HEAD
git cherry-pick -x 93618edf753838a727dbff63c7c291dee22d656b
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026051228-thrift-subside-ee8f@gregkh' --subject-prefix 'PATCH 7.0.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 93618edf753838a727dbff63c7c291dee22d656b Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Fri, 1 May 2026 08:31:22 -1000
Subject: [PATCH] cgroup: Defer css percpu_ref kill on rmdir until cgroup is
depopulated
A chain of commits going back to v7.0 reworked rmdir to satisfy the controller invariant that a subsystem's ->css_offline() must not run while tasks are still doing kernel-side work in the cgroup.
[1] d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out")
[2] a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup")
[3] 1b164b876c36 ("cgroup: Wait for dying tasks to leave on rmdir")
[4] 4c56a8ac6869 ("cgroup: Fix cgroup_drain_dying() testing the wrong condition")
[5] 13e786b64bd3 ("cgroup: Increment nr_dying_subsys_* from rmdir context")
[1] moved task cset unlink from do_exit() to finish_task_switch() so a task's cset link drops only after the task has fully stopped scheduling. That made tasks past exit_signals() linger on cset->tasks until their final context switch, which led to a series of problems as what userspace expected to see after rmdir diverged from what the kernel needs to wait for. [2]-[5] tried to bridge that divergence: [2] filtered the exiting tasks from cgroup.procs; [3] had rmdir(2) sleep in TASK_UNINTERRUPTIBLE for them; [4] fixed the wait's condition; [5] made nr_dying_subsys_* visible synchronously.
The cgroup_drain_dying() wait in [3] turned out to be a dead end. When the rmdir caller is also the reaper of a zombie that pins a pidns teardown (e.g. host PID 1 systemd reaping orphan pids that were re-parented to it during the same teardown), rmdir blocks in TASK_UNINTERRUPTIBLE waiting for those pids to free, the pids can't free because PID 1 is the reaper and it's stuck in rmdir, and the system A-A deadlocks. No internal lock ordering breaks this; the wait itself is the bug.
The css killing side that drove the original reorder, however, can be made cleanly asynchronous: ->css_offline() is already async, run from css_killed_work_fn() driven by percpu_ref_kill_and_confirm(). The fix is to make that chain start only after all tasks have left the cgroup. rmdir's user-visible side then returns as soon as cgroup.procs and friends are empty, while ->css_offline() still runs only after the cgroup is fully drained.
Verified by the original reproducer (pidns teardown + zombie reaper, runs under vng) which hangs vanilla and succeeds here, and by per-commit deterministic repros for [2], [3], [4], [5] with a boot parameter that widens the post-exit_signals() window so each state is reliably reachable. Some stress tests on top of that.
cgroup_apply_control_disable() has the same shape of pre-existing race:
when a controller is disabled via subtree_control, kill_css() ran synchronously while tasks past exit_signals() could still be linked to the cgroup's csets, and ->css_offline() could fire before they drained. This patch preserves the existing synchronous behavior at that call site (kill_css_sync() + kill_css_finish() back-to-back) and a follow-up patch will defer kill_css_finish() there using a per-css trigger.
This seems like the right approach and I don't see problems with it. The changes are somewhat invasive but not excessively so, so backporting to -stable should be okay. If something does turn out to be wrong, the fallback is to revert the entire chain ([1]-[5]) and rework in the development branch instead.
v2: Pin cgrp across the deferred destroy work with explicit
cgroup_get()/cgroup_put() around queue_work() and the work_fn. v1 wasn't actually broken (ordered cgroup_offline_wq + queue_work order in cgroup_task_dead() saved it) but the explicit ref removes the dependency on those non-obvious invariants. Also note the pre-existing cgroup_apply_control_disable() race in the description; a follow-up will defer kill_css_finish() there.
include/linux/cgroup-defs.h +2 -2
@@ -611,8 +611,8 @@ struct cgroup {/* used to wait for offlining of csses */ wait_queue_head_t offline_waitq; -/* used by cgroup_rmdir() to wait for dying tasks to leave */ -wait_queue_head_t dying_populated_waitq; +/* defers killing csses after removal until cgroup is depopulated */ +struct work_struct finish_destroy_work;/* used to schedule release agent */ struct work_struct release_agent_work;
kernel/cgroup/cgroup.c +114 -130
365 lines not on this page · raw message
Sasha Levin
maintainerPATCH 1/2 cgroup: Increment nr_dying_subsys_* from rmdir context
From: Petr Malat <[email protected]>
[ Upstream commit 13e786b64bd3fd81c7eb22aa32bf8305c32f2ccf ]
Incrementing nr_dying_subsys_* in offline_css(), which is executed by cgroup_offline_wq worker, leads to a race where user can see the value to be 0 if he reads cgroup.stat after calling rmdir and before the worker executes. This makes the user wrongly expect resources released by the removed cgroup to be available for a new assignment.
Increment nr_dying_subsys_* from kill_css(), which is called from the cgroup_rmdir() context.
Stable-dep-of: 93618edf7538 ("cgroup: Defer css percpu_ref kill on rmdir until cgroup is depopulated")
--- kernel/cgroup/cgroup.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-)
kernel/cgroup/cgroup.c +12 -10
@@ -5768,16 +5768,6 @@ static void offline_css(struct cgroup_subsys_state *css)RCU_INIT_POINTER (css->cgroup->subsys[ss->id],NULL );wake_up_all (&css->cgroup->offline_waitq); - - css->cgroup->nr_dying_subsys[ss->id]++; -/* -* Parent css and cgroup cannot be freed until after the freeing -* of child css, see css_free_rwork_fn(). -*/ -while ((css = css->parent)) { - css->nr_descendants--; - css->cgroup->nr_dying_subsys[ss->id]++; - } }/**
@@ -6089,6 +6079,8 @@ static void css_killed_ref_fn(struct percpu_ref *ref)*/ static void kill_css (struct cgroup_subsys_state *css) { +struct cgroup_subsys *ss = css->ss; +lockdep_assert_held (&cgroup_mutex);if (css->flags &CSS_DYING )
@@ -6125,6 +6117,16 @@ static void kill_css(struct cgroup_subsys_state *css)* css is confirmed to be seen as killed on all CPUs. */ percpu_ref_kill_and_confirm (&css->refcnt, css_killed_ref_fn); + + css->cgroup->nr_dying_subsys[ss->id]++; +/* +* Parent css and cgroup cannot be freed until after the freeing +* of child css, see css_free_rwork_fn(). +*/ +while ((css = css->parent)) { + css->nr_descendants--; + css->cgroup->nr_dying_subsys[ss->id]++; + } }/**
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 cgroup: Defer css percpu_ref kill on rmdir until cgroup is depopulated
From: Tejun Heo <[email protected]>
[ Upstream commit 93618edf753838a727dbff63c7c291dee22d656b ]
A chain of commits going back to v7.0 reworked rmdir to satisfy the controller invariant that a subsystem's ->css_offline() must not run while tasks are still doing kernel-side work in the cgroup.
[1] d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out")
[2] a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup")
[3] 1b164b876c36 ("cgroup: Wait for dying tasks to leave on rmdir")
[4] 4c56a8ac6869 ("cgroup: Fix cgroup_drain_dying() testing the wrong condition")
[5] 13e786b64bd3 ("cgroup: Increment nr_dying_subsys_* from rmdir context")
[1] moved task cset unlink from do_exit() to finish_task_switch() so a task's cset link drops only after the task has fully stopped scheduling. That made tasks past exit_signals() linger on cset->tasks until their final context switch, which led to a series of problems as what userspace expected to see after rmdir diverged from what the kernel needs to wait for. [2]-[5] tried to bridge that divergence: [2] filtered the exiting tasks from cgroup.procs; [3] had rmdir(2) sleep in TASK_UNINTERRUPTIBLE for them; [4] fixed the wait's condition; [5] made nr_dying_subsys_* visible synchronously.
The cgroup_drain_dying() wait in [3] turned out to be a dead end. When the rmdir caller is also the reaper of a zombie that pins a pidns teardown (e.g. host PID 1 systemd reaping orphan pids that were re-parented to it during the same teardown), rmdir blocks in TASK_UNINTERRUPTIBLE waiting for those pids to free, the pids can't free because PID 1 is the reaper and it's stuck in rmdir, and the system A-A deadlocks. No internal lock ordering breaks this; the wait itself is the bug.
The css killing side that drove the original reorder, however, can be made cleanly asynchronous: ->css_offline() is already async, run from css_killed_work_fn() driven by percpu_ref_kill_and_confirm(). The fix is to make that chain start only after all tasks have left the cgroup. rmdir's user-visible side then returns as soon as cgroup.procs and friends are empty, while ->css_offline() still runs only after the cgroup is fully drained.
Verified by the original reproducer (pidns teardown + zombie reaper, runs under vng) which hangs vanilla and succeeds here, and by per-commit deterministic repros for [2], [3], [4], [5] with a boot parameter that widens the post-exit_signals() window so each state is reliably reachable. Some stress tests on top of that.
cgroup_apply_control_disable() has the same shape of pre-existing race:
when a controller is disabled via subtree_control, kill_css() ran synchronously while tasks past exit_signals() could still be linked to the cgroup's csets, and ->css_offline() could fire before they drained. This patch preserves the existing synchronous behavior at that call site (kill_css_sync() + kill_css_finish() back-to-back) and a follow-up patch will defer kill_css_finish() there using a per-css trigger.
This seems like the right approach and I don't see problems with it. The changes are somewhat invasive but not excessively so, so backporting to -stable should be okay. If something does turn out to be wrong, the fallback is to revert the entire chain ([1]-[5]) and rework in the development branch instead.
v2: Pin cgrp across the deferred destroy work with explicit
cgroup_get()/cgroup_put() around queue_work() and the work_fn. v1 wasn't actually broken (ordered cgroup_offline_wq + queue_work order in cgroup_task_dead() saved it) but the explicit ref removes the dependency on those non-obvious invariants. Also note the pre-existing cgroup_apply_control_disable() race in the description; a follow-up will defer kill_css_finish() there.
--- include/linux/cgroup-defs.h | 4 +- kernel/cgroup/cgroup.c | 250 +++++++++++++++++------------------- 2 files changed, 119 insertions(+), 135 deletions(-)
include/linux/cgroup-defs.h +2 -2
@@ -609,8 +609,8 @@ struct cgroup {/* used to wait for offlining of csses */ wait_queue_head_t offline_waitq; -/* used by cgroup_rmdir() to wait for dying tasks to leave */ -wait_queue_head_t dying_populated_waitq; +/* defers killing csses after removal until cgroup is depopulated */ +struct work_struct finish_destroy_work;/* used to schedule release agent */ struct work_struct release_agent_work;
kernel/cgroup/cgroup.c +117 -133
368 lines not on this page · raw message
Signature · 1 line
2.53.0