aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-06-05 11:54:12 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-06-05 11:54:12 -0700
commit365f0e173f44aad979c464eb8250f6138a9911ef (patch)
treecec00f56b5ab382df2701e06dc02bdfd04dbd628
parent690efa08e268a36df170bee79df93a9649c3f217 (diff)
parentfa980ca87d15bb8a1317853f257a505990f3ffde (diff)
Merge branch 'for-3.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup fix from Tejun Heo: "This fixes the possible premature superblock release on umount bug mentioned during v3.5-rc1 pull request. Originally, cgroup dentry destruction path assumed that cgroup dentry didn't have any reference left after cgroup removal thus put super during dentry removal. Now that there can be lingering dentry references, this led to super being put with live dentries. This patch fixes the problem by putting super ref on dentry release instead of removal." * 'for-3.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup: cgroup: superblock can't be released with active dentries
-rw-r--r--kernel/cgroup.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 0f3527d6184..72fcd3069a9 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -896,10 +896,13 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode)
mutex_unlock(&cgroup_mutex);
/*
- * Drop the active superblock reference that we took when we
- * created the cgroup
+ * We want to drop the active superblock reference from the
+ * cgroup creation after all the dentry refs are gone -
+ * kill_sb gets mighty unhappy otherwise. Mark
+ * dentry->d_fsdata with cgroup_diput() to tell
+ * cgroup_d_release() to call deactivate_super().
*/
- deactivate_super(cgrp->root->sb);
+ dentry->d_fsdata = cgroup_diput;
/*
* if we're getting rid of the cgroup, refcount should ensure
@@ -925,6 +928,13 @@ static int cgroup_delete(const struct dentry *d)
return 1;
}
+static void cgroup_d_release(struct dentry *dentry)
+{
+ /* did cgroup_diput() tell me to deactivate super? */
+ if (dentry->d_fsdata == cgroup_diput)
+ deactivate_super(dentry->d_sb);
+}
+
static void remove_dir(struct dentry *d)
{
struct dentry *parent = dget(d->d_parent);
@@ -1532,6 +1542,7 @@ static int cgroup_get_rootdir(struct super_block *sb)
static const struct dentry_operations cgroup_dops = {
.d_iput = cgroup_diput,
.d_delete = cgroup_delete,
+ .d_release = cgroup_d_release,
};
struct inode *inode =