cgroup_leave_vz_slice() tail-called cgroup_attach_task(cgrp, current, true)
with no locks at all, and cgroup_join_vz_slice() called it holding only
cgroup_mutex (via cgroup_kn_lock_live()) but without the threadgroup lock.
cgroup_attach_task() requires cgroup_mutex AND cgroup_threadgroup_rwsem
(and, for the ->attach() callbacks, cpus_read_lock()); see its kerneldoc.
Because the leave path is a tail call, its frame is absent from the
backtrace, so on a PROVE_LOCKING kernel the splat looked like it came from
ve_start_container() directly:
WARNING: at kernel/cgroup/cgroup.c:535 find_existing_css_set
lockdep_assert_held(&cgroup_mutex)
cgroup_attach_task <- ve_start_container <- ve_state_write
and fired ~200 times on every single container start/stop. Beyond the
annotation, this is a real race on production kernels: find_css_set() /
cgroup_migrate_prepare_dst() / cgroup_migrate_finish() manipulate the
per-cset mg_src_cgrp/mg_dst_cgrp and mg_*_preload_node fields that are
serialized only by cgroup_mutex, so a concurrent migration (any host
cgroup.procs write, another CT start, or a subtree_control update) can
corrupt the migration preload lists; the missing threadgroup lock can also
leave a thread forked mid-migration on the old cset.
Take cgroup_mutex (leave path) and cgroup_attach_lock(true) (both paths)
around cgroup_attach_task(), mirroring cgroup_attach_task_all() and
__cgroup_procs_write(); the attach_lock nests inside cgroup_mutex, and the
ve->op_sem -> cgroup_mutex order already used by the join path is preserved.
Fixes: 20a5affcf263 ("ve/cgroup: add vz.slice cgroup to put kernel threads to")
Feature: ve: ve generic structures
https://virtuozzo.atlassian.net/browse/VSTOR-137234
Signed-off-by: Konstantin Khorenko <[email protected]>
---
kernel/cgroup/cgroup.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 5585545304a3..4db77090f1be 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -2276,7 +2276,9 @@ int cgroup_join_vz_slice(struct ve_struct *ve)
return -ENODEV;
}
+ cgroup_attach_lock(true);
ret = cgroup_attach_task(cgrp, current, true);
+ cgroup_attach_unlock(true);
cgroup_kn_unlock(kn);
kernfs_put(kn);
return ret;
@@ -2286,6 +2288,7 @@ int cgroup_leave_vz_slice(struct ve_struct *ve)
{
struct css_set *cset;
struct cgroup *cgrp;
+ int ret;
cset = rcu_dereference_protected(ve->ve_nsproxy,
1)->cgroup_ns->root_cset;
cgrp = __cset_cgroup_from_root(cset, &cgrp_dfl_root);
@@ -2294,7 +2297,12 @@ int cgroup_leave_vz_slice(struct ve_struct *ve)
!test_bit(CGRP_VE_ROOT, &cgrp->flags))
return 0;
- return cgroup_attach_task(cgrp, current, true);
+ cgroup_lock();
+ cgroup_attach_lock(true);
+ ret = cgroup_attach_task(cgrp, current, true);
+ cgroup_attach_unlock(true);
+ cgroup_unlock();
+ return ret;
}
struct cgroup_subsys_state *css_ve_root1(struct cgroup_subsys_state *css)
--
2.47.1
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel