Skip to content

Commit a99ce69

Browse files
committed
cgroup: Migrate tasks to the root css when a controller is rebound
cgroup_apply_control_disable() defers kill_css_finish() while a css is still populated, relying on css_update_populated() to fire the deferred kill once the populated count reaches zero. This deadlocks when a controller is rebound out of a hierarchy. Mounting an implicit_on_dfl controller such as perf_event as a v1 hierarchy steals it off the default hierarchy, and rebind_subsystems() kills its per-cgroup csses while they are still populated. The migration run in the same step keeps the old css for a controller no longer in the hierarchy's mask, so no task is migrated off the dying csses. Their populated count never reaches zero, the deferred kill_css_finish() never fires, and the next cgroup_lock_and_drain_offline() hangs forever under cgroup_mutex. That migration is already a no-op pass over the rebound subtree. Add cgroup_rebind_ss_mask so find_existing_css_set() resolves the leaving controllers to the root css. Their tasks are migrated there, the per-cgroup csses depopulate, and cgroup_apply_control_disable() kills them synchronously. The deferral stays correct for the rmdir and controller-disable paths it was meant for. Fixes: 1dffd95 ("cgroup: Defer kill_css_finish() in cgroup_apply_control_disable()") Reported-by: Mark Brown <broonie@kernel.org> Closes: https://lore.kernel.org/all/41cd159c-54e5-45e0-81df-eaf36a6c028e@sirena.org.uk/ Reported-by: Bert Karwatzki <spasswolf@web.de> Closes: https://lore.kernel.org/all/4e986b4ed7e16547805d54b6e67d09120bc4d2f2.camel@web.de/ Tested-by: Mark Brown <broonie@kernel.org> Tested-by: Bert Karwatzki <spasswolf@web.de> Signed-off-by: Tejun Heo <tj@kernel.org>
1 parent 6935f04 commit a99ce69

1 file changed

Lines changed: 31 additions & 4 deletions

File tree

kernel/cgroup/cgroup.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,14 @@ static u32 cgrp_dfl_implicit_ss_mask;
197197
/* some controllers can be threaded on the default hierarchy */
198198
static u32 cgrp_dfl_threaded_ss_mask;
199199

200+
/*
201+
* Set across rebind_subsystems() to the controllers leaving a hierarchy.
202+
* Guarded by cgroup_mutex. Makes find_existing_css_set() resolve them to the
203+
* root css so the affected tasks are migrated there before
204+
* cgroup_apply_control_disable() kills the per-cgroup csses.
205+
*/
206+
static u32 cgroup_rebind_ss_mask;
207+
200208
/* The list of hierarchy roots */
201209
LIST_HEAD(cgroup_roots);
202210
static int cgroup_root_count;
@@ -1083,7 +1091,15 @@ static struct css_set *find_existing_css_set(struct css_set *old_cset,
10831091
* won't change, so no need for locking.
10841092
*/
10851093
for_each_subsys(ss, i) {
1086-
if (root->subsys_mask & (1UL << i)) {
1094+
if (unlikely(cgroup_rebind_ss_mask & (1UL << i))) {
1095+
/*
1096+
* @ss is leaving this hierarchy and its per-cgroup
1097+
* csses are about to be killed. Resolve to the
1098+
* surviving root css so the tasks are migrated there.
1099+
*/
1100+
template[i] = cgroup_css(&root->cgrp, ss);
1101+
WARN_ON_ONCE(!template[i]);
1102+
} else if (root->subsys_mask & (1UL << i)) {
10871103
/*
10881104
* @ss is in this hierarchy, so we want the
10891105
* effective css from @cgrp.
@@ -1853,11 +1869,17 @@ int rebind_subsystems(struct cgroup_root *dst_root, u32 ss_mask)
18531869
struct cgroup *scgrp = &cgrp_dfl_root.cgrp;
18541870

18551871
/*
1856-
* Controllers from default hierarchy that need to be rebound
1857-
* are all disabled together in one go.
1872+
* Controllers leaving the default hierarchy are disabled
1873+
* together. cgroup_rebind_ss_mask makes cgroup_apply_control()
1874+
* migrate their tasks to the root css, so the per-cgroup csses
1875+
* are unpopulated when cgroup_finalize_control() kills them.
1876+
* Clear it before cgroup_finalize_control(), which does no
1877+
* css_set lookup.
18581878
*/
18591879
cgrp_dfl_root.subsys_mask &= ~dfl_disable_ss_mask;
1880+
cgroup_rebind_ss_mask = dfl_disable_ss_mask;
18601881
WARN_ON(cgroup_apply_control(scgrp));
1882+
cgroup_rebind_ss_mask = 0;
18611883
cgroup_finalize_control(scgrp, 0);
18621884
}
18631885

@@ -1871,9 +1893,14 @@ int rebind_subsystems(struct cgroup_root *dst_root, u32 ss_mask)
18711893
WARN_ON(!css || cgroup_css(dcgrp, ss));
18721894

18731895
if (src_root != &cgrp_dfl_root) {
1874-
/* disable from the source */
1896+
/*
1897+
* Disable from the source, migrating its tasks to the
1898+
* root css first (see cgroup_rebind_ss_mask).
1899+
*/
18751900
src_root->subsys_mask &= ~(1 << ssid);
1901+
cgroup_rebind_ss_mask = 1 << ssid;
18761902
WARN_ON(cgroup_apply_control(scgrp));
1903+
cgroup_rebind_ss_mask = 0;
18771904
cgroup_finalize_control(scgrp, 0);
18781905
}
18791906

0 commit comments

Comments
 (0)