From: Zefan Li
ohos inclusion
category: bugfix
issue: #I3ZXZF
CVE: NA
-------------------------------------------------
Since commit 3c606d35fe97 ("cgroup: prevent mount hang due to memory
controller lifetime"), a cgroup root won't be destroyed if there are any
child cgroups, dead or alive.
This introduced a small regression.
# cat test.sh
mount -t cgroup -o cpuset xxx /cgroup
mkdir /cgroup/tmp
rmdir /cgroup/tmp
umount /cgroup
After running this script, you'll probably find the cgroup hierarchy
is still active.
# cat /proc/cgroups | grep cpuset
#subsys_name hierarchy num_cgroups enabled
cpuset 1 1 1
...
Fix this by waiting for a while when umount. Now run the script again
and you'll see:
# cat /proc/cgroups | grep cpuset
#subsys_name hierarchy num_cgroups enabled
cpuset 0 1 1
...
Cc: stable@vger.kernel.org # 3.19+
Signed-off-by: Zefan Li
Tested-by: Kefeng Wang
Signed-off-by: Hanjun Guo
Signed-off-by: Changchun Yu
Reviewed-by: Zefan Li
Signed-off-by: zhangyi (F)
Signed-off-by: Lu Jialin
conflict:
kernel/cgroup/cgroup.c
Reviewed-by: xiu jianfeng
Signed-off-by: Chen Jun
Signed-off-by: Yu Changchun
---
include/linux/cgroup-defs.h | 3 +++
kernel/cgroup/cgroup.c | 16 +++++++++++++++-
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index fee0b5547cd0..ffec16930b00 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -509,6 +509,9 @@ struct cgroup_root {
/* Number of cgroups in the hierarchy, used only for /proc/cgroups */
atomic_t nr_cgrps;
+ /* Wait while cgroups are being destroyed */
+ wait_queue_head_t wait;
+
/* A list running through the active hierarchies */
struct list_head root_list;
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index c8b811e039cc..416bec1428d0 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -1914,6 +1914,7 @@ void init_cgroup_root(struct cgroup_fs_context *ctx)
atomic_set(&root->nr_cgrps, 1);
cgrp->root = root;
init_cgroup_housekeeping(cgrp);
+ init_waitqueue_head(&root->wait);
root->flags = ctx->flags;
if (ctx->release_agent)
@@ -2139,6 +2140,17 @@ static void cgroup_kill_sb(struct super_block *sb)
struct kernfs_root *kf_root = kernfs_root_from_sb(sb);
struct cgroup_root *root = cgroup_root_from_kf(kf_root);
+ /*
+ * Wait if there are cgroups being destroyed, because the destruction
+ * is asynchronous. On the other hand some controllers like memcg
+ * may pin cgroups for a very long time, so don't wait forever.
+ */
+ if (root != &cgrp_dfl_root) {
+ wait_event_timeout(root->wait,
+ list_empty(&root->cgrp.self.children),
+ msecs_to_jiffies(500));
+ }
+
/*
* If @root doesn't have any children, start killing it.
* This prevents new mounts by disabling percpu_ref_tryget_live().
@@ -5025,8 +5037,10 @@ static void css_release_work_fn(struct work_struct *work)
if (cgrp->kn)
RCU_INIT_POINTER(*(void __rcu __force **)&cgrp->kn->priv,
NULL);
+ if (css->parent && !css->parent->parent &&
+ list_empty(&css->parent->children))
+ wake_up(&cgrp->root->wait);
}
-
mutex_unlock(&cgroup_mutex);
INIT_RCU_WORK(&css->destroy_rwork, css_free_rwork_fn);
--
2.22.0