Skip to content

Commit

Permalink
cgroup: introduce struct cfent
Browse files Browse the repository at this point in the history
This patch adds cfent (cgroup file entry) which is the association
between a cgroup and a file.  This is in-cgroup representation of
files under a cgroup directory.  This simplifies walking walking
cgroup files and thus cgroup_clear_directory(), which is now
implemented in two parts - cgroup_rm_file() and a loop around it.

cgroup_rm_file() will be used to implement cftype removal and cfent is
scheduled to serve cgroup specific per-file data (e.g. for sysfs-like
"sever" semantics).

v2: - cfe was freed from cgroup_rm_file() which led to use-after-free
      if the file had openers at the time of removal.  Moved to
      cgroup_diput().

    - cgroup_clear_directory() triggered WARN_ON_ONCE() if d_subdirs
      wasn't empty after removing all files.  This triggered
      spuriously if some files were open during directory clearing.
      Removed.

v3: - In cgroup_diput(), WARN_ONCE(!list_empty(&cfe->node)) could be
      spuriously triggered for root cgroups because they don't go
      through cgroup_clear_directory() on unmount.  Don't trigger WARN
      for root cgroups.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Glauber Costa <glommer@parallels.com>
  • Loading branch information
Tejun Heo committed Apr 1, 2012
1 parent f6ea937 commit 05ef1d7
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 36 deletions.
1 change: 1 addition & 0 deletions include/linux/cgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ struct cgroup {
*/
struct list_head sibling; /* my parent's children */
struct list_head children; /* my children */
struct list_head files; /* my files */

struct cgroup *parent; /* my parent */
struct dentry __rcu *dentry; /* cgroup fs entry, RCU protected */
Expand Down
113 changes: 77 additions & 36 deletions kernel/cgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ struct cgroupfs_root {
*/
static struct cgroupfs_root rootnode;

/*
* cgroupfs file entry, pointed to from leaf dentry->d_fsdata.
*/
struct cfent {
struct list_head node;
struct dentry *dentry;
struct cftype *type;
};

/*
* CSS ID -- ID per subsys's Cgroup Subsys State(CSS). used only when
* cgroup_subsys->use_id != 0.
Expand Down Expand Up @@ -287,11 +296,16 @@ static inline struct cgroup *__d_cgrp(struct dentry *dentry)
return dentry->d_fsdata;
}

static inline struct cftype *__d_cft(struct dentry *dentry)
static inline struct cfent *__d_cfe(struct dentry *dentry)
{
return dentry->d_fsdata;
}

static inline struct cftype *__d_cft(struct dentry *dentry)
{
return __d_cfe(dentry)->type;
}

/* the list of cgroups eligible for automatic release. Protected by
* release_list_lock */
static LIST_HEAD(release_list);
Expand Down Expand Up @@ -877,6 +891,14 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
BUG_ON(!list_empty(&cgrp->pidlists));

kfree_rcu(cgrp, rcu_head);
} else {
struct cfent *cfe = __d_cfe(dentry);
struct cgroup *cgrp = dentry->d_parent->d_fsdata;

WARN_ONCE(!list_empty(&cfe->node) &&
cgrp != &cgrp->root->top_cgroup,
"cfe still linked for %s\n", cfe->type->name);
kfree(cfe);
}
iput(inode);
}
Expand All @@ -895,34 +917,36 @@ static void remove_dir(struct dentry *d)
dput(parent);
}

static void cgroup_clear_directory(struct dentry *dentry)
{
struct list_head *node;

BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
spin_lock(&dentry->d_lock);
node = dentry->d_subdirs.next;
while (node != &dentry->d_subdirs) {
struct dentry *d = list_entry(node, struct dentry, d_u.d_child);

spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
list_del_init(node);
if (d->d_inode) {
/* This should never be called on a cgroup
* directory with child cgroups */
BUG_ON(d->d_inode->i_mode & S_IFDIR);
dget_dlock(d);
spin_unlock(&d->d_lock);
spin_unlock(&dentry->d_lock);
d_delete(d);
simple_unlink(dentry->d_inode, d);
dput(d);
spin_lock(&dentry->d_lock);
} else
spin_unlock(&d->d_lock);
node = dentry->d_subdirs.next;
static int cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
{
struct cfent *cfe;

lockdep_assert_held(&cgrp->dentry->d_inode->i_mutex);
lockdep_assert_held(&cgroup_mutex);

list_for_each_entry(cfe, &cgrp->files, node) {
struct dentry *d = cfe->dentry;

if (cft && cfe->type != cft)
continue;

dget(d);
d_delete(d);
simple_unlink(d->d_inode, d);
list_del_init(&cfe->node);
dput(d);

return 0;
}
spin_unlock(&dentry->d_lock);
return -ENOENT;
}

static void cgroup_clear_directory(struct dentry *dir)
{
struct cgroup *cgrp = __d_cgrp(dir);

while (!list_empty(&cgrp->files))
cgroup_rm_file(cgrp, NULL);
}

/*
Expand Down Expand Up @@ -1352,6 +1376,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
{
INIT_LIST_HEAD(&cgrp->sibling);
INIT_LIST_HEAD(&cgrp->children);
INIT_LIST_HEAD(&cgrp->files);
INIT_LIST_HEAD(&cgrp->css_sets);
INIT_LIST_HEAD(&cgrp->release_list);
INIT_LIST_HEAD(&cgrp->pidlists);
Expand Down Expand Up @@ -2619,7 +2644,9 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
const struct cftype *cft)
{
struct dentry *dir = cgrp->dentry;
struct cgroup *parent = __d_cgrp(dir);
struct dentry *dentry;
struct cfent *cfe;
int error;
umode_t mode;
char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 };
Expand All @@ -2635,17 +2662,31 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys,
strcat(name, ".");
}
strcat(name, cft->name);

BUG_ON(!mutex_is_locked(&dir->d_inode->i_mutex));

cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
if (!cfe)
return -ENOMEM;

dentry = lookup_one_len(name, dir, strlen(name));
if (!IS_ERR(dentry)) {
mode = cgroup_file_mode(cft);
error = cgroup_create_file(dentry, mode | S_IFREG,
cgrp->root->sb);
if (!error)
dentry->d_fsdata = (void *)cft;
dput(dentry);
} else
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out;
}

mode = cgroup_file_mode(cft);
error = cgroup_create_file(dentry, mode | S_IFREG, cgrp->root->sb);
if (!error) {
cfe->type = (void *)cft;
cfe->dentry = dentry;
dentry->d_fsdata = cfe;
list_add_tail(&cfe->node, &parent->files);
cfe = NULL;
}
dput(dentry);
out:
kfree(cfe);
return error;
}

Expand Down

0 comments on commit 05ef1d7

Please sign in to comment.