diff options
author | J. Bruce Fields <bfields@redhat.com> | 2013-09-09 22:15:13 +0200 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-11-09 06:16:36 +0100 |
commit | 0dbc018a490ed482a1236aad77ac12e20742b322 (patch) | |
tree | 22ea922d1d9d68bf7d9d1f82e256380e87190f5c | |
parent | exportfs: more detailed comment for path_reconnect (diff) | |
download | linux-0dbc018a490ed482a1236aad77ac12e20742b322.tar.xz linux-0dbc018a490ed482a1236aad77ac12e20742b322.zip |
exportfs: clear DISCONNECTED on all parents sooner
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>
-rw-r--r-- | fs/exportfs/expfs.c | 25 |
1 files changed, 21 insertions, 4 deletions
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 87e6dca69e43..c65b748688ff 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -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 |