Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 339310
b: refs/heads/master
c: ed95779
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo committed Nov 5, 2012
1 parent 7237edd commit 1cafddb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 108 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2ef37d3fe474b218e170010a59066e19427c9847
refs/heads/master: ed95779340b50e362245c81b5dec0d11a1debfa8
12 changes: 0 additions & 12 deletions trunk/include/linux/cgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ struct cgroup_subsys_state {
enum {
CSS_ROOT, /* This CSS is the root of the subsystem */
CSS_REMOVED, /* This CSS is dead */
CSS_CLEAR_CSS_REFS, /* @ss->__DEPRECATED_clear_css_refs */
};

/* Caller must verify that the css is not for root cgroup */
Expand Down Expand Up @@ -485,17 +484,6 @@ struct cgroup_subsys {
*/
bool use_id;

/*
* If %true, cgroup removal will try to clear css refs by retrying
* ss->pre_destroy() until there's no css ref left. This behavior
* is strictly for backward compatibility and will be removed as
* soon as the current user (memcg) is updated.
*
* If %false, ss->pre_destroy() can't fail and cgroup removal won't
* wait for css refs to drop to zero before proceeding.
*/
bool __DEPRECATED_clear_css_refs;

#define MAX_CGROUP_TYPE_NAMELEN 32
const char *name;

Expand Down
131 changes: 36 additions & 95 deletions trunk/kernel/cgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,11 +865,8 @@ static int cgroup_call_pre_destroy(struct cgroup *cgrp)
continue;

ret = ss->pre_destroy(cgrp);
if (ret) {
/* ->pre_destroy() failure is being deprecated */
WARN_ON_ONCE(!ss->__DEPRECATED_clear_css_refs);
if (WARN_ON_ONCE(ret))
break;
}
}

return ret;
Expand Down Expand Up @@ -3901,14 +3898,12 @@ static void init_cgroup_css(struct cgroup_subsys_state *css,
cgrp->subsys[ss->subsys_id] = css;

/*
* If !clear_css_refs, css holds an extra ref to @cgrp->dentry
* which is put on the last css_put(). dput() requires process
* context, which css_put() may be called without. @css->dput_work
* will be used to invoke dput() asynchronously from css_put().
* css holds an extra ref to @cgrp->dentry which is put on the last
* css_put(). dput() requires process context, which css_put() may
* be called without. @css->dput_work will be used to invoke
* dput() asynchronously from css_put().
*/
INIT_WORK(&css->dput_work, css_dput_fn);
if (ss->__DEPRECATED_clear_css_refs)
set_bit(CSS_CLEAR_CSS_REFS, &css->flags);
}

/*
Expand Down Expand Up @@ -3978,10 +3973,9 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry,
if (err < 0)
goto err_remove;

/* If !clear_css_refs, each css holds a ref to the cgroup's dentry */
/* each css holds a ref to the cgroup's dentry */
for_each_subsys(root, ss)
if (!ss->__DEPRECATED_clear_css_refs)
dget(dentry);
dget(dentry);

/* The cgroup directory was pre-locked for us */
BUG_ON(!mutex_is_locked(&cgrp->dentry->d_inode->i_mutex));
Expand Down Expand Up @@ -4066,82 +4060,17 @@ static int cgroup_has_css_refs(struct cgroup *cgrp)
return 0;
}

/*
* Atomically mark all (or else none) of the cgroup's CSS objects as
* CSS_REMOVED. Return true on success, or false if the cgroup has
* busy subsystems. Call with cgroup_mutex held
*
* Depending on whether a subsys has __DEPRECATED_clear_css_refs set or
* not, cgroup removal behaves differently.
*
* If clear is set, css refcnt for the subsystem should be zero before
* cgroup removal can be committed. This is implemented by
* CGRP_WAIT_ON_RMDIR and retry logic around ->pre_destroy(), which may be
* called multiple times until all css refcnts reach zero and is allowed to
* veto removal on any invocation. This behavior is deprecated and will be
* removed as soon as the existing user (memcg) is updated.
*
* If clear is not set, each css holds an extra reference to the cgroup's
* dentry and cgroup removal proceeds regardless of css refs.
* ->pre_destroy() will be called at least once and is not allowed to fail.
* On the last put of each css, whenever that may be, the extra dentry ref
* is put so that dentry destruction happens only after all css's are
* released.
*/
static int cgroup_clear_css_refs(struct cgroup *cgrp)
{
struct cgroup_subsys *ss;
unsigned long flags;
bool failed = false;

local_irq_save(flags);

/*
* Block new css_tryget() by deactivating refcnt. If all refcnts
* for subsystems w/ clear_css_refs set were 1 at the moment of
* deactivation, we succeeded.
*/
for_each_subsys(cgrp->root, ss) {
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];

WARN_ON(atomic_read(&css->refcnt) < 0);
atomic_add(CSS_DEACT_BIAS, &css->refcnt);

if (ss->__DEPRECATED_clear_css_refs)
failed |= css_refcnt(css) != 1;
}

/*
* If succeeded, set REMOVED and put all the base refs; otherwise,
* restore refcnts to positive values. Either way, all in-progress
* css_tryget() will be released.
*/
for_each_subsys(cgrp->root, ss) {
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];

if (!failed) {
set_bit(CSS_REMOVED, &css->flags);
css_put(css);
} else {
atomic_sub(CSS_DEACT_BIAS, &css->refcnt);
}
}

local_irq_restore(flags);
return !failed;
}

static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
{
struct cgroup *cgrp = dentry->d_fsdata;
struct dentry *d;
struct cgroup *parent;
DEFINE_WAIT(wait);
struct cgroup_event *event, *tmp;
struct cgroup_subsys *ss;
int ret;

/* the vfs holds both inode->i_mutex already */
again:
mutex_lock(&cgroup_mutex);
if (atomic_read(&cgrp->count) != 0) {
mutex_unlock(&cgroup_mutex);
Expand Down Expand Up @@ -4182,21 +4111,34 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry)
return -EBUSY;
}
prepare_to_wait(&cgroup_rmdir_waitq, &wait, TASK_INTERRUPTIBLE);
if (!cgroup_clear_css_refs(cgrp)) {
mutex_unlock(&cgroup_mutex);
/*
* Because someone may call cgroup_wakeup_rmdir_waiter() before
* prepare_to_wait(), we need to check this flag.
*/
if (test_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags))
schedule();
finish_wait(&cgroup_rmdir_waitq, &wait);
clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags);
if (signal_pending(current))
return -EINTR;
goto again;

local_irq_disable();

/* block new css_tryget() by deactivating refcnt */
for_each_subsys(cgrp->root, ss) {
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];

WARN_ON(atomic_read(&css->refcnt) < 0);
atomic_add(CSS_DEACT_BIAS, &css->refcnt);
}

/*
* Set REMOVED. All in-progress css_tryget() will be released.
* Put all the base refs. Each css holds an extra reference to the
* cgroup's dentry and cgroup removal proceeds regardless of css
* refs. On the last put of each css, whenever that may be, the
* extra dentry ref is put so that dentry destruction happens only
* after all css's are released.
*/
for_each_subsys(cgrp->root, ss) {
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];

set_bit(CSS_REMOVED, &css->flags);
css_put(css);
}
/* NO css_tryget() can success after here. */

local_irq_enable();

finish_wait(&cgroup_rmdir_waitq, &wait);
clear_bit(CGRP_WAIT_ON_RMDIR, &cgrp->flags);

Expand Down Expand Up @@ -4949,8 +4891,7 @@ void __css_put(struct cgroup_subsys_state *css)
cgroup_wakeup_rmdir_waiter(cgrp);
break;
case 0:
if (!test_bit(CSS_CLEAR_CSS_REFS, &css->flags))
schedule_work(&css->dput_work);
schedule_work(&css->dput_work);
break;
}
rcu_read_unlock();
Expand Down

0 comments on commit 1cafddb

Please sign in to comment.