diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-08-30 12:24:25 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-09-03 15:26:29 -0400 |
commit | ba6c05928dcafc7e0a0c8e4ee6a293ba47190fd4 (patch) | |
tree | 3b32adfdc8fb978f1933d43591e36ed0b14897d6 /fs/nfs | |
parent | a5250def7c4549a6a1cd8257900bef9c12ffc2fc (diff) |
NFS: Ensure that rmdir() waits for sillyrenames to complete
If an NFS client does
mkdir("dir");
fd = open("dir/file");
unlink("dir/file");
close(fd);
rmdir("dir");
then the asynchronous nature of the sillyrename operation means that
we can end up getting EBUSY for the rmdir() in the above test. Fix
that by ensuring that we wait for any in-progress sillyrenames
before sending the rmdir() to the server.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 19 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 7 |
2 files changed, 20 insertions, 6 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d8149e916dd7..187caa47dad9 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1694,12 +1694,19 @@ int nfs_rmdir(struct inode *dir, struct dentry *dentry) dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); trace_nfs_rmdir_enter(dir, dentry); - error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); - /* Ensure the VFS deletes this inode */ - if (error == 0 && dentry->d_inode != NULL) - clear_nlink(dentry->d_inode); - else if (error == -ENOENT) - nfs_dentry_handle_enoent(dentry); + if (dentry->d_inode) { + nfs_wait_on_sillyrename(dentry); + error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); + /* Ensure the VFS deletes this inode */ + switch (error) { + case 0: + clear_nlink(dentry->d_inode); + break; + case -ENOENT: + nfs_dentry_handle_enoent(dentry); + } + } else + error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); trace_nfs_rmdir_exit(dir, dentry, error); return error; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 2c1485d18419..bb939edd4c99 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -207,6 +207,13 @@ out_free: return ret; } +void nfs_wait_on_sillyrename(struct dentry *dentry) +{ + struct nfs_inode *nfsi = NFS_I(dentry->d_inode); + + wait_event(nfsi->waitqueue, atomic_read(&nfsi->silly_count) <= 1); +} + void nfs_block_sillyrename(struct dentry *dentry) { struct nfs_inode *nfsi = NFS_I(dentry->d_inode); |