Commit 53e871a3 authored by Zefan Li's avatar Zefan Li Committed by yanhaitao
Browse files

cgroup: wait for cgroup destruction to complete when umount

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I8QLND


CVE: N/A

-------------------------------------------------

Since commit 3c606d35 ("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: default avatarZefan Li <lizefan@huawei.com>
Signed-off-by: default avatarchenridong <chenridong@huawei.com>
parent 5204214f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -556,6 +556,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;

+15 −0
Original line number Diff line number Diff line
@@ -2018,6 +2018,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);

	/* DYNMODS must be modified through cgroup_favor_dynmods() */
	root->flags = ctx->flags & ~CGRP_ROOT_FAVOR_DYNMODS;
@@ -2254,6 +2255,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().
@@ -5445,6 +5457,9 @@ 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);
	}

	cgroup_unlock();