Merge tag 'mac80211-for-john-2014-11-18' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / fs / autofs4 / expire.c
index 8fa3895..683a5b9 100644 (file)
@@ -30,12 +30,6 @@ static inline int autofs4_can_expire(struct dentry *dentry,
                /* Too young to die */
                if (!timeout || time_after(ino->last_used + timeout, now))
                        return 0;
-
-               /* update last_used here :-
-                  - obviously makes sense if it is in use now
-                  - less obviously, prevents rapid-fire expire
-                    attempts if expire fails the first time */
-               ino->last_used = now;
        }
        return 1;
 }
@@ -327,10 +321,19 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
        if (ino->flags & AUTOFS_INF_PENDING)
                goto out;
        if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
-               ino->flags |= AUTOFS_INF_EXPIRING;
-               init_completion(&ino->expire_complete);
+               ino->flags |= AUTOFS_INF_NO_RCU;
                spin_unlock(&sbi->fs_lock);
-               return root;
+               synchronize_rcu();
+               spin_lock(&sbi->fs_lock);
+               if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
+                       ino->flags |= AUTOFS_INF_EXPIRING;
+                       smp_mb();
+                       ino->flags &= ~AUTOFS_INF_NO_RCU;
+                       init_completion(&ino->expire_complete);
+                       spin_unlock(&sbi->fs_lock);
+                       return root;
+               }
+               ino->flags &= ~AUTOFS_INF_NO_RCU;
        }
 out:
        spin_unlock(&sbi->fs_lock);
@@ -339,6 +342,89 @@ out:
        return NULL;
 }
 
+/* Check if 'dentry' should expire, or return a nearby
+ * dentry that is suitable.
+ * If returned dentry is different from arg dentry,
+ * then a dget() reference was taken, else not.
+ */
+static struct dentry *should_expire(struct dentry *dentry,
+                                   struct vfsmount *mnt,
+                                   unsigned long timeout,
+                                   int how)
+{
+       int do_now = how & AUTOFS_EXP_IMMEDIATE;
+       int exp_leaves = how & AUTOFS_EXP_LEAVES;
+       struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       unsigned int ino_count;
+
+       /* No point expiring a pending mount */
+       if (ino->flags & AUTOFS_INF_PENDING)
+               return NULL;
+
+       /*
+        * Case 1: (i) indirect mount or top level pseudo direct mount
+        *         (autofs-4.1).
+        *         (ii) indirect mount with offset mount, check the "/"
+        *         offset (autofs-5.0+).
+        */
+       if (d_mountpoint(dentry)) {
+               DPRINTK("checking mountpoint %p %.*s",
+                       dentry, (int)dentry->d_name.len, dentry->d_name.name);
+
+               /* Can we umount this guy */
+               if (autofs4_mount_busy(mnt, dentry))
+                       return NULL;
+
+               /* Can we expire this guy */
+               if (autofs4_can_expire(dentry, timeout, do_now))
+                       return dentry;
+               return NULL;
+       }
+
+       if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)) {
+               DPRINTK("checking symlink %p %.*s",
+                       dentry, (int)dentry->d_name.len, dentry->d_name.name);
+               /*
+                * A symlink can't be "busy" in the usual sense so
+                * just check last used for expire timeout.
+                */
+               if (autofs4_can_expire(dentry, timeout, do_now))
+                       return dentry;
+               return NULL;
+       }
+
+       if (simple_empty(dentry))
+               return NULL;
+
+       /* Case 2: tree mount, expire iff entire tree is not busy */
+       if (!exp_leaves) {
+               /* Path walk currently on this dentry? */
+               ino_count = atomic_read(&ino->count) + 1;
+               if (d_count(dentry) > ino_count)
+                       return NULL;
+
+               if (!autofs4_tree_busy(mnt, dentry, timeout, do_now))
+                       return dentry;
+       /*
+        * Case 3: pseudo direct mount, expire individual leaves
+        *         (autofs-4.1).
+        */
+       } else {
+               /* Path walk currently on this dentry? */
+               struct dentry *expired;
+               ino_count = atomic_read(&ino->count) + 1;
+               if (d_count(dentry) > ino_count)
+                       return NULL;
+
+               expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
+               if (expired) {
+                       if (expired == dentry)
+                               dput(dentry);
+                       return expired;
+               }
+       }
+       return NULL;
+}
 /*
  * Find an eligible tree to time-out
  * A tree is eligible if :-
@@ -353,11 +439,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
        unsigned long timeout;
        struct dentry *root = sb->s_root;
        struct dentry *dentry;
-       struct dentry *expired = NULL;
-       int do_now = how & AUTOFS_EXP_IMMEDIATE;
-       int exp_leaves = how & AUTOFS_EXP_LEAVES;
+       struct dentry *expired;
        struct autofs_info *ino;
-       unsigned int ino_count;
 
        if (!root)
                return NULL;
@@ -369,77 +452,28 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
        while ((dentry = get_next_positive_subdir(dentry, root))) {
                spin_lock(&sbi->fs_lock);
                ino = autofs4_dentry_ino(dentry);
-               /* No point expiring a pending mount */
-               if (ino->flags & AUTOFS_INF_PENDING)
-                       goto next;
-
-               /*
-                * Case 1: (i) indirect mount or top level pseudo direct mount
-                *         (autofs-4.1).
-                *         (ii) indirect mount with offset mount, check the "/"
-                *         offset (autofs-5.0+).
-                */
-               if (d_mountpoint(dentry)) {
-                       DPRINTK("checking mountpoint %p %.*s",
-                               dentry, (int)dentry->d_name.len, dentry->d_name.name);
-
-                       /* Can we umount this guy */
-                       if (autofs4_mount_busy(mnt, dentry))
-                               goto next;
-
-                       /* Can we expire this guy */
-                       if (autofs4_can_expire(dentry, timeout, do_now)) {
-                               expired = dentry;
-                               goto found;
-                       }
-                       goto next;
+               if (ino->flags & AUTOFS_INF_NO_RCU)
+                       expired = NULL;
+               else
+                       expired = should_expire(dentry, mnt, timeout, how);
+               if (!expired) {
+                       spin_unlock(&sbi->fs_lock);
+                       continue;
                }
-
-               if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)) {
-                       DPRINTK("checking symlink %p %.*s",
-                               dentry, (int)dentry->d_name.len, dentry->d_name.name);
-                       /*
-                        * A symlink can't be "busy" in the usual sense so
-                        * just check last used for expire timeout.
-                        */
-                       if (autofs4_can_expire(dentry, timeout, do_now)) {
-                               expired = dentry;
-                               goto found;
-                       }
-                       goto next;
-               }
-
-               if (simple_empty(dentry))
-                       goto next;
-
-               /* Case 2: tree mount, expire iff entire tree is not busy */
-               if (!exp_leaves) {
-                       /* Path walk currently on this dentry? */
-                       ino_count = atomic_read(&ino->count) + 1;
-                       if (d_count(dentry) > ino_count)
-                               goto next;
-
-                       if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
-                               expired = dentry;
-                               goto found;
-                       }
-               /*
-                * Case 3: pseudo direct mount, expire individual leaves
-                *         (autofs-4.1).
-                */
-               } else {
-                       /* Path walk currently on this dentry? */
-                       ino_count = atomic_read(&ino->count) + 1;
-                       if (d_count(dentry) > ino_count)
-                               goto next;
-
-                       expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
-                       if (expired) {
+               ino = autofs4_dentry_ino(expired);
+               ino->flags |= AUTOFS_INF_NO_RCU;
+               spin_unlock(&sbi->fs_lock);
+               synchronize_rcu();
+               spin_lock(&sbi->fs_lock);
+               if (should_expire(expired, mnt, timeout, how)) {
+                       if (expired != dentry)
                                dput(dentry);
-                               goto found;
-                       }
+                       goto found;
                }
-next:
+
+               ino->flags &= ~AUTOFS_INF_NO_RCU;
+               if (expired != dentry)
+                       dput(expired);
                spin_unlock(&sbi->fs_lock);
        }
        return NULL;
@@ -447,8 +481,9 @@ next:
 found:
        DPRINTK("returning %p %.*s",
                expired, (int)expired->d_name.len, expired->d_name.name);
-       ino = autofs4_dentry_ino(expired);
        ino->flags |= AUTOFS_INF_EXPIRING;
+       smp_mb();
+       ino->flags &= ~AUTOFS_INF_NO_RCU;
        init_completion(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
        spin_lock(&sbi->lookup_lock);
@@ -461,13 +496,18 @@ found:
        return expired;
 }
 
-int autofs4_expire_wait(struct dentry *dentry)
+int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
        int status;
 
        /* Block on any pending expire */
+       if (!(ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU)))
+               return 0;
+       if (rcu_walk)
+               return -ECHILD;
+
        spin_lock(&sbi->fs_lock);
        if (ino->flags & AUTOFS_INF_EXPIRING) {
                spin_unlock(&sbi->fs_lock);
@@ -519,6 +559,8 @@ int autofs4_expire_run(struct super_block *sb,
 
        spin_lock(&sbi->fs_lock);
        ino = autofs4_dentry_ino(dentry);
+       /* avoid rapid-fire expire attempts if expiry fails */
+       ino->last_used = now;
        ino->flags &= ~AUTOFS_INF_EXPIRING;
        complete_all(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
@@ -545,6 +587,8 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
                ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
 
                spin_lock(&sbi->fs_lock);
+               /* avoid rapid-fire expire attempts if expiry fails */
+               ino->last_used = now;
                ino->flags &= ~AUTOFS_INF_EXPIRING;
                complete_all(&ino->expire_complete);
                spin_unlock(&sbi->fs_lock);