mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
cgroups: support named cgroups hierarchies
To simplify referring to cgroup hierarchies in mount statements, and to allow disambiguation in the presence of empty hierarchies and multiply-bindable subsystems this patch adds support for naming a new cgroup hierarchy via the "name=" mount option A pre-existing hierarchy may be specified by either name or by subsystems; a hierarchy's name cannot be changed by a remount operation. Example usage: # To create a hierarchy called "foo" containing the "cpu" subsystem mount -t cgroup -oname=foo,cpu cgroup /mnt/cgroup1 # To mount the "foo" hierarchy on a second location mount -t cgroup -oname=foo cgroup /mnt/cgroup2 Signed-off-by: Paul Menage <menage@google.com> Reviewed-by: Li Zefan <lizf@cn.fujitsu.com> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Balbir Singh <balbir@in.ibm.com> Cc: Dhaval Giani <dhaval@linux.vnet.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
34f77a90f7
commit
c6d57f3312
2 changed files with 157 additions and 49 deletions
|
@ -408,6 +408,26 @@ You can attach the current shell task by echoing 0:
|
||||||
|
|
||||||
# echo 0 > tasks
|
# echo 0 > tasks
|
||||||
|
|
||||||
|
2.3 Mounting hierarchies by name
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Passing the name=<x> option when mounting a cgroups hierarchy
|
||||||
|
associates the given name with the hierarchy. This can be used when
|
||||||
|
mounting a pre-existing hierarchy, in order to refer to it by name
|
||||||
|
rather than by its set of active subsystems. Each hierarchy is either
|
||||||
|
nameless, or has a unique name.
|
||||||
|
|
||||||
|
The name should match [\w.-]+
|
||||||
|
|
||||||
|
When passing a name=<x> option for a new hierarchy, you need to
|
||||||
|
specify subsystems manually; the legacy behaviour of mounting all
|
||||||
|
subsystems when none are explicitly specified is not supported when
|
||||||
|
you give a subsystem a name.
|
||||||
|
|
||||||
|
The name of the subsystem appears as part of the hierarchy description
|
||||||
|
in /proc/mounts and /proc/<pid>/cgroups.
|
||||||
|
|
||||||
|
|
||||||
3. Kernel API
|
3. Kernel API
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
186
kernel/cgroup.c
186
kernel/cgroup.c
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/cgroup.h>
|
#include <linux/cgroup.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -60,6 +61,8 @@ static struct cgroup_subsys *subsys[] = {
|
||||||
#include <linux/cgroup_subsys.h>
|
#include <linux/cgroup_subsys.h>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MAX_CGROUP_ROOT_NAMELEN 64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A cgroupfs_root represents the root of a cgroup hierarchy,
|
* A cgroupfs_root represents the root of a cgroup hierarchy,
|
||||||
* and may be associated with a superblock to form an active
|
* and may be associated with a superblock to form an active
|
||||||
|
@ -94,6 +97,9 @@ struct cgroupfs_root {
|
||||||
|
|
||||||
/* The path to use for release notifications. */
|
/* The path to use for release notifications. */
|
||||||
char release_agent_path[PATH_MAX];
|
char release_agent_path[PATH_MAX];
|
||||||
|
|
||||||
|
/* The name for this hierarchy - may be empty */
|
||||||
|
char name[MAX_CGROUP_ROOT_NAMELEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -841,6 +847,8 @@ static int cgroup_show_options(struct seq_file *seq, struct vfsmount *vfs)
|
||||||
seq_puts(seq, ",noprefix");
|
seq_puts(seq, ",noprefix");
|
||||||
if (strlen(root->release_agent_path))
|
if (strlen(root->release_agent_path))
|
||||||
seq_printf(seq, ",release_agent=%s", root->release_agent_path);
|
seq_printf(seq, ",release_agent=%s", root->release_agent_path);
|
||||||
|
if (strlen(root->name))
|
||||||
|
seq_printf(seq, ",name=%s", root->name);
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -849,6 +857,9 @@ struct cgroup_sb_opts {
|
||||||
unsigned long subsys_bits;
|
unsigned long subsys_bits;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
char *release_agent;
|
char *release_agent;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
struct cgroupfs_root *new_root;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Convert a hierarchy specifier into a bitmask of subsystems and
|
/* Convert a hierarchy specifier into a bitmask of subsystems and
|
||||||
|
@ -863,9 +874,7 @@ static int parse_cgroupfs_options(char *data,
|
||||||
mask = ~(1UL << cpuset_subsys_id);
|
mask = ~(1UL << cpuset_subsys_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
opts->subsys_bits = 0;
|
memset(opts, 0, sizeof(*opts));
|
||||||
opts->flags = 0;
|
|
||||||
opts->release_agent = NULL;
|
|
||||||
|
|
||||||
while ((token = strsep(&o, ",")) != NULL) {
|
while ((token = strsep(&o, ",")) != NULL) {
|
||||||
if (!*token)
|
if (!*token)
|
||||||
|
@ -885,11 +894,33 @@ static int parse_cgroupfs_options(char *data,
|
||||||
/* Specifying two release agents is forbidden */
|
/* Specifying two release agents is forbidden */
|
||||||
if (opts->release_agent)
|
if (opts->release_agent)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
opts->release_agent = kzalloc(PATH_MAX, GFP_KERNEL);
|
opts->release_agent =
|
||||||
|
kstrndup(token + 14, PATH_MAX, GFP_KERNEL);
|
||||||
if (!opts->release_agent)
|
if (!opts->release_agent)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
strncpy(opts->release_agent, token + 14, PATH_MAX - 1);
|
} else if (!strncmp(token, "name=", 5)) {
|
||||||
opts->release_agent[PATH_MAX - 1] = 0;
|
int i;
|
||||||
|
const char *name = token + 5;
|
||||||
|
/* Can't specify an empty name */
|
||||||
|
if (!strlen(name))
|
||||||
|
return -EINVAL;
|
||||||
|
/* Must match [\w.-]+ */
|
||||||
|
for (i = 0; i < strlen(name); i++) {
|
||||||
|
char c = name[i];
|
||||||
|
if (isalnum(c))
|
||||||
|
continue;
|
||||||
|
if ((c == '.') || (c == '-') || (c == '_'))
|
||||||
|
continue;
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* Specifying two names is forbidden */
|
||||||
|
if (opts->name)
|
||||||
|
return -EINVAL;
|
||||||
|
opts->name = kstrndup(name,
|
||||||
|
MAX_CGROUP_ROOT_NAMELEN,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!opts->name)
|
||||||
|
return -ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
struct cgroup_subsys *ss;
|
struct cgroup_subsys *ss;
|
||||||
int i;
|
int i;
|
||||||
|
@ -916,7 +947,7 @@ static int parse_cgroupfs_options(char *data,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* We can't have an empty hierarchy */
|
/* We can't have an empty hierarchy */
|
||||||
if (!opts->subsys_bits)
|
if (!opts->subsys_bits && !opts->name)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -944,6 +975,12 @@ static int cgroup_remount(struct super_block *sb, int *flags, char *data)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't allow name to change at remount */
|
||||||
|
if (opts.name && strcmp(opts.name, root->name)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
ret = rebind_subsystems(root, opts.subsys_bits);
|
ret = rebind_subsystems(root, opts.subsys_bits);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
@ -955,6 +992,7 @@ static int cgroup_remount(struct super_block *sb, int *flags, char *data)
|
||||||
strcpy(root->release_agent_path, opts.release_agent);
|
strcpy(root->release_agent_path, opts.release_agent);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
kfree(opts.release_agent);
|
kfree(opts.release_agent);
|
||||||
|
kfree(opts.name);
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
mutex_unlock(&cgrp->dentry->d_inode->i_mutex);
|
mutex_unlock(&cgrp->dentry->d_inode->i_mutex);
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
@ -977,6 +1015,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
|
||||||
INIT_LIST_HEAD(&cgrp->pids_list);
|
INIT_LIST_HEAD(&cgrp->pids_list);
|
||||||
init_rwsem(&cgrp->pids_mutex);
|
init_rwsem(&cgrp->pids_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_cgroup_root(struct cgroupfs_root *root)
|
static void init_cgroup_root(struct cgroupfs_root *root)
|
||||||
{
|
{
|
||||||
struct cgroup *cgrp = &root->top_cgroup;
|
struct cgroup *cgrp = &root->top_cgroup;
|
||||||
|
@ -990,31 +1029,59 @@ static void init_cgroup_root(struct cgroupfs_root *root)
|
||||||
|
|
||||||
static int cgroup_test_super(struct super_block *sb, void *data)
|
static int cgroup_test_super(struct super_block *sb, void *data)
|
||||||
{
|
{
|
||||||
struct cgroupfs_root *new = data;
|
struct cgroup_sb_opts *opts = data;
|
||||||
struct cgroupfs_root *root = sb->s_fs_info;
|
struct cgroupfs_root *root = sb->s_fs_info;
|
||||||
|
|
||||||
/* First check subsystems */
|
/* If we asked for a name then it must match */
|
||||||
if (new->subsys_bits != root->subsys_bits)
|
if (opts->name && strcmp(opts->name, root->name))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Next check flags */
|
/* If we asked for subsystems then they must match */
|
||||||
if (new->flags != root->flags)
|
if (opts->subsys_bits && (opts->subsys_bits != root->subsys_bits))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts)
|
||||||
|
{
|
||||||
|
struct cgroupfs_root *root;
|
||||||
|
|
||||||
|
/* Empty hierarchies aren't supported */
|
||||||
|
if (!opts->subsys_bits)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
root = kzalloc(sizeof(*root), GFP_KERNEL);
|
||||||
|
if (!root)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
init_cgroup_root(root);
|
||||||
|
root->subsys_bits = opts->subsys_bits;
|
||||||
|
root->flags = opts->flags;
|
||||||
|
if (opts->release_agent)
|
||||||
|
strcpy(root->release_agent_path, opts->release_agent);
|
||||||
|
if (opts->name)
|
||||||
|
strcpy(root->name, opts->name);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
static int cgroup_set_super(struct super_block *sb, void *data)
|
static int cgroup_set_super(struct super_block *sb, void *data)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct cgroupfs_root *root = data;
|
struct cgroup_sb_opts *opts = data;
|
||||||
|
|
||||||
|
/* If we don't have a new root, we can't set up a new sb */
|
||||||
|
if (!opts->new_root)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
BUG_ON(!opts->subsys_bits);
|
||||||
|
|
||||||
ret = set_anon_super(sb, NULL);
|
ret = set_anon_super(sb, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
sb->s_fs_info = root;
|
sb->s_fs_info = opts->new_root;
|
||||||
root->sb = sb;
|
opts->new_root->sb = sb;
|
||||||
|
|
||||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||||
|
@ -1051,48 +1118,43 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
||||||
void *data, struct vfsmount *mnt)
|
void *data, struct vfsmount *mnt)
|
||||||
{
|
{
|
||||||
struct cgroup_sb_opts opts;
|
struct cgroup_sb_opts opts;
|
||||||
|
struct cgroupfs_root *root;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
struct cgroupfs_root *root;
|
struct cgroupfs_root *new_root;
|
||||||
struct list_head tmp_cg_links;
|
|
||||||
|
|
||||||
/* First find the desired set of subsystems */
|
/* First find the desired set of subsystems */
|
||||||
ret = parse_cgroupfs_options(data, &opts);
|
ret = parse_cgroupfs_options(data, &opts);
|
||||||
if (ret) {
|
if (ret)
|
||||||
kfree(opts.release_agent);
|
goto out_err;
|
||||||
return ret;
|
|
||||||
|
/*
|
||||||
|
* Allocate a new cgroup root. We may not need it if we're
|
||||||
|
* reusing an existing hierarchy.
|
||||||
|
*/
|
||||||
|
new_root = cgroup_root_from_opts(&opts);
|
||||||
|
if (IS_ERR(new_root)) {
|
||||||
|
ret = PTR_ERR(new_root);
|
||||||
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
opts.new_root = new_root;
|
||||||
|
|
||||||
root = kzalloc(sizeof(*root), GFP_KERNEL);
|
/* Locate an existing or new sb for this hierarchy */
|
||||||
if (!root) {
|
sb = sget(fs_type, cgroup_test_super, cgroup_set_super, &opts);
|
||||||
kfree(opts.release_agent);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
init_cgroup_root(root);
|
|
||||||
root->subsys_bits = opts.subsys_bits;
|
|
||||||
root->flags = opts.flags;
|
|
||||||
if (opts.release_agent) {
|
|
||||||
strcpy(root->release_agent_path, opts.release_agent);
|
|
||||||
kfree(opts.release_agent);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb = sget(fs_type, cgroup_test_super, cgroup_set_super, root);
|
|
||||||
|
|
||||||
if (IS_ERR(sb)) {
|
if (IS_ERR(sb)) {
|
||||||
kfree(root);
|
ret = PTR_ERR(sb);
|
||||||
return PTR_ERR(sb);
|
kfree(opts.new_root);
|
||||||
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sb->s_fs_info != root) {
|
root = sb->s_fs_info;
|
||||||
/* Reusing an existing superblock */
|
BUG_ON(!root);
|
||||||
BUG_ON(sb->s_root == NULL);
|
if (root == opts.new_root) {
|
||||||
kfree(root);
|
/* We used the new root structure, so this is a new hierarchy */
|
||||||
root = NULL;
|
struct list_head tmp_cg_links;
|
||||||
} else {
|
|
||||||
/* New superblock */
|
|
||||||
struct cgroup *root_cgrp = &root->top_cgroup;
|
struct cgroup *root_cgrp = &root->top_cgroup;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct cgroupfs_root *existing_root;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUG_ON(sb->s_root != NULL);
|
BUG_ON(sb->s_root != NULL);
|
||||||
|
@ -1105,6 +1167,18 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
mutex_lock(&cgroup_mutex);
|
mutex_lock(&cgroup_mutex);
|
||||||
|
|
||||||
|
if (strlen(root->name)) {
|
||||||
|
/* Check for name clashes with existing mounts */
|
||||||
|
for_each_active_root(existing_root) {
|
||||||
|
if (!strcmp(existing_root->name, root->name)) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
mutex_unlock(&cgroup_mutex);
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
goto drop_new_super;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We're accessing css_set_count without locking
|
* We're accessing css_set_count without locking
|
||||||
* css_set_lock here, but that's OK - it can only be
|
* css_set_lock here, but that's OK - it can only be
|
||||||
|
@ -1123,7 +1197,8 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
||||||
if (ret == -EBUSY) {
|
if (ret == -EBUSY) {
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
goto free_cg_links;
|
free_cg_links(&tmp_cg_links);
|
||||||
|
goto drop_new_super;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EBUSY should be the only error here */
|
/* EBUSY should be the only error here */
|
||||||
|
@ -1157,15 +1232,25 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
|
||||||
cgroup_populate_dir(root_cgrp);
|
cgroup_populate_dir(root_cgrp);
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We re-used an existing hierarchy - the new root (if
|
||||||
|
* any) is not needed
|
||||||
|
*/
|
||||||
|
kfree(opts.new_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
simple_set_mnt(mnt, sb);
|
simple_set_mnt(mnt, sb);
|
||||||
|
kfree(opts.release_agent);
|
||||||
|
kfree(opts.name);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_cg_links:
|
|
||||||
free_cg_links(&tmp_cg_links);
|
|
||||||
drop_new_super:
|
drop_new_super:
|
||||||
deactivate_locked_super(sb);
|
deactivate_locked_super(sb);
|
||||||
|
out_err:
|
||||||
|
kfree(opts.release_agent);
|
||||||
|
kfree(opts.name);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2992,6 +3077,9 @@ static int proc_cgroup_show(struct seq_file *m, void *v)
|
||||||
seq_printf(m, "%lu:", root->subsys_bits);
|
seq_printf(m, "%lu:", root->subsys_bits);
|
||||||
for_each_subsys(root, ss)
|
for_each_subsys(root, ss)
|
||||||
seq_printf(m, "%s%s", count++ ? "," : "", ss->name);
|
seq_printf(m, "%s%s", count++ ? "," : "", ss->name);
|
||||||
|
if (strlen(root->name))
|
||||||
|
seq_printf(m, "%sname=%s", count ? "," : "",
|
||||||
|
root->name);
|
||||||
seq_putc(m, ':');
|
seq_putc(m, ':');
|
||||||
get_first_subsys(&root->top_cgroup, NULL, &subsys_id);
|
get_first_subsys(&root->top_cgroup, NULL, &subsys_id);
|
||||||
cgrp = task_cgroup(tsk, subsys_id);
|
cgrp = task_cgroup(tsk, subsys_id);
|
||||||
|
|
Loading…
Reference in a new issue