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

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
...@@ -221,7 +221,8 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss, ...@@ -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) ...@@ -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; struct cftype *cfts;
for_each_subsys(ss, i) { list_for_each_entry(cfts, &css->ss->cfts, node)
struct cftype *cfts; cgroup_addrm_files(css, cgrp, cfts, false);
if (!(subsys_mask & (1 << i)))
continue;
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) {
goto err; failed_cfts = cfts;
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, ...@@ -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, ...@@ -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 (cgrp_dfl_root_visible) { if (dst_root == &cgrp_dfl_root) {
pr_warn("failed to create files (%d) while rebinding 0x%lx to default root\n", if (cgrp_dfl_root_visible) {
ret, ss_mask); pr_warn("failed to create files (%d) while rebinding 0x%lx to default root\n",
pr_warn("you may retry by moving them to a different hierarchy and unbinding\n"); ret, ss_mask);
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, ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, ...@@ -3066,7 +3078,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
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) ...@@ -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) ...@@ -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 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], ...@@ -3277,7 +3292,7 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment