]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cgroup: Defer kill_css_finish() in cgroup_apply_control_disable()
authorTejun Heo <tj@kernel.org>
Tue, 5 May 2026 00:51:21 +0000 (14:51 -1000)
committerTejun Heo <tj@kernel.org>
Fri, 15 May 2026 17:24:32 +0000 (07:24 -1000)
Same race shape as the rmdir path that 93618edf7538 ("cgroup: Defer css
percpu_ref kill on rmdir until cgroup is depopulated") fixed: a task past
exit_signals() whose cset subsys[ssid] still pins the disabled controller's
css can be touching subsys state while ->css_offline() runs. The earlier
patches in this series built up the per-subsys-css deferral machinery and
routed cgroup_destroy_locked() through it. Apply the same shape to
cgroup_apply_control_disable():

kill_css_sync(css);
if (!css_is_populated(css))
kill_css_finish(css);

When the dying css is still populated, kill_css_finish() is deferred. The
walker in css_update_populated() fires kill_finish_work once the css's
hierarchical populated count drops to zero.

cgroup_lock_and_drain_offline()'s wait predicate switches from
percpu_ref_is_dying() to css_is_dying(). CSS_DYING is set by kill_css_sync()
and is a strict superset of percpu_ref_is_dying. Without this change, a +cpu
re-enable after a deferred -cpu disable would skip the drain (percpu_ref
isn't killed yet) and observe the still-CSS_DYING css through cgroup_css(),
treating it as live.

Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/cgroup/cgroup.c

index fa24102535d90892db0c5b8eddaa389812889e7c..bdc8deedb4f792a0d1c7fabf45e211888591a644 100644 (file)
@@ -3237,7 +3237,7 @@ restart:
                        struct cgroup_subsys_state *css = cgroup_css(dsct, ss);
                        DEFINE_WAIT(wait);
 
-                       if (!css || !percpu_ref_is_dying(&css->refcnt))
+                       if (!css || !css_is_dying(css))
                                continue;
 
                        cgroup_get_live(dsct);
@@ -3405,7 +3405,8 @@ static void cgroup_apply_control_disable(struct cgroup *cgrp)
                        if (css->parent &&
                            !(cgroup_ss_mask(dsct) & (1 << ss->id))) {
                                kill_css_sync(css);
-                               kill_css_finish(css);
+                               if (!css_is_populated(css))
+                                       kill_css_finish(css);
                        } else if (!css_visible(css)) {
                                css_clear_dir(css);
                                if (ss->css_reset)