aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/cgroup.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-03-19 10:23:54 -0400
committerTejun Heo <tj@kernel.org>2014-03-19 10:23:54 -0400
commit5df3603229e520442502ff7fc5715c77bbf61912 (patch)
tree7e5191604fd4ddf110d523d062d29aad08753aa7 /kernel/cgroup.c
parentfdce6bf8c5b6968eb9b96ecc5fe400514a604902 (diff)
cgroup: treat cgroup_dummy_root as an equivalent hierarchy during rebinding
Currently, while rebinding, cgroup_dummy_root serves as the anchor point. In addition to the target root, rebind_subsystems() takes @added_mask and @removed_mask. The subsystems specified in the former are expected to be on the dummy root and then moved to the target root. The ones in the latter are moved from non-dummy root to dummy. Now that the dummy root is a fully functional one and we're planning to use it for the default unified hierarchy, this level of distinction between dummy and non-dummy roots is quite awkward. This patch updates rebind_subsystems() to take the target root and one subsystem mask and move the specified subsystmes to the target root which may or may not be the dummy root. IOW, unbinding now becomes moving the subsystems to the dummy root and binding to non-dummy root. This makes the dummy root mostly equivalent to other hierarchies in terms of the mechanism of moving subsystems around; however, we still retain all the semantical restrictions so that this patch doesn't introduce any visible behavior differences. Another noteworthy detail is that rebind_subsystems() guarantees that moving a subsystem to the dummy root never fails so that valid unmounting attempts always succeed. This unifies binding and unbinding of subsystems. The invocation points of ->bind() were inconsistent between the two and now moved after whole rebinding is complete. This doesn't break the current users and generally makes more sense. All rebind_subsystems() users are converted accordingly. Note that cgroup_remount() now makes two calls to rebind_subsystems() to bind and then unbind the requested subsystems. This will allow repurposing of the dummy hierarchy as the default unified hierarchy and shouldn't make any userland visible behavior difference. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Li Zefan <lizefan@huawei.com>
Diffstat (limited to 'kernel/cgroup.c')
-rw-r--r--kernel/cgroup.c100
1 files changed, 56 insertions, 44 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 78017f52c69b..b632981bd9c7 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -175,8 +175,8 @@ static int need_forkexit_callback __read_mostly;
175static struct cftype cgroup_base_files[]; 175static struct cftype cgroup_base_files[];
176 176
177static void cgroup_put(struct cgroup *cgrp); 177static void cgroup_put(struct cgroup *cgrp);
178static int rebind_subsystems(struct cgroupfs_root *root, 178static int rebind_subsystems(struct cgroupfs_root *dst_root,
179 unsigned long added_mask, unsigned removed_mask); 179 unsigned long ss_mask);
180static void cgroup_destroy_css_killed(struct cgroup *cgrp); 180static void cgroup_destroy_css_killed(struct cgroup *cgrp);
181static int cgroup_destroy_locked(struct cgroup *cgrp); 181static int cgroup_destroy_locked(struct cgroup *cgrp);
182static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], 182static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
@@ -739,7 +739,7 @@ static void cgroup_destroy_root(struct cgroupfs_root *root)
739 BUG_ON(!list_empty(&cgrp->children)); 739 BUG_ON(!list_empty(&cgrp->children));
740 740
741 /* Rebind all subsystems back to the default hierarchy */ 741 /* Rebind all subsystems back to the default hierarchy */
742 WARN_ON(rebind_subsystems(root, 0, root->subsys_mask)); 742 rebind_subsystems(&cgroup_dummy_root, root->subsys_mask);
743 743
744 /* 744 /*
745 * Release all the links from cset_links to this hierarchy's 745 * Release all the links from cset_links to this hierarchy's
@@ -976,69 +976,77 @@ static void cgroup_clear_dir(struct cgroup *cgrp, unsigned long subsys_mask)
976 } 976 }
977} 977}
978 978
979static int rebind_subsystems(struct cgroupfs_root *root, 979static int rebind_subsystems(struct cgroupfs_root *dst_root,
980 unsigned long added_mask, unsigned removed_mask) 980 unsigned long ss_mask)
981{ 981{
982 struct cgroup *cgrp = &root->top_cgroup; 982 struct cgroup *dst_top = &dst_root->top_cgroup;
983 struct cgroup_subsys *ss; 983 struct cgroup_subsys *ss;
984 int i, ret; 984 int ssid, ret;
985 985
986 lockdep_assert_held(&cgroup_tree_mutex); 986 lockdep_assert_held(&cgroup_tree_mutex);
987 lockdep_assert_held(&cgroup_mutex); 987 lockdep_assert_held(&cgroup_mutex);
988 988
989 /* Check that any added subsystems are currently free */ 989 for_each_subsys(ss, ssid) {
990 for_each_subsys(ss, i) 990 if (!(ss_mask & (1 << ssid)))
991 if ((added_mask & (1 << i)) && ss->root != &cgroup_dummy_root) 991 continue;
992
993 /* if @ss is on the dummy_root, we can always move it */
994 if (ss->root == &cgroup_dummy_root)
995 continue;
996
997 /* if @ss has non-root cgroups attached to it, can't move */
998 if (!list_empty(&ss->root->top_cgroup.children))
992 return -EBUSY; 999 return -EBUSY;
993 1000
994 ret = cgroup_populate_dir(cgrp, added_mask); 1001 /* can't move between two non-dummy roots either */
995 if (ret) 1002 if (dst_root != &cgroup_dummy_root)
996 return ret; 1003 return -EBUSY;
1004 }
1005
1006 if (dst_root != &cgroup_dummy_root) {
1007 ret = cgroup_populate_dir(dst_top, ss_mask);
1008 if (ret)
1009 return ret;
1010 }
997 1011
998 /* 1012 /*
999 * Nothing can fail from this point on. Remove files for the 1013 * Nothing can fail from this point on. Remove files for the
1000 * removed subsystems and rebind each subsystem. 1014 * removed subsystems and rebind each subsystem.
1001 */ 1015 */
1002 mutex_unlock(&cgroup_mutex); 1016 mutex_unlock(&cgroup_mutex);
1003 cgroup_clear_dir(cgrp, removed_mask); 1017 for_each_subsys(ss, ssid)
1018 if ((ss_mask & (1 << ssid)) && ss->root != &cgroup_dummy_root)
1019 cgroup_clear_dir(&ss->root->top_cgroup, 1 << ssid);
1004 mutex_lock(&cgroup_mutex); 1020 mutex_lock(&cgroup_mutex);
1005 1021
1006 for_each_subsys(ss, i) { 1022 for_each_subsys(ss, ssid) {
1007 unsigned long bit = 1UL << i; 1023 struct cgroupfs_root *src_root;
1008 1024 struct cgroup *src_top;
1009 if (bit & added_mask) { 1025 struct cgroup_subsys_state *css;
1010 /* We're binding this subsystem to this hierarchy */
1011 BUG_ON(cgroup_css(cgrp, ss));
1012 BUG_ON(!cgroup_css(cgroup_dummy_top, ss));
1013 BUG_ON(cgroup_css(cgroup_dummy_top, ss)->cgroup != cgroup_dummy_top);
1014 1026
1015 rcu_assign_pointer(cgrp->subsys[i], 1027 if (!(ss_mask & (1 << ssid)))
1016 cgroup_css(cgroup_dummy_top, ss)); 1028 continue;
1017 cgroup_css(cgrp, ss)->cgroup = cgrp;
1018 1029
1019 ss->root = root; 1030 src_root = ss->root;
1020 if (ss->bind) 1031 src_top = &src_root->top_cgroup;
1021 ss->bind(cgroup_css(cgrp, ss)); 1032 css = cgroup_css(src_top, ss);
1022 1033
1023 /* refcount was already taken, and we're keeping it */ 1034 WARN_ON(!css || cgroup_css(dst_top, ss));
1024 root->subsys_mask |= bit;
1025 } else if (bit & removed_mask) {
1026 /* We're removing this subsystem */
1027 BUG_ON(cgroup_css(cgrp, ss) != cgroup_css(cgroup_dummy_top, ss));
1028 BUG_ON(cgroup_css(cgrp, ss)->cgroup != cgrp);
1029 1035
1030 if (ss->bind) 1036 RCU_INIT_POINTER(src_top->subsys[ssid], NULL);
1031 ss->bind(cgroup_css(cgroup_dummy_top, ss)); 1037 rcu_assign_pointer(dst_top->subsys[ssid], css);
1038 ss->root = dst_root;
1039 css->cgroup = dst_top;
1032 1040
1033 cgroup_css(cgroup_dummy_top, ss)->cgroup = cgroup_dummy_top; 1041 src_root->subsys_mask &= ~(1 << ssid);
1034 RCU_INIT_POINTER(cgrp->subsys[i], NULL); 1042 dst_root->subsys_mask |= 1 << ssid;
1035 1043
1036 cgroup_subsys[i]->root = &cgroup_dummy_root; 1044 if (ss->bind)
1037 root->subsys_mask &= ~bit; 1045 ss->bind(css);
1038 }
1039 } 1046 }
1040 1047
1041 kernfs_activate(cgrp->kn); 1048 if (dst_root != &cgroup_dummy_root)
1049 kernfs_activate(dst_top->kn);
1042 return 0; 1050 return 0;
1043} 1051}
1044 1052
@@ -1277,10 +1285,12 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
1277 goto out_unlock; 1285 goto out_unlock;
1278 } 1286 }
1279 1287
1280 ret = rebind_subsystems(root, added_mask, removed_mask); 1288 ret = rebind_subsystems(root, added_mask);
1281 if (ret) 1289 if (ret)
1282 goto out_unlock; 1290 goto out_unlock;
1283 1291
1292 rebind_subsystems(&cgroup_dummy_root, removed_mask);
1293
1284 if (opts.release_agent) { 1294 if (opts.release_agent) {
1285 spin_lock(&release_agent_path_lock); 1295 spin_lock(&release_agent_path_lock);
1286 strcpy(root->release_agent_path, opts.release_agent); 1296 strcpy(root->release_agent_path, opts.release_agent);
@@ -1420,7 +1430,7 @@ static int cgroup_setup_root(struct cgroupfs_root *root, unsigned long ss_mask)
1420 if (ret) 1430 if (ret)
1421 goto destroy_root; 1431 goto destroy_root;
1422 1432
1423 ret = rebind_subsystems(root, ss_mask, 0); 1433 ret = rebind_subsystems(root, ss_mask);
1424 if (ret) 1434 if (ret)
1425 goto destroy_root; 1435 goto destroy_root;
1426 1436
@@ -4026,6 +4036,8 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
4026 4036
4027 BUG_ON(online_css(css)); 4037 BUG_ON(online_css(css));
4028 4038
4039 cgroup_dummy_root.subsys_mask |= 1 << ss->id;
4040
4029 mutex_unlock(&cgroup_mutex); 4041 mutex_unlock(&cgroup_mutex);
4030 mutex_unlock(&cgroup_tree_mutex); 4042 mutex_unlock(&cgroup_tree_mutex);
4031} 4043}