
From: Zefan Li <lizefan@huawei.com> 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 <lizefan@huawei.com> Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org> Signed-off-by: Changchun Yu <yuchangchun1@huawei.com> Reviewed-by: Zefan Li <lizefan@huawei.com> Signed-off-by: zhangyi (F) <yi.zhang@huawei.com> Signed-off-by: Lu Jialin <lujialin4@huawei.com> conflict: kernel/cgroup/cgroup.c Reviewed-by: xiu jianfeng <xiujianfeng@huawei.com> Signed-off-by: Chen Jun <chenjun102@huawei.com> Signed-off-by: Yu Changchun <yuchangchun1@huawei.com> --- 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