|
|
@@ -1455,17 +1455,126 @@ static int cgroup_get_rootdir(struct super_block *sb)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int cgroup_setup_root(struct cgroupfs_root *root)
|
|
|
+{
|
|
|
+ LIST_HEAD(tmp_links);
|
|
|
+ struct super_block *sb = root->sb;
|
|
|
+ struct cgroup *root_cgrp = &root->top_cgroup;
|
|
|
+ struct cgroupfs_root *existing_root;
|
|
|
+ struct css_set *cset;
|
|
|
+ struct inode *inode;
|
|
|
+ const struct cred *cred;
|
|
|
+ int i, ret;
|
|
|
+
|
|
|
+ lockdep_assert_held(&cgroup_tree_mutex);
|
|
|
+ lockdep_assert_held(&cgroup_mutex);
|
|
|
+ BUG_ON(sb->s_root != NULL);
|
|
|
+
|
|
|
+ mutex_unlock(&cgroup_mutex);
|
|
|
+ mutex_unlock(&cgroup_tree_mutex);
|
|
|
+
|
|
|
+ ret = cgroup_get_rootdir(sb);
|
|
|
+ if (ret) {
|
|
|
+ mutex_lock(&cgroup_tree_mutex);
|
|
|
+ mutex_lock(&cgroup_mutex);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ inode = sb->s_root->d_inode;
|
|
|
+
|
|
|
+ mutex_lock(&inode->i_mutex);
|
|
|
+ mutex_lock(&cgroup_tree_mutex);
|
|
|
+ mutex_lock(&cgroup_mutex);
|
|
|
+
|
|
|
+ ret = idr_alloc(&root->cgroup_idr, root_cgrp, 0, 1, GFP_KERNEL);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_unlock;
|
|
|
+ root_cgrp->id = ret;
|
|
|
+
|
|
|
+ /* check for name clashes with existing mounts */
|
|
|
+ ret = -EBUSY;
|
|
|
+ if (strlen(root->name))
|
|
|
+ for_each_active_root(existing_root)
|
|
|
+ if (!strcmp(existing_root->name, root->name))
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We're accessing css_set_count without locking css_set_lock here,
|
|
|
+ * but that's OK - it can only be increased by someone holding
|
|
|
+ * cgroup_lock, and that's us. The worst that can happen is that we
|
|
|
+ * have some link structures left over
|
|
|
+ */
|
|
|
+ ret = allocate_cgrp_cset_links(css_set_count, &tmp_links);
|
|
|
+ if (ret)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ /* ID 0 is reserved for dummy root, 1 for unified hierarchy */
|
|
|
+ ret = cgroup_init_root_id(root, 2, 0);
|
|
|
+ if (ret)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ sb->s_root->d_fsdata = root_cgrp;
|
|
|
+ root_cgrp->dentry = sb->s_root;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We're inside get_sb() and will call lookup_one_len() to create
|
|
|
+ * the root files, which doesn't work if SELinux is in use. The
|
|
|
+ * following cred dancing somehow works around it. See 2ce9738ba
|
|
|
+ * ("cgroupfs: use init_cred when populating new cgroupfs mount")
|
|
|
+ * for more details.
|
|
|
+ */
|
|
|
+ cred = override_creds(&init_cred);
|
|
|
+
|
|
|
+ ret = cgroup_addrm_files(root_cgrp, cgroup_base_files, true);
|
|
|
+ if (ret)
|
|
|
+ goto rm_base_files;
|
|
|
+
|
|
|
+ ret = rebind_subsystems(root, root->subsys_mask, 0);
|
|
|
+ if (ret)
|
|
|
+ goto rm_base_files;
|
|
|
+
|
|
|
+ revert_creds(cred);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * There must be no failure case after here, since rebinding takes
|
|
|
+ * care of subsystems' refcounts, which are explicitly dropped in
|
|
|
+ * the failure exit path.
|
|
|
+ */
|
|
|
+ list_add(&root->root_list, &cgroup_roots);
|
|
|
+ cgroup_root_count++;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Link the top cgroup in this hierarchy into all the css_set
|
|
|
+ * objects.
|
|
|
+ */
|
|
|
+ write_lock(&css_set_lock);
|
|
|
+ hash_for_each(css_set_table, i, cset, hlist)
|
|
|
+ link_css_set(&tmp_links, cset, root_cgrp);
|
|
|
+ write_unlock(&css_set_lock);
|
|
|
+
|
|
|
+ BUG_ON(!list_empty(&root_cgrp->children));
|
|
|
+ BUG_ON(root->number_of_cgroups != 1);
|
|
|
+
|
|
|
+ ret = 0;
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+rm_base_files:
|
|
|
+ cgroup_addrm_files(&root->top_cgroup, cgroup_base_files, false);
|
|
|
+ revert_creds(cred);
|
|
|
+ cgroup_exit_root_id(root);
|
|
|
+out_unlock:
|
|
|
+ mutex_unlock(&inode->i_mutex);
|
|
|
+ free_cgrp_cset_links(&tmp_links);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
|
|
int flags, const char *unused_dev_name,
|
|
|
void *data)
|
|
|
{
|
|
|
- LIST_HEAD(tmp_links);
|
|
|
struct super_block *sb = NULL;
|
|
|
- struct inode *inode = NULL;
|
|
|
struct cgroupfs_root *root = NULL;
|
|
|
struct cgroup_sb_opts opts;
|
|
|
struct cgroupfs_root *new_root;
|
|
|
- const struct cred *cred;
|
|
|
int ret;
|
|
|
|
|
|
mutex_lock(&cgroup_tree_mutex);
|
|
|
@@ -1502,94 +1611,9 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
|
|
root = sb->s_fs_info;
|
|
|
BUG_ON(!root);
|
|
|
if (root == opts.new_root) {
|
|
|
- /* We used the new root structure, so this is a new hierarchy */
|
|
|
- struct cgroup *root_cgrp = &root->top_cgroup;
|
|
|
- struct cgroupfs_root *existing_root;
|
|
|
- int i;
|
|
|
- struct css_set *cset;
|
|
|
-
|
|
|
- BUG_ON(sb->s_root != NULL);
|
|
|
-
|
|
|
- mutex_unlock(&cgroup_mutex);
|
|
|
- mutex_unlock(&cgroup_tree_mutex);
|
|
|
-
|
|
|
- ret = cgroup_get_rootdir(sb);
|
|
|
+ ret = cgroup_setup_root(root);
|
|
|
if (ret)
|
|
|
goto out_unlock;
|
|
|
- inode = sb->s_root->d_inode;
|
|
|
-
|
|
|
- mutex_lock(&inode->i_mutex);
|
|
|
- mutex_lock(&cgroup_tree_mutex);
|
|
|
- mutex_lock(&cgroup_mutex);
|
|
|
-
|
|
|
- ret = idr_alloc(&root->cgroup_idr, root_cgrp, 0, 1, GFP_KERNEL);
|
|
|
- if (ret < 0)
|
|
|
- goto out_unlock;
|
|
|
- root_cgrp->id = ret;
|
|
|
-
|
|
|
- /* Check for name clashes with existing mounts */
|
|
|
- ret = -EBUSY;
|
|
|
- if (strlen(root->name))
|
|
|
- for_each_active_root(existing_root)
|
|
|
- if (!strcmp(existing_root->name, root->name))
|
|
|
- goto out_unlock;
|
|
|
-
|
|
|
- /*
|
|
|
- * We're accessing css_set_count without locking
|
|
|
- * css_set_lock here, but that's OK - it can only be
|
|
|
- * increased by someone holding cgroup_lock, and
|
|
|
- * that's us. The worst that can happen is that we
|
|
|
- * have some link structures left over
|
|
|
- */
|
|
|
- ret = allocate_cgrp_cset_links(css_set_count, &tmp_links);
|
|
|
- if (ret)
|
|
|
- goto out_unlock;
|
|
|
-
|
|
|
- /* ID 0 is reserved for dummy root, 1 for unified hierarchy */
|
|
|
- ret = cgroup_init_root_id(root, 2, 0);
|
|
|
- if (ret)
|
|
|
- goto out_unlock;
|
|
|
-
|
|
|
- sb->s_root->d_fsdata = root_cgrp;
|
|
|
- root_cgrp->dentry = sb->s_root;
|
|
|
-
|
|
|
- /*
|
|
|
- * We're inside get_sb() and will call lookup_one_len() to
|
|
|
- * create the root files, which doesn't work if SELinux is
|
|
|
- * in use. The following cred dancing somehow works around
|
|
|
- * it. See 2ce9738ba ("cgroupfs: use init_cred when
|
|
|
- * populating new cgroupfs mount") for more details.
|
|
|
- */
|
|
|
- cred = override_creds(&init_cred);
|
|
|
-
|
|
|
- ret = cgroup_addrm_files(root_cgrp, cgroup_base_files, true);
|
|
|
- if (ret)
|
|
|
- goto rm_base_files;
|
|
|
-
|
|
|
- ret = rebind_subsystems(root, root->subsys_mask, 0);
|
|
|
- if (ret)
|
|
|
- goto rm_base_files;
|
|
|
-
|
|
|
- revert_creds(cred);
|
|
|
-
|
|
|
- /*
|
|
|
- * There must be no failure case after here, since rebinding
|
|
|
- * takes care of subsystems' refcounts, which are explicitly
|
|
|
- * dropped in the failure exit path.
|
|
|
- */
|
|
|
-
|
|
|
- list_add(&root->root_list, &cgroup_roots);
|
|
|
- cgroup_root_count++;
|
|
|
-
|
|
|
- /* Link the top cgroup in this hierarchy into all
|
|
|
- * the css_set objects */
|
|
|
- write_lock(&css_set_lock);
|
|
|
- hash_for_each(css_set_table, i, cset, hlist)
|
|
|
- link_css_set(&tmp_links, cset, root_cgrp);
|
|
|
- write_unlock(&css_set_lock);
|
|
|
-
|
|
|
- BUG_ON(!list_empty(&root_cgrp->children));
|
|
|
- BUG_ON(root->number_of_cgroups != 1);
|
|
|
} else {
|
|
|
/*
|
|
|
* We re-used an existing hierarchy - the new root (if
|
|
|
@@ -1609,22 +1633,13 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
|
|
}
|
|
|
|
|
|
ret = 0;
|
|
|
- goto out_unlock;
|
|
|
-
|
|
|
-rm_base_files:
|
|
|
- cgroup_addrm_files(&root->top_cgroup, cgroup_base_files, false);
|
|
|
- revert_creds(cred);
|
|
|
- cgroup_exit_root_id(root);
|
|
|
out_unlock:
|
|
|
mutex_unlock(&cgroup_mutex);
|
|
|
mutex_unlock(&cgroup_tree_mutex);
|
|
|
- if (inode)
|
|
|
- mutex_unlock(&inode->i_mutex);
|
|
|
|
|
|
if (ret && !IS_ERR_OR_NULL(sb))
|
|
|
deactivate_locked_super(sb);
|
|
|
|
|
|
- free_cgrp_cset_links(&tmp_links);
|
|
|
kfree(opts.release_agent);
|
|
|
kfree(opts.name);
|
|
|
|