|
@@ -221,6 +221,8 @@ static struct cftype cgroup_legacy_base_files[];
|
|
|
|
|
|
static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask);
|
|
|
static void cgroup_lock_and_drain_offline(struct cgroup *cgrp);
|
|
|
+static int cgroup_apply_control(struct cgroup *cgrp);
|
|
|
+static void cgroup_finalize_control(struct cgroup *cgrp, int ret);
|
|
|
static void css_task_iter_advance(struct css_task_iter *it);
|
|
|
static int cgroup_destroy_locked(struct cgroup *cgrp);
|
|
|
static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
|
|
@@ -1160,13 +1162,13 @@ static void cgroup_destroy_root(struct cgroup_root *root)
|
|
|
struct cgroup *cgrp = &root->cgrp;
|
|
|
struct cgrp_cset_link *link, *tmp_link;
|
|
|
|
|
|
- mutex_lock(&cgroup_mutex);
|
|
|
+ cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
|
|
|
|
|
|
BUG_ON(atomic_read(&root->nr_cgrps));
|
|
|
BUG_ON(!list_empty(&cgrp->self.children));
|
|
|
|
|
|
/* Rebind all subsystems back to the default hierarchy */
|
|
|
- rebind_subsystems(&cgrp_dfl_root, root->subsys_mask);
|
|
|
+ WARN_ON(rebind_subsystems(&cgrp_dfl_root, root->subsys_mask));
|
|
|
|
|
|
/*
|
|
|
* Release all the links from cset_links to this hierarchy's
|
|
@@ -1351,19 +1353,6 @@ static u16 cgroup_calc_subtree_ss_mask(struct cgroup *cgrp, u16 subtree_control)
|
|
|
return cur_ss_mask;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * cgroup_refresh_subtree_ss_mask - update subtree_ss_mask
|
|
|
- * @cgrp: the target cgroup
|
|
|
- *
|
|
|
- * Update @cgrp->subtree_ss_mask according to the current
|
|
|
- * @cgrp->subtree_control using cgroup_calc_subtree_ss_mask().
|
|
|
- */
|
|
|
-static void cgroup_refresh_subtree_ss_mask(struct cgroup *cgrp)
|
|
|
-{
|
|
|
- cgrp->subtree_ss_mask =
|
|
|
- cgroup_calc_subtree_ss_mask(cgrp, cgrp->subtree_control);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* cgroup_kn_unlock - unlocking helper for cgroup kernfs methods
|
|
|
* @kn: the kernfs_node being serviced
|
|
@@ -1459,12 +1448,10 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
|
|
|
/**
|
|
|
* css_clear_dir - remove subsys files in a cgroup directory
|
|
|
* @css: taget css
|
|
|
- * @cgrp_override: specify if target cgroup is different from css->cgroup
|
|
|
*/
|
|
|
-static void css_clear_dir(struct cgroup_subsys_state *css,
|
|
|
- struct cgroup *cgrp_override)
|
|
|
+static void css_clear_dir(struct cgroup_subsys_state *css)
|
|
|
{
|
|
|
- struct cgroup *cgrp = cgrp_override ?: css->cgroup;
|
|
|
+ struct cgroup *cgrp = css->cgroup;
|
|
|
struct cftype *cfts;
|
|
|
|
|
|
if (!(css->flags & CSS_VISIBLE))
|
|
@@ -1479,14 +1466,12 @@ static void css_clear_dir(struct cgroup_subsys_state *css,
|
|
|
/**
|
|
|
* css_populate_dir - create subsys files in a cgroup directory
|
|
|
* @css: target css
|
|
|
- * @cgrp_overried: specify if target cgroup is different from css->cgroup
|
|
|
*
|
|
|
* On failure, no file is added.
|
|
|
*/
|
|
|
-static int css_populate_dir(struct cgroup_subsys_state *css,
|
|
|
- struct cgroup *cgrp_override)
|
|
|
+static int css_populate_dir(struct cgroup_subsys_state *css)
|
|
|
{
|
|
|
- struct cgroup *cgrp = cgrp_override ?: css->cgroup;
|
|
|
+ struct cgroup *cgrp = css->cgroup;
|
|
|
struct cftype *cfts, *failed_cfts;
|
|
|
int ret;
|
|
|
|
|
@@ -1526,7 +1511,6 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
|
|
|
{
|
|
|
struct cgroup *dcgrp = &dst_root->cgrp;
|
|
|
struct cgroup_subsys *ss;
|
|
|
- u16 tmp_ss_mask;
|
|
|
int ssid, i, ret;
|
|
|
|
|
|
lockdep_assert_held(&cgroup_mutex);
|
|
@@ -1541,46 +1525,6 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
|
|
|
return -EBUSY;
|
|
|
} while_each_subsys_mask();
|
|
|
|
|
|
- /* skip creating root files on dfl_root for inhibited subsystems */
|
|
|
- tmp_ss_mask = ss_mask;
|
|
|
- if (dst_root == &cgrp_dfl_root)
|
|
|
- tmp_ss_mask &= ~cgrp_dfl_inhibit_ss_mask;
|
|
|
-
|
|
|
- do_each_subsys_mask(ss, ssid, tmp_ss_mask) {
|
|
|
- struct cgroup *scgrp = &ss->root->cgrp;
|
|
|
- int tssid;
|
|
|
-
|
|
|
- ret = css_populate_dir(cgroup_css(scgrp, ss), dcgrp);
|
|
|
- if (!ret)
|
|
|
- continue;
|
|
|
-
|
|
|
- /*
|
|
|
- * Rebinding back to the default root is not allowed to
|
|
|
- * fail. Using both default and non-default roots should
|
|
|
- * be rare. Moving subsystems back and forth even more so.
|
|
|
- * Just warn about it and continue.
|
|
|
- */
|
|
|
- if (dst_root == &cgrp_dfl_root) {
|
|
|
- if (cgrp_dfl_visible) {
|
|
|
- pr_warn("failed to create files (%d) while rebinding 0x%x to default root\n",
|
|
|
- ret, ss_mask);
|
|
|
- pr_warn("you may retry by moving them to a different hierarchy and unbinding\n");
|
|
|
- }
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- do_each_subsys_mask(ss, tssid, tmp_ss_mask) {
|
|
|
- if (tssid == ssid)
|
|
|
- break;
|
|
|
- css_clear_dir(cgroup_css(scgrp, ss), dcgrp);
|
|
|
- } while_each_subsys_mask();
|
|
|
- return ret;
|
|
|
- } while_each_subsys_mask();
|
|
|
-
|
|
|
- /*
|
|
|
- * Nothing can fail from this point on. Remove files for the
|
|
|
- * removed subsystems and rebind each subsystem.
|
|
|
- */
|
|
|
do_each_subsys_mask(ss, ssid, ss_mask) {
|
|
|
struct cgroup_root *src_root = ss->root;
|
|
|
struct cgroup *scgrp = &src_root->cgrp;
|
|
@@ -1589,8 +1533,12 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
|
|
|
|
|
|
WARN_ON(!css || cgroup_css(dcgrp, ss));
|
|
|
|
|
|
- css_clear_dir(css, NULL);
|
|
|
+ /* disable from the source */
|
|
|
+ src_root->subsys_mask &= ~(1 << ssid);
|
|
|
+ WARN_ON(cgroup_apply_control(scgrp));
|
|
|
+ cgroup_finalize_control(scgrp, 0);
|
|
|
|
|
|
+ /* rebind */
|
|
|
RCU_INIT_POINTER(scgrp->subsys[ssid], NULL);
|
|
|
rcu_assign_pointer(dcgrp->subsys[ssid], css);
|
|
|
ss->root = dst_root;
|
|
@@ -1602,20 +1550,20 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
|
|
|
&dcgrp->e_csets[ss->id]);
|
|
|
spin_unlock_bh(&css_set_lock);
|
|
|
|
|
|
- src_root->subsys_mask &= ~(1 << ssid);
|
|
|
- scgrp->subtree_control &= ~(1 << ssid);
|
|
|
- cgroup_refresh_subtree_ss_mask(scgrp);
|
|
|
-
|
|
|
/* default hierarchy doesn't enable controllers by default */
|
|
|
dst_root->subsys_mask |= 1 << ssid;
|
|
|
if (dst_root == &cgrp_dfl_root) {
|
|
|
static_branch_enable(cgroup_subsys_on_dfl_key[ssid]);
|
|
|
} else {
|
|
|
dcgrp->subtree_control |= 1 << ssid;
|
|
|
- cgroup_refresh_subtree_ss_mask(dcgrp);
|
|
|
static_branch_disable(cgroup_subsys_on_dfl_key[ssid]);
|
|
|
}
|
|
|
|
|
|
+ ret = cgroup_apply_control(dcgrp);
|
|
|
+ if (ret)
|
|
|
+ pr_warn("partial failure to rebind %s controller (err=%d)\n",
|
|
|
+ ss->name, ret);
|
|
|
+
|
|
|
if (ss->bind)
|
|
|
ss->bind(css);
|
|
|
} while_each_subsys_mask();
|
|
@@ -1807,7 +1755,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- mutex_lock(&cgroup_mutex);
|
|
|
+ cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
|
|
|
|
|
|
/* See what subsystems are wanted */
|
|
|
ret = parse_cgroupfs_options(data, &opts);
|
|
@@ -1840,7 +1788,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
|
|
|
if (ret)
|
|
|
goto out_unlock;
|
|
|
|
|
|
- rebind_subsystems(&cgrp_dfl_root, removed_mask);
|
|
|
+ WARN_ON(rebind_subsystems(&cgrp_dfl_root, removed_mask));
|
|
|
|
|
|
if (opts.release_agent) {
|
|
|
spin_lock(&release_agent_path_lock);
|
|
@@ -1991,7 +1939,7 @@ static int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
|
|
|
}
|
|
|
root_cgrp->kn = root->kf_root->kn;
|
|
|
|
|
|
- ret = css_populate_dir(&root_cgrp->self, NULL);
|
|
|
+ ret = css_populate_dir(&root_cgrp->self);
|
|
|
if (ret)
|
|
|
goto destroy_root;
|
|
|
|
|
@@ -2070,7 +2018,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
|
|
goto out_mount;
|
|
|
}
|
|
|
|
|
|
- mutex_lock(&cgroup_mutex);
|
|
|
+ cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
|
|
|
|
|
|
/* First find the desired set of subsystems */
|
|
|
ret = parse_cgroupfs_options(data, &opts);
|
|
@@ -3123,7 +3071,7 @@ static int cgroup_apply_control_enable(struct cgroup *cgrp)
|
|
|
}
|
|
|
|
|
|
if (cgroup_control(dsct) & (1 << ss->id)) {
|
|
|
- ret = css_populate_dir(css, NULL);
|
|
|
+ ret = css_populate_dir(css);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
}
|
|
@@ -3162,10 +3110,11 @@ static void cgroup_apply_control_disable(struct cgroup *cgrp)
|
|
|
if (!css)
|
|
|
continue;
|
|
|
|
|
|
- if (!(cgroup_ss_mask(dsct) & (1 << ss->id))) {
|
|
|
+ if (css->parent &&
|
|
|
+ !(cgroup_ss_mask(dsct) & (1 << ss->id))) {
|
|
|
kill_css(css);
|
|
|
} else if (!(cgroup_control(dsct) & (1 << ss->id))) {
|
|
|
- css_clear_dir(css, NULL);
|
|
|
+ css_clear_dir(css);
|
|
|
if (ss->css_reset)
|
|
|
ss->css_reset(css);
|
|
|
}
|
|
@@ -5159,7 +5108,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
|
|
|
if (ret)
|
|
|
goto out_destroy;
|
|
|
|
|
|
- ret = css_populate_dir(&cgrp->self, NULL);
|
|
|
+ ret = css_populate_dir(&cgrp->self);
|
|
|
if (ret)
|
|
|
goto out_destroy;
|
|
|
|
|
@@ -5231,7 +5180,7 @@ static void kill_css(struct cgroup_subsys_state *css)
|
|
|
* This must happen before css is disassociated with its cgroup.
|
|
|
* See seq_css() for details.
|
|
|
*/
|
|
|
- css_clear_dir(css, NULL);
|
|
|
+ css_clear_dir(css);
|
|
|
|
|
|
/*
|
|
|
* Killing would put the base ref, but we need to keep it alive
|