exportfs: clear DISCONNECTED on all parents sooner
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 9 Sep 2013 20:15:13 +0000 (16:15 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 9 Nov 2013 05:16:36 +0000 (00:16 -0500)
Once we've found any connected parent, we know all our parents are
connected--that's true even if there's a concurrent rename.  May as well
clear them all at once and be done with it.

Reviewed-by: Cristoph Hellwig <hch@lst.de>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/exportfs/expfs.c

index 87e6dca69e438efcf0f584540d2bcd8447154d0b..c65b748688ff6917c56f726a6a2d77e623aa1c76 100644 (file)
@@ -90,6 +90,24 @@ find_disconnected_root(struct dentry *dentry)
        return dentry;
 }
 
+static void clear_disconnected(struct dentry *dentry)
+{
+       dget(dentry);
+       while (dentry->d_flags & DCACHE_DISCONNECTED) {
+               struct dentry *parent = dget_parent(dentry);
+
+               WARN_ON_ONCE(IS_ROOT(dentry));
+
+               spin_lock(&dentry->d_lock);
+               dentry->d_flags &= ~DCACHE_DISCONNECTED;
+               spin_unlock(&dentry->d_lock);
+
+               dput(dentry);
+               dentry = parent;
+       }
+       dput(dentry);
+}
+
 /*
  * Make sure target_dir is fully connected to the dentry tree.
  *
@@ -128,10 +146,9 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
 
                if (!IS_ROOT(pd)) {
                        /* must have found a connected parent - great */
-                       spin_lock(&pd->d_lock);
-                       pd->d_flags &= ~DCACHE_DISCONNECTED;
-                       spin_unlock(&pd->d_lock);
-                       noprogress = 0;
+                       clear_disconnected(target_dir);
+                       dput(pd);
+                       break;
                } else {
                        /*
                         * We have hit the top of a disconnected path, try to