cgroup: fix cgroup hierarchy umount race
[firefly-linux-kernel-4.4.55.git] / kernel / cgroup.c
index 0f3527d6184a1597fb81f00a878df15eeab0ecf8..b303dfc7dce0703299c3e8840e91d101497495ec 100644 (file)
@@ -255,12 +255,17 @@ int cgroup_lock_is_held(void)
 
 EXPORT_SYMBOL_GPL(cgroup_lock_is_held);
 
+static int css_unbias_refcnt(int refcnt)
+{
+       return refcnt >= 0 ? refcnt : refcnt - CSS_DEACT_BIAS;
+}
+
 /* the current nr of refs, always >= 0 whether @css is deactivated or not */
 static int css_refcnt(struct cgroup_subsys_state *css)
 {
        int v = atomic_read(&css->refcnt);
 
-       return v >= 0 ? v : v - CSS_DEACT_BIAS;
+       return css_unbias_refcnt(v);
 }
 
 /* convenient tests for these bits */
@@ -3878,8 +3883,12 @@ static void css_dput_fn(struct work_struct *work)
 {
        struct cgroup_subsys_state *css =
                container_of(work, struct cgroup_subsys_state, dput_work);
+       struct dentry *dentry = css->cgroup->dentry;
+       struct super_block *sb = dentry->d_sb;
 
-       dput(css->cgroup->dentry);
+       atomic_inc(&sb->s_active);
+       dput(dentry);
+       deactivate_super(sb);
 }
 
 static void init_cgroup_css(struct cgroup_subsys_state *css,
@@ -4971,10 +4980,12 @@ EXPORT_SYMBOL_GPL(__css_tryget);
 void __css_put(struct cgroup_subsys_state *css)
 {
        struct cgroup *cgrp = css->cgroup;
+       int v;
 
        rcu_read_lock();
-       atomic_dec(&css->refcnt);
-       switch (css_refcnt(css)) {
+       v = css_unbias_refcnt(atomic_dec_return(&css->refcnt));
+
+       switch (v) {
        case 1:
                if (notify_on_release(cgrp)) {
                        set_bit(CGRP_RELEASABLE, &cgrp->flags);