Commit 8e3f6541 authored by Tejun Heo's avatar Tejun Heo

cgroup: implement cgroup_add_cftypes() and friends

Currently, cgroup directories are populated by subsys->populate()
callback explicitly creating files on each cgroup creation.  This
level of flexibility isn't needed or desirable.  It provides largely
unused flexibility which call for abuses while severely limiting what
the core layer can do through the lack of structure and conventions.

Per each cgroup file type, the only distinction that cgroup users is
making is whether a cgroup is root or not, which can easily be
expressed with flags.

This patch introduces cgroup_add_cftypes().  These deal with cftypes
instead of individual files - controllers indicate that certain types
of files exist for certain subsystem.  Newly added CFTYPE_*_ON_ROOT
flags indicate whether a cftype should be excluded or created only on
the root cgroup.

cgroup_add_cftypes() can be called any time whether the target
subsystem is currently attached or not.  cgroup core will create files
on the existing cgroups as necessary.

Also, cgroup_subsys->base_cftypes is added to ease registration of the
base files for the subsystem.  If non-NULL on subsys init, the cftypes
pointed to by ->base_cftypes are automatically registered on subsys
init / load.

Further patches will convert the existing users and remove the file
based interface.  Note that this interface allows dynamic addition of
files to an active controller.  This will be used for sub-controller
modularity and unified hierarchy in the longer term.

This patch implements the new mechanism but doesn't apply it to any
user.

v2: replaced DECLARE_CGROUP_CFTYPES[_COND]() with
    cgroup_subsys->base_cftypes, which works better for cgroup_subsys
    which is loaded as module.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
parent b0ca5a84
...@@ -192,6 +192,7 @@ struct cgroup { ...@@ -192,6 +192,7 @@ struct cgroup {
struct list_head css_sets; struct list_head css_sets;
struct list_head allcg_node; /* cgroupfs_root->allcg_list */ struct list_head allcg_node; /* cgroupfs_root->allcg_list */
struct list_head cft_q_node; /* used during cftype add/rm */
/* /*
* Linked list running through all cgroups that can * Linked list running through all cgroups that can
...@@ -277,11 +278,17 @@ struct cgroup_map_cb { ...@@ -277,11 +278,17 @@ struct cgroup_map_cb {
* - the 'cftype' of the file is file->f_dentry->d_fsdata * - the 'cftype' of the file is file->f_dentry->d_fsdata
*/ */
#define MAX_CFTYPE_NAME 64 /* cftype->flags */
#define CFTYPE_ONLY_ON_ROOT (1U << 0) /* only create on root cg */
#define CFTYPE_NOT_ON_ROOT (1U << 1) /* don't create onp root cg */
#define MAX_CFTYPE_NAME 64
struct cftype { struct cftype {
/* /*
* By convention, the name should begin with the name of the * By convention, the name should begin with the name of the
* subsystem, followed by a period * subsystem, followed by a period. Zero length string indicates
* end of cftype array.
*/ */
char name[MAX_CFTYPE_NAME]; char name[MAX_CFTYPE_NAME];
int private; int private;
...@@ -297,6 +304,9 @@ struct cftype { ...@@ -297,6 +304,9 @@ struct cftype {
*/ */
size_t max_write_len; size_t max_write_len;
/* CFTYPE_* flags */
unsigned int flags;
int (*open)(struct inode *inode, struct file *file); int (*open)(struct inode *inode, struct file *file);
ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft,
struct file *file, struct file *file,
...@@ -375,6 +385,16 @@ struct cftype { ...@@ -375,6 +385,16 @@ struct cftype {
struct eventfd_ctx *eventfd); struct eventfd_ctx *eventfd);
}; };
/*
* cftype_sets describe cftypes belonging to a subsystem and are chained at
* cgroup_subsys->cftsets. Each cftset points to an array of cftypes
* terminated by zero length name.
*/
struct cftype_set {
struct list_head node; /* chained at subsys->cftsets */
const struct cftype *cfts;
};
struct cgroup_scanner { struct cgroup_scanner {
struct cgroup *cg; struct cgroup *cg;
int (*test_task)(struct task_struct *p, struct cgroup_scanner *scan); int (*test_task)(struct task_struct *p, struct cgroup_scanner *scan);
...@@ -400,6 +420,8 @@ int cgroup_add_files(struct cgroup *cgrp, ...@@ -400,6 +420,8 @@ int cgroup_add_files(struct cgroup *cgrp,
const struct cftype cft[], const struct cftype cft[],
int count); int count);
int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts);
int cgroup_is_removed(const struct cgroup *cgrp); int cgroup_is_removed(const struct cgroup *cgrp);
int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen);
...@@ -502,6 +524,13 @@ struct cgroup_subsys { ...@@ -502,6 +524,13 @@ struct cgroup_subsys {
struct idr idr; struct idr idr;
spinlock_t id_lock; spinlock_t id_lock;
/* list of cftype_sets */
struct list_head cftsets;
/* base cftypes, automatically [de]registered with subsys itself */
struct cftype *base_cftypes;
struct cftype_set base_cftset;
/* should be defined only by modular subsystems */ /* should be defined only by modular subsystems */
struct module *module; struct module *module;
}; };
......
...@@ -2623,8 +2623,14 @@ int cgroup_add_file(struct cgroup *cgrp, ...@@ -2623,8 +2623,14 @@ int cgroup_add_file(struct cgroup *cgrp,
struct dentry *dentry; struct dentry *dentry;
int error; int error;
umode_t mode; umode_t mode;
char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 }; char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
/* does @cft->flags tell us to skip creation on @cgrp? */
if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent)
return 0;
if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgrp->parent)
return 0;
if (subsys && !test_bit(ROOT_NOPREFIX, &cgrp->root->flags)) { if (subsys && !test_bit(ROOT_NOPREFIX, &cgrp->root->flags)) {
strcpy(name, subsys->name); strcpy(name, subsys->name);
strcat(name, "."); strcat(name, ".");
...@@ -2660,6 +2666,95 @@ int cgroup_add_files(struct cgroup *cgrp, ...@@ -2660,6 +2666,95 @@ int cgroup_add_files(struct cgroup *cgrp,
} }
EXPORT_SYMBOL_GPL(cgroup_add_files); EXPORT_SYMBOL_GPL(cgroup_add_files);
static DEFINE_MUTEX(cgroup_cft_mutex);
static void cgroup_cfts_prepare(void)
__acquires(&cgroup_cft_mutex) __acquires(&cgroup_mutex)
{
/*
* Thanks to the entanglement with vfs inode locking, we can't walk
* the existing cgroups under cgroup_mutex and create files.
* Instead, we increment reference on all cgroups and build list of
* them using @cgrp->cft_q_node. Grab cgroup_cft_mutex to ensure
* exclusive access to the field.
*/
mutex_lock(&cgroup_cft_mutex);
mutex_lock(&cgroup_mutex);
}
static void cgroup_cfts_commit(struct cgroup_subsys *ss,
const struct cftype *cfts)
__releases(&cgroup_mutex) __releases(&cgroup_cft_mutex)
{
LIST_HEAD(pending);
struct cgroup *cgrp, *n;
int count = 0;
while (cfts[count].name[0] != '\0')
count++;
/* %NULL @cfts indicates abort and don't bother if @ss isn't attached */
if (cfts && ss->root != &rootnode) {
list_for_each_entry(cgrp, &ss->root->allcg_list, allcg_node) {
dget(cgrp->dentry);
list_add_tail(&cgrp->cft_q_node, &pending);
}
}
mutex_unlock(&cgroup_mutex);
/*
* All new cgroups will see @cfts update on @ss->cftsets. Add/rm
* files for all cgroups which were created before.
*/
list_for_each_entry_safe(cgrp, n, &pending, cft_q_node) {
struct inode *inode = cgrp->dentry->d_inode;
mutex_lock(&inode->i_mutex);
mutex_lock(&cgroup_mutex);
if (!cgroup_is_removed(cgrp))
cgroup_add_files(cgrp, ss, cfts, count);
mutex_unlock(&cgroup_mutex);
mutex_unlock(&inode->i_mutex);
list_del_init(&cgrp->cft_q_node);
dput(cgrp->dentry);
}
mutex_unlock(&cgroup_cft_mutex);
}
/**
* cgroup_add_cftypes - add an array of cftypes to a subsystem
* @ss: target cgroup subsystem
* @cfts: zero-length name terminated array of cftypes
*
* Register @cfts to @ss. Files described by @cfts are created for all
* existing cgroups to which @ss is attached and all future cgroups will
* have them too. This function can be called anytime whether @ss is
* attached or not.
*
* Returns 0 on successful registration, -errno on failure. Note that this
* function currently returns 0 as long as @cfts registration is successful
* even if some file creation attempts on existing cgroups fail.
*/
int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts)
{
struct cftype_set *set;
set = kzalloc(sizeof(*set), GFP_KERNEL);
if (!set)
return -ENOMEM;
cgroup_cfts_prepare();
set->cfts = cfts;
list_add_tail(&set->node, &ss->cftsets);
cgroup_cfts_commit(ss, cfts);
return 0;
}
EXPORT_SYMBOL_GPL(cgroup_add_cftypes);
/** /**
* cgroup_task_count - count the number of tasks in a cgroup. * cgroup_task_count - count the number of tasks in a cgroup.
* @cgrp: the cgroup in question * @cgrp: the cgroup in question
...@@ -3660,10 +3755,25 @@ static int cgroup_populate_dir(struct cgroup *cgrp) ...@@ -3660,10 +3755,25 @@ static int cgroup_populate_dir(struct cgroup *cgrp)
return err; return err;
} }
/* process cftsets of each subsystem */
for_each_subsys(cgrp->root, ss) { for_each_subsys(cgrp->root, ss) {
struct cftype_set *set;
if (ss->populate && (err = ss->populate(ss, cgrp)) < 0) if (ss->populate && (err = ss->populate(ss, cgrp)) < 0)
return err; return err;
list_for_each_entry(set, &ss->cftsets, node) {
const struct cftype *cft;
for (cft = set->cfts; cft->name[0] != '\0'; cft++) {
err = cgroup_add_file(cgrp, ss, cft);
if (err)
pr_warning("cgroup_populate_dir: failed to create %s, err=%d\n",
cft->name, err);
}
}
} }
/* This cgroup is ready now */ /* This cgroup is ready now */
for_each_subsys(cgrp->root, ss) { for_each_subsys(cgrp->root, ss) {
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];
...@@ -4034,12 +4144,29 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) ...@@ -4034,12 +4144,29 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
return 0; return 0;
} }
static void __init_or_module cgroup_init_cftsets(struct cgroup_subsys *ss)
{
INIT_LIST_HEAD(&ss->cftsets);
/*
* base_cftset is embedded in subsys itself, no need to worry about
* deregistration.
*/
if (ss->base_cftypes) {
ss->base_cftset.cfts = ss->base_cftypes;
list_add_tail(&ss->base_cftset.node, &ss->cftsets);
}
}
static void __init cgroup_init_subsys(struct cgroup_subsys *ss) static void __init cgroup_init_subsys(struct cgroup_subsys *ss)
{ {
struct cgroup_subsys_state *css; struct cgroup_subsys_state *css;
printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name); printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name);
/* init base cftset */
cgroup_init_cftsets(ss);
/* Create the top cgroup state for this subsystem */ /* Create the top cgroup state for this subsystem */
list_add(&ss->sibling, &rootnode.subsys_list); list_add(&ss->sibling, &rootnode.subsys_list);
ss->root = &rootnode; ss->root = &rootnode;
...@@ -4109,6 +4236,9 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) ...@@ -4109,6 +4236,9 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss)
return 0; return 0;
} }
/* init base cftset */
cgroup_init_cftsets(ss);
/* /*
* need to register a subsys id before anything else - for example, * need to register a subsys id before anything else - for example,
* init_cgroup_css needs it. * init_cgroup_css needs it.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment