Merge branch 'for-3.13-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup

Pull cgroup fixes from Tejun Heo:
 "Two fixes.  One fixes a bug in the error path of cgroup_create().  The
  other changes cgrp->id lifetime rule so that the id doesn't get
  recycled before all controller states are destroyed.  This premature
  id recycling made memcg malfunction"

* 'for-3.13-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup:
  cgroup: don't recycle cgroup id until all csses' have been destroyed
  cgroup: fix cgroup_create() error handling path
This commit is contained in:
Linus Torvalds 2013-12-24 09:49:20 -08:00
commit 70e672fa73
1 changed files with 32 additions and 18 deletions

View File

@ -890,6 +890,16 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
struct cgroup *cgrp = dentry->d_fsdata; struct cgroup *cgrp = dentry->d_fsdata;
BUG_ON(!(cgroup_is_dead(cgrp))); BUG_ON(!(cgroup_is_dead(cgrp)));
/*
* XXX: cgrp->id is only used to look up css's. As cgroup
* and css's lifetimes will be decoupled, it should be made
* per-subsystem and moved to css->id so that lookups are
* successful until the target css is released.
*/
idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
cgrp->id = -1;
call_rcu(&cgrp->rcu_head, cgroup_free_rcu); call_rcu(&cgrp->rcu_head, cgroup_free_rcu);
} else { } else {
struct cfent *cfe = __d_cfe(dentry); struct cfent *cfe = __d_cfe(dentry);
@ -4268,6 +4278,7 @@ static void css_release(struct percpu_ref *ref)
struct cgroup_subsys_state *css = struct cgroup_subsys_state *css =
container_of(ref, struct cgroup_subsys_state, refcnt); container_of(ref, struct cgroup_subsys_state, refcnt);
rcu_assign_pointer(css->cgroup->subsys[css->ss->subsys_id], NULL);
call_rcu(&css->rcu_head, css_free_rcu_fn); call_rcu(&css->rcu_head, css_free_rcu_fn);
} }
@ -4426,14 +4437,6 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children); list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children);
root->number_of_cgroups++; root->number_of_cgroups++;
/* each css holds a ref to the cgroup's dentry and the parent css */
for_each_root_subsys(root, ss) {
struct cgroup_subsys_state *css = css_ar[ss->subsys_id];
dget(dentry);
css_get(css->parent);
}
/* hold a ref to the parent's dentry */ /* hold a ref to the parent's dentry */
dget(parent->dentry); dget(parent->dentry);
@ -4445,6 +4448,13 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
if (err) if (err)
goto err_destroy; goto err_destroy;
/* each css holds a ref to the cgroup's dentry and parent css */
dget(dentry);
css_get(css->parent);
/* mark it consumed for error path */
css_ar[ss->subsys_id] = NULL;
if (ss->broken_hierarchy && !ss->warned_broken_hierarchy && if (ss->broken_hierarchy && !ss->warned_broken_hierarchy &&
parent->parent) { parent->parent) {
pr_warning("cgroup: %s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n", pr_warning("cgroup: %s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n",
@ -4491,6 +4501,14 @@ err_free_cgrp:
return err; return err;
err_destroy: err_destroy:
for_each_root_subsys(root, ss) {
struct cgroup_subsys_state *css = css_ar[ss->subsys_id];
if (css) {
percpu_ref_cancel_init(&css->refcnt);
ss->css_free(css);
}
}
cgroup_destroy_locked(cgrp); cgroup_destroy_locked(cgrp);
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
mutex_unlock(&dentry->d_inode->i_mutex); mutex_unlock(&dentry->d_inode->i_mutex);
@ -4652,8 +4670,12 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
* will be invoked to perform the rest of destruction once the * will be invoked to perform the rest of destruction once the
* percpu refs of all css's are confirmed to be killed. * percpu refs of all css's are confirmed to be killed.
*/ */
for_each_root_subsys(cgrp->root, ss) for_each_root_subsys(cgrp->root, ss) {
kill_css(cgroup_css(cgrp, ss)); struct cgroup_subsys_state *css = cgroup_css(cgrp, ss);
if (css)
kill_css(css);
}
/* /*
* Mark @cgrp dead. This prevents further task migration and child * Mark @cgrp dead. This prevents further task migration and child
@ -4722,14 +4744,6 @@ static void cgroup_destroy_css_killed(struct cgroup *cgrp)
/* delete this cgroup from parent->children */ /* delete this cgroup from parent->children */
list_del_rcu(&cgrp->sibling); list_del_rcu(&cgrp->sibling);
/*
* We should remove the cgroup object from idr before its grace
* period starts, so we won't be looking up a cgroup while the
* cgroup is being freed.
*/
idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
cgrp->id = -1;
dput(d); dput(d);
set_bit(CGRP_RELEASABLE, &parent->flags); set_bit(CGRP_RELEASABLE, &parent->flags);