NFS: Ensure that rmdir() waits for sillyrenames to complete
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 30 Aug 2013 16:24:25 +0000 (12:24 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 3 Sep 2013 19:26:29 +0000 (15:26 -0400)
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>
fs/nfs/dir.c
fs/nfs/unlink.c
include/linux/nfs_fs.h

index d8149e916dd73b260f054e1a8feba40050c6f1e3..187caa47dad979ca8bd1a2c0013703284daab6f0 100644 (file)
@@ -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;
index 2c1485d1841903f990dc0b4ff142fc773d1ce19f..bb939edd4c998cb98b7aeb56ae1aa308e4d9009d 100644 (file)
@@ -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);
index 7125cef741642b77ca5c41758c26e500be3a2672..3ea4cde8701ce1e97d6a39a4a1264452468a5254 100644 (file)
@@ -524,6 +524,7 @@ static inline void nfs4_label_free(void *label) {}
  * linux/fs/nfs/unlink.c
  */
 extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
+extern void nfs_wait_on_sillyrename(struct dentry *dentry);
 extern void nfs_block_sillyrename(struct dentry *dentry);
 extern void nfs_unblock_sillyrename(struct dentry *dentry);
 extern int  nfs_sillyrename(struct inode *dir, struct dentry *dentry);