Skip to content

Commit 334c367

Browse files
committed
cgroup: reimplement rebind_subsystems() using cgroup_apply_control() and friends
rebind_subsystem() open codes quite a bit of css and interface file manipulations. It tries to be fail-safe but doesn't quite achieve it. It can be greatly simplified by using the new css management helpers. This patch reimplements rebind_subsytsems() using cgroup_apply_control() and friends. * The half-baked rollback on file creation failure is dropped. It is an extremely cold path, failure isn't critical, and, aside from kernel bugs, the only reason it can fail is memory allocation failure which pretty much doesn't happen for small allocations. * As cgroup_apply_control_disable() is now used to clean up root cgroup on rebind, make sure that it doesn't end up killing root csses. * All callers of rebind_subsystems() are updated to use cgroup_lock_and_drain_offline() as the apply_control functions require drained subtree. * This leaves cgroup_refresh_subtree_ss_mask() without any user. Removed. * css_populate_dir() and css_clear_dir() no longer needs @cgrp_override parameter. Dropped. * While at it, add WARN_ON() to rebind_subsystem() calls which are expected to always succeed just in case. While the rules visible to userland aren't changed, this reimplementation not only simplifies rebind_subsystems() but also allows it to disable and enable csses recursively. This can be used to implement more flexible rebinding. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Zefan Li <lizefan@huawei.com>
1 parent 03970d3 commit 334c367

File tree

1 file changed

+28
-79
lines changed

1 file changed

+28
-79
lines changed

kernel/cgroup.c

Lines changed: 28 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ static struct cftype cgroup_legacy_base_files[];
221221

222222
static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask);
223223
static void cgroup_lock_and_drain_offline(struct cgroup *cgrp);
224+
static int cgroup_apply_control(struct cgroup *cgrp);
225+
static void cgroup_finalize_control(struct cgroup *cgrp, int ret);
224226
static void css_task_iter_advance(struct css_task_iter *it);
225227
static int cgroup_destroy_locked(struct cgroup *cgrp);
226228
static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
@@ -1160,13 +1162,13 @@ static void cgroup_destroy_root(struct cgroup_root *root)
11601162
struct cgroup *cgrp = &root->cgrp;
11611163
struct cgrp_cset_link *link, *tmp_link;
11621164

1163-
mutex_lock(&cgroup_mutex);
1165+
cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
11641166

11651167
BUG_ON(atomic_read(&root->nr_cgrps));
11661168
BUG_ON(!list_empty(&cgrp->self.children));
11671169

11681170
/* Rebind all subsystems back to the default hierarchy */
1169-
rebind_subsystems(&cgrp_dfl_root, root->subsys_mask);
1171+
WARN_ON(rebind_subsystems(&cgrp_dfl_root, root->subsys_mask));
11701172

11711173
/*
11721174
* 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)
13511353
return cur_ss_mask;
13521354
}
13531355

1354-
/**
1355-
* cgroup_refresh_subtree_ss_mask - update subtree_ss_mask
1356-
* @cgrp: the target cgroup
1357-
*
1358-
* Update @cgrp->subtree_ss_mask according to the current
1359-
* @cgrp->subtree_control using cgroup_calc_subtree_ss_mask().
1360-
*/
1361-
static void cgroup_refresh_subtree_ss_mask(struct cgroup *cgrp)
1362-
{
1363-
cgrp->subtree_ss_mask =
1364-
cgroup_calc_subtree_ss_mask(cgrp, cgrp->subtree_control);
1365-
}
1366-
13671356
/**
13681357
* cgroup_kn_unlock - unlocking helper for cgroup kernfs methods
13691358
* @kn: the kernfs_node being serviced
@@ -1459,12 +1448,10 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
14591448
/**
14601449
* css_clear_dir - remove subsys files in a cgroup directory
14611450
* @css: taget css
1462-
* @cgrp_override: specify if target cgroup is different from css->cgroup
14631451
*/
1464-
static void css_clear_dir(struct cgroup_subsys_state *css,
1465-
struct cgroup *cgrp_override)
1452+
static void css_clear_dir(struct cgroup_subsys_state *css)
14661453
{
1467-
struct cgroup *cgrp = cgrp_override ?: css->cgroup;
1454+
struct cgroup *cgrp = css->cgroup;
14681455
struct cftype *cfts;
14691456

14701457
if (!(css->flags & CSS_VISIBLE))
@@ -1479,14 +1466,12 @@ static void css_clear_dir(struct cgroup_subsys_state *css,
14791466
/**
14801467
* css_populate_dir - create subsys files in a cgroup directory
14811468
* @css: target css
1482-
* @cgrp_overried: specify if target cgroup is different from css->cgroup
14831469
*
14841470
* On failure, no file is added.
14851471
*/
1486-
static int css_populate_dir(struct cgroup_subsys_state *css,
1487-
struct cgroup *cgrp_override)
1472+
static int css_populate_dir(struct cgroup_subsys_state *css)
14881473
{
1489-
struct cgroup *cgrp = cgrp_override ?: css->cgroup;
1474+
struct cgroup *cgrp = css->cgroup;
14901475
struct cftype *cfts, *failed_cfts;
14911476
int ret;
14921477

@@ -1526,7 +1511,6 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
15261511
{
15271512
struct cgroup *dcgrp = &dst_root->cgrp;
15281513
struct cgroup_subsys *ss;
1529-
u16 tmp_ss_mask;
15301514
int ssid, i, ret;
15311515

15321516
lockdep_assert_held(&cgroup_mutex);
@@ -1541,46 +1525,6 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
15411525
return -EBUSY;
15421526
} while_each_subsys_mask();
15431527

1544-
/* skip creating root files on dfl_root for inhibited subsystems */
1545-
tmp_ss_mask = ss_mask;
1546-
if (dst_root == &cgrp_dfl_root)
1547-
tmp_ss_mask &= ~cgrp_dfl_inhibit_ss_mask;
1548-
1549-
do_each_subsys_mask(ss, ssid, tmp_ss_mask) {
1550-
struct cgroup *scgrp = &ss->root->cgrp;
1551-
int tssid;
1552-
1553-
ret = css_populate_dir(cgroup_css(scgrp, ss), dcgrp);
1554-
if (!ret)
1555-
continue;
1556-
1557-
/*
1558-
* Rebinding back to the default root is not allowed to
1559-
* fail. Using both default and non-default roots should
1560-
* be rare. Moving subsystems back and forth even more so.
1561-
* Just warn about it and continue.
1562-
*/
1563-
if (dst_root == &cgrp_dfl_root) {
1564-
if (cgrp_dfl_visible) {
1565-
pr_warn("failed to create files (%d) while rebinding 0x%x to default root\n",
1566-
ret, ss_mask);
1567-
pr_warn("you may retry by moving them to a different hierarchy and unbinding\n");
1568-
}
1569-
continue;
1570-
}
1571-
1572-
do_each_subsys_mask(ss, tssid, tmp_ss_mask) {
1573-
if (tssid == ssid)
1574-
break;
1575-
css_clear_dir(cgroup_css(scgrp, ss), dcgrp);
1576-
} while_each_subsys_mask();
1577-
return ret;
1578-
} while_each_subsys_mask();
1579-
1580-
/*
1581-
* Nothing can fail from this point on. Remove files for the
1582-
* removed subsystems and rebind each subsystem.
1583-
*/
15841528
do_each_subsys_mask(ss, ssid, ss_mask) {
15851529
struct cgroup_root *src_root = ss->root;
15861530
struct cgroup *scgrp = &src_root->cgrp;
@@ -1589,8 +1533,12 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
15891533

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

1592-
css_clear_dir(css, NULL);
1536+
/* disable from the source */
1537+
src_root->subsys_mask &= ~(1 << ssid);
1538+
WARN_ON(cgroup_apply_control(scgrp));
1539+
cgroup_finalize_control(scgrp, 0);
15931540

1541+
/* rebind */
15941542
RCU_INIT_POINTER(scgrp->subsys[ssid], NULL);
15951543
rcu_assign_pointer(dcgrp->subsys[ssid], css);
15961544
ss->root = dst_root;
@@ -1602,20 +1550,20 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
16021550
&dcgrp->e_csets[ss->id]);
16031551
spin_unlock_bh(&css_set_lock);
16041552

1605-
src_root->subsys_mask &= ~(1 << ssid);
1606-
scgrp->subtree_control &= ~(1 << ssid);
1607-
cgroup_refresh_subtree_ss_mask(scgrp);
1608-
16091553
/* default hierarchy doesn't enable controllers by default */
16101554
dst_root->subsys_mask |= 1 << ssid;
16111555
if (dst_root == &cgrp_dfl_root) {
16121556
static_branch_enable(cgroup_subsys_on_dfl_key[ssid]);
16131557
} else {
16141558
dcgrp->subtree_control |= 1 << ssid;
1615-
cgroup_refresh_subtree_ss_mask(dcgrp);
16161559
static_branch_disable(cgroup_subsys_on_dfl_key[ssid]);
16171560
}
16181561

1562+
ret = cgroup_apply_control(dcgrp);
1563+
if (ret)
1564+
pr_warn("partial failure to rebind %s controller (err=%d)\n",
1565+
ss->name, ret);
1566+
16191567
if (ss->bind)
16201568
ss->bind(css);
16211569
} while_each_subsys_mask();
@@ -1807,7 +1755,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
18071755
return -EINVAL;
18081756
}
18091757

1810-
mutex_lock(&cgroup_mutex);
1758+
cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
18111759

18121760
/* See what subsystems are wanted */
18131761
ret = parse_cgroupfs_options(data, &opts);
@@ -1840,7 +1788,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
18401788
if (ret)
18411789
goto out_unlock;
18421790

1843-
rebind_subsystems(&cgrp_dfl_root, removed_mask);
1791+
WARN_ON(rebind_subsystems(&cgrp_dfl_root, removed_mask));
18441792

18451793
if (opts.release_agent) {
18461794
spin_lock(&release_agent_path_lock);
@@ -1991,7 +1939,7 @@ static int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
19911939
}
19921940
root_cgrp->kn = root->kf_root->kn;
19931941

1994-
ret = css_populate_dir(&root_cgrp->self, NULL);
1942+
ret = css_populate_dir(&root_cgrp->self);
19951943
if (ret)
19961944
goto destroy_root;
19971945

@@ -2070,7 +2018,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
20702018
goto out_mount;
20712019
}
20722020

2073-
mutex_lock(&cgroup_mutex);
2021+
cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
20742022

20752023
/* First find the desired set of subsystems */
20762024
ret = parse_cgroupfs_options(data, &opts);
@@ -3123,7 +3071,7 @@ static int cgroup_apply_control_enable(struct cgroup *cgrp)
31233071
}
31243072

31253073
if (cgroup_control(dsct) & (1 << ss->id)) {
3126-
ret = css_populate_dir(css, NULL);
3074+
ret = css_populate_dir(css);
31273075
if (ret)
31283076
return ret;
31293077
}
@@ -3162,10 +3110,11 @@ static void cgroup_apply_control_disable(struct cgroup *cgrp)
31623110
if (!css)
31633111
continue;
31643112

3165-
if (!(cgroup_ss_mask(dsct) & (1 << ss->id))) {
3113+
if (css->parent &&
3114+
!(cgroup_ss_mask(dsct) & (1 << ss->id))) {
31663115
kill_css(css);
31673116
} else if (!(cgroup_control(dsct) & (1 << ss->id))) {
3168-
css_clear_dir(css, NULL);
3117+
css_clear_dir(css);
31693118
if (ss->css_reset)
31703119
ss->css_reset(css);
31713120
}
@@ -5159,7 +5108,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
51595108
if (ret)
51605109
goto out_destroy;
51615110

5162-
ret = css_populate_dir(&cgrp->self, NULL);
5111+
ret = css_populate_dir(&cgrp->self);
51635112
if (ret)
51645113
goto out_destroy;
51655114

@@ -5231,7 +5180,7 @@ static void kill_css(struct cgroup_subsys_state *css)
52315180
* This must happen before css is disassociated with its cgroup.
52325181
* See seq_css() for details.
52335182
*/
5234-
css_clear_dir(css, NULL);
5183+
css_clear_dir(css);
52355184

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

0 commit comments

Comments
 (0)