Commit 4df8dc90 authored by Tejun Heo's avatar Tejun Heo
Browse files

cgroup: restructure file creation / removal handling



The file creation / removal path has always been a bit icky and the
planned notification update requires css during file creation.
Restructure as follows.

* cgroup_addrm_files() now takes both @css and @cgrp and is only
  called directly by other file handling functions.

* cgroup_populate/clear_dir() are replaced with
  css_populate/clear_dir() taking @css and @cgrp_override.
  @cgrp_override is used only when files needs to be created on /
  removed from a cgroup which isn't attached to @css which happens
  during subsystem rebinds.  Subsystem loops are moved to the callers.

* cgroup_add_file() now takes both @css and @cgrp.  @css isn't used
  yet but will be used by the planned notification update.

This patch doens't cause any behavior changes.

Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
parent 1ada4838
Loading
Loading
Loading
Loading
+76 −67
Original line number Original line Diff line number Diff line
@@ -221,7 +221,8 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss,
		      bool visible);
		      bool visible);
static void css_release(struct percpu_ref *ref);
static void css_release(struct percpu_ref *ref);
static void kill_css(struct cgroup_subsys_state *css);
static void kill_css(struct cgroup_subsys_state *css);
static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
static int cgroup_addrm_files(struct cgroup_subsys_state *css,
			      struct cgroup *cgrp, struct cftype cfts[],
			      bool is_add);
			      bool is_add);


/**
/**
@@ -1313,53 +1314,57 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
}
}


/**
/**
 * cgroup_clear_dir - remove subsys files in a cgroup directory
 * css_clear_dir - remove subsys files in a cgroup directory
 * @cgrp: target cgroup
 * @css: taget css
 * @subsys_mask: mask of the subsystem ids whose files should be removed
 * @cgrp_override: specify if target cgroup is different from css->cgroup
 */
 */
static void cgroup_clear_dir(struct cgroup *cgrp, unsigned long subsys_mask)
static void css_clear_dir(struct cgroup_subsys_state *css,
			  struct cgroup *cgrp_override)
{
{
	struct cgroup_subsys *ss;
	struct cgroup *cgrp = cgrp_override ?: css->cgroup;
	int i;

	for_each_subsys(ss, i) {
	struct cftype *cfts;
	struct cftype *cfts;


		if (!(subsys_mask & (1 << i)))
	list_for_each_entry(cfts, &css->ss->cfts, node)
			continue;
		cgroup_addrm_files(css, cgrp, cfts, false);
		list_for_each_entry(cfts, &ss->cfts, node)
			cgroup_addrm_files(cgrp, cfts, false);
	}
}
}


/**
/**
 * cgroup_populate_dir - create subsys files in a cgroup directory
 * css_populate_dir - create subsys files in a cgroup directory
 * @cgrp: target cgroup
 * @css: target css
 * @subsys_mask: mask of the subsystem ids whose files should be added
 * @cgrp_overried: specify if target cgroup is different from css->cgroup
 *
 *
 * On failure, no file is added.
 * On failure, no file is added.
 */
 */
static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask)
static int css_populate_dir(struct cgroup_subsys_state *css,
			    struct cgroup *cgrp_override)
{
{
	struct cgroup_subsys *ss;
	struct cgroup *cgrp = cgrp_override ?: css->cgroup;
	int i, ret = 0;
	struct cftype *cfts, *failed_cfts;
	int ret;


	/* process cftsets of each subsystem */
	if (!css->ss) {
	for_each_subsys(ss, i) {
		if (cgroup_on_dfl(cgrp))
		struct cftype *cfts;
			cfts = cgroup_dfl_base_files;
		else
			cfts = cgroup_legacy_base_files;


		if (!(subsys_mask & (1 << i)))
		return cgroup_addrm_files(&cgrp->self, cgrp, cfts, true);
			continue;
	}


		list_for_each_entry(cfts, &ss->cfts, node) {
	list_for_each_entry(cfts, &css->ss->cfts, node) {
			ret = cgroup_addrm_files(cgrp, cfts, true);
		ret = cgroup_addrm_files(css, cgrp, cfts, true);
			if (ret < 0)
		if (ret < 0) {
			failed_cfts = cfts;
			goto err;
			goto err;
		}
		}
	}
	}
	return 0;
	return 0;
err:
err:
	cgroup_clear_dir(cgrp, subsys_mask);
	list_for_each_entry(cfts, &css->ss->cfts, node) {
		if (cfts == failed_cfts)
			break;
		cgroup_addrm_files(css, cgrp, cfts, false);
	}
	return ret;
	return ret;
}
}


@@ -1388,10 +1393,13 @@ static int rebind_subsystems(struct cgroup_root *dst_root,
	if (dst_root == &cgrp_dfl_root)
	if (dst_root == &cgrp_dfl_root)
		tmp_ss_mask &= ~cgrp_dfl_root_inhibit_ss_mask;
		tmp_ss_mask &= ~cgrp_dfl_root_inhibit_ss_mask;


	ret = cgroup_populate_dir(dcgrp, tmp_ss_mask);
	for_each_subsys_which(ss, ssid, &tmp_ss_mask) {
	if (ret) {
		struct cgroup *scgrp = &ss->root->cgrp;
		if (dst_root != &cgrp_dfl_root)
		int tssid;
			return ret;

		ret = css_populate_dir(cgroup_css(scgrp, ss), dcgrp);
		if (!ret)
			continue;


		/*
		/*
		 * Rebinding back to the default root is not allowed to
		 * Rebinding back to the default root is not allowed to
@@ -1399,20 +1407,27 @@ static int rebind_subsystems(struct cgroup_root *dst_root,
		 * be rare.  Moving subsystems back and forth even more so.
		 * be rare.  Moving subsystems back and forth even more so.
		 * Just warn about it and continue.
		 * Just warn about it and continue.
		 */
		 */
		if (dst_root == &cgrp_dfl_root) {
			if (cgrp_dfl_root_visible) {
			if (cgrp_dfl_root_visible) {
				pr_warn("failed to create files (%d) while rebinding 0x%lx to default root\n",
				pr_warn("failed to create files (%d) while rebinding 0x%lx to default root\n",
					ret, ss_mask);
					ret, ss_mask);
				pr_warn("you may retry by moving them to a different hierarchy and unbinding\n");
				pr_warn("you may retry by moving them to a different hierarchy and unbinding\n");
			}
			}
			continue;
		}

		for_each_subsys_which(ss, tssid, &tmp_ss_mask) {
			if (tssid == ssid)
				break;
			css_clear_dir(cgroup_css(scgrp, ss), dcgrp);
		}
		return ret;
	}
	}


	/*
	/*
	 * Nothing can fail from this point on.  Remove files for the
	 * Nothing can fail from this point on.  Remove files for the
	 * removed subsystems and rebind each subsystem.
	 * removed subsystems and rebind each subsystem.
	 */
	 */
	for_each_subsys_which(ss, ssid, &ss_mask)
		cgroup_clear_dir(&ss->root->cgrp, 1 << ssid);

	for_each_subsys_which(ss, ssid, &ss_mask) {
	for_each_subsys_which(ss, ssid, &ss_mask) {
		struct cgroup_root *src_root = ss->root;
		struct cgroup_root *src_root = ss->root;
		struct cgroup *scgrp = &src_root->cgrp;
		struct cgroup *scgrp = &src_root->cgrp;
@@ -1421,6 +1436,8 @@ static int rebind_subsystems(struct cgroup_root *dst_root,


		WARN_ON(!css || cgroup_css(dcgrp, ss));
		WARN_ON(!css || cgroup_css(dcgrp, ss));


		css_clear_dir(css, NULL);

		RCU_INIT_POINTER(scgrp->subsys[ssid], NULL);
		RCU_INIT_POINTER(scgrp->subsys[ssid], NULL);
		rcu_assign_pointer(dcgrp->subsys[ssid], css);
		rcu_assign_pointer(dcgrp->subsys[ssid], css);
		ss->root = dst_root;
		ss->root = dst_root;
@@ -1791,7 +1808,6 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)
{
{
	LIST_HEAD(tmp_links);
	LIST_HEAD(tmp_links);
	struct cgroup *root_cgrp = &root->cgrp;
	struct cgroup *root_cgrp = &root->cgrp;
	struct cftype *base_files;
	struct css_set *cset;
	struct css_set *cset;
	int i, ret;
	int i, ret;


@@ -1830,12 +1846,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)
	}
	}
	root_cgrp->kn = root->kf_root->kn;
	root_cgrp->kn = root->kf_root->kn;


	if (root == &cgrp_dfl_root)
	ret = css_populate_dir(&root_cgrp->self, NULL);
		base_files = cgroup_dfl_base_files;
	else
		base_files = cgroup_legacy_base_files;

	ret = cgroup_addrm_files(root_cgrp, base_files, true);
	if (ret)
	if (ret)
		goto destroy_root;
		goto destroy_root;


@@ -2985,7 +2996,8 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
				ret = create_css(child, ss,
				ret = create_css(child, ss,
					cgrp->subtree_control & (1 << ssid));
					cgrp->subtree_control & (1 << ssid));
			else
			else
				ret = cgroup_populate_dir(child, 1 << ssid);
				ret = css_populate_dir(cgroup_css(child, ss),
						       NULL);
			if (ret)
			if (ret)
				goto err_undo_css;
				goto err_undo_css;
		}
		}
@@ -3018,7 +3030,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
			if (css_disable & (1 << ssid)) {
			if (css_disable & (1 << ssid)) {
				kill_css(css);
				kill_css(css);
			} else {
			} else {
				cgroup_clear_dir(child, 1 << ssid);
				css_clear_dir(css, NULL);
				if (ss->css_reset)
				if (ss->css_reset)
					ss->css_reset(css);
					ss->css_reset(css);
			}
			}
@@ -3066,7 +3078,7 @@ err_undo_css:
			if (css_enable & (1 << ssid))
			if (css_enable & (1 << ssid))
				kill_css(css);
				kill_css(css);
			else
			else
				cgroup_clear_dir(child, 1 << ssid);
				css_clear_dir(css, NULL);
		}
		}
	}
	}
	goto out_unlock;
	goto out_unlock;
@@ -3218,7 +3230,8 @@ static int cgroup_kn_set_ugid(struct kernfs_node *kn)
	return kernfs_setattr(kn, &iattr);
	return kernfs_setattr(kn, &iattr);
}
}


static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft)
static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
			   struct cftype *cft)
{
{
	char name[CGROUP_FILE_NAME_MAX];
	char name[CGROUP_FILE_NAME_MAX];
	struct kernfs_node *kn;
	struct kernfs_node *kn;
@@ -3249,14 +3262,16 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft)


/**
/**
 * cgroup_addrm_files - add or remove files to a cgroup directory
 * cgroup_addrm_files - add or remove files to a cgroup directory
 * @cgrp: the target cgroup
 * @css: the target css
 * @cgrp: the target cgroup (usually css->cgroup)
 * @cfts: array of cftypes to be added
 * @cfts: array of cftypes to be added
 * @is_add: whether to add or remove
 * @is_add: whether to add or remove
 *
 *
 * Depending on @is_add, add or remove files defined by @cfts on @cgrp.
 * Depending on @is_add, add or remove files defined by @cfts on @cgrp.
 * For removals, this function never fails.
 * For removals, this function never fails.
 */
 */
static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
static int cgroup_addrm_files(struct cgroup_subsys_state *css,
			      struct cgroup *cgrp, struct cftype cfts[],
			      bool is_add)
			      bool is_add)
{
{
	struct cftype *cft, *cft_end = NULL;
	struct cftype *cft, *cft_end = NULL;
@@ -3277,7 +3292,7 @@ restart:
			continue;
			continue;


		if (is_add) {
		if (is_add) {
			ret = cgroup_add_file(cgrp, cft);
			ret = cgroup_add_file(css, cgrp, cft);
			if (ret) {
			if (ret) {
				pr_warn("%s: failed to add %s, err=%d\n",
				pr_warn("%s: failed to add %s, err=%d\n",
					__func__, cft->name, ret);
					__func__, cft->name, ret);
@@ -3309,7 +3324,7 @@ static int cgroup_apply_cftypes(struct cftype *cfts, bool is_add)
		if (cgroup_is_dead(cgrp))
		if (cgroup_is_dead(cgrp))
			continue;
			continue;


		ret = cgroup_addrm_files(cgrp, cfts, is_add);
		ret = cgroup_addrm_files(css, cgrp, cfts, is_add);
		if (ret)
		if (ret)
			break;
			break;
	}
	}
@@ -4685,7 +4700,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss,
	css->id = err;
	css->id = err;


	if (visible) {
	if (visible) {
		err = cgroup_populate_dir(cgrp, 1 << ss->id);
		err = css_populate_dir(css, NULL);
		if (err)
		if (err)
			goto err_free_id;
			goto err_free_id;
	}
	}
@@ -4711,7 +4726,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss,


err_list_del:
err_list_del:
	list_del_rcu(&css->sibling);
	list_del_rcu(&css->sibling);
	cgroup_clear_dir(css->cgroup, 1 << css->ss->id);
	css_clear_dir(css, NULL);
err_free_id:
err_free_id:
	cgroup_idr_remove(&ss->css_idr, css->id);
	cgroup_idr_remove(&ss->css_idr, css->id);
err_free_percpu_ref:
err_free_percpu_ref:
@@ -4728,7 +4743,6 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
	struct cgroup_root *root;
	struct cgroup_root *root;
	struct cgroup_subsys *ss;
	struct cgroup_subsys *ss;
	struct kernfs_node *kn;
	struct kernfs_node *kn;
	struct cftype *base_files;
	int ssid, ret;
	int ssid, ret;


	/* Do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable.
	/* Do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable.
@@ -4804,12 +4818,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
	if (ret)
	if (ret)
		goto out_destroy;
		goto out_destroy;


	if (cgroup_on_dfl(cgrp))
	ret = css_populate_dir(&cgrp->self, NULL);
		base_files = cgroup_dfl_base_files;
	else
		base_files = cgroup_legacy_base_files;

	ret = cgroup_addrm_files(cgrp, base_files, true);
	if (ret)
	if (ret)
		goto out_destroy;
		goto out_destroy;


@@ -4896,7 +4905,7 @@ static void kill_css(struct cgroup_subsys_state *css)
	 * This must happen before css is disassociated with its cgroup.
	 * This must happen before css is disassociated with its cgroup.
	 * See seq_css() for details.
	 * See seq_css() for details.
	 */
	 */
	cgroup_clear_dir(css->cgroup, 1 << css->ss->id);
	css_clear_dir(css, NULL);


	/*
	/*
	 * Killing would put the base ref, but we need to keep it alive
	 * Killing would put the base ref, but we need to keep it alive