NFS: Fix another O_DIRECT race
Trond Myklebust [Tue, 13 Dec 2005 21:13:54 +0000 (16:13 -0500)]
 Ensure we call unmap_mapping_range() and sync dirty pages to disk before
 doing an NFS direct write.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/inode.c
include/linux/nfs_fs.h

index b497c71..0792288 100644 (file)
@@ -678,15 +678,9 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
        if (!count)
                goto out;
 
-       if (mapping->nrpages) {
-               retval = filemap_fdatawrite(mapping);
-               if (retval == 0)
-                       retval = nfs_wb_all(inode);
-               if (retval == 0)
-                       retval = filemap_fdatawait(mapping);
-               if (retval)
-                       goto out;
-       }
+       retval = nfs_sync_mapping(mapping);
+       if (retval)
+               goto out;
 
        retval = nfs_direct_read(inode, ctx, &iov, pos, 1);
        if (retval > 0)
@@ -764,15 +758,9 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
        if (!count)
                goto out;
 
-       if (mapping->nrpages) {
-               retval = filemap_fdatawrite(mapping);
-               if (retval == 0)
-                       retval = nfs_wb_all(inode);
-               if (retval == 0)
-                       retval = filemap_fdatawait(mapping);
-               if (retval)
-                       goto out;
-       }
+       retval = nfs_sync_mapping(mapping);
+       if (retval)
+               goto out;
 
        retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
        if (mapping->nrpages)
index 57d3e77..eb5cd4c 100644 (file)
@@ -433,11 +433,7 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
         * Flush all pending writes before doing anything
         * with locks..
         */
-       filemap_fdatawrite(filp->f_mapping);
-       down(&inode->i_sem);
-       nfs_wb_all(inode);
-       up(&inode->i_sem);
-       filemap_fdatawait(filp->f_mapping);
+       nfs_sync_mapping(filp->f_mapping);
 
        /* NOTE: special case
         *      If we're signalled while cleaning up locks on process exit, we
@@ -465,15 +461,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
         * Flush all pending writes before doing anything
         * with locks..
         */
-       status = filemap_fdatawrite(filp->f_mapping);
-       if (status == 0) {
-               down(&inode->i_sem);
-               status = nfs_wb_all(inode);
-               up(&inode->i_sem);
-               if (status == 0)
-                       status = filemap_fdatawait(filp->f_mapping);
-       }
-       if (status < 0)
+       status = nfs_sync_mapping(filp->f_mapping);
+       if (status != 0)
                goto out;
 
        lock_kernel();
@@ -497,11 +486,7 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
         * Make sure we clear the cache whenever we try to get the lock.
         * This makes locking act as a cache coherency point.
         */
-       filemap_fdatawrite(filp->f_mapping);
-       down(&inode->i_sem);
-       nfs_wb_all(inode);      /* we may have slept */
-       up(&inode->i_sem);
-       filemap_fdatawait(filp->f_mapping);
+       nfs_sync_mapping(filp->f_mapping);
        nfs_zap_caches(inode);
 out:
        rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
index afd75d0..432f41c 100644 (file)
@@ -640,6 +640,27 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
        return 0;
 }
 
+/**
+ * nfs_sync_mapping - helper to flush all mmapped dirty data to disk
+ */
+int nfs_sync_mapping(struct address_space *mapping)
+{
+       int ret;
+
+       if (mapping->nrpages == 0)
+               return 0;
+       unmap_mapping_range(mapping, 0, 0, 0);
+       ret = filemap_fdatawrite(mapping);
+       if (ret != 0)
+               goto out;
+       ret = filemap_fdatawait(mapping);
+       if (ret != 0)
+               goto out;
+       ret = nfs_wb_all(mapping->host);
+out:
+       return ret;
+}
+
 /*
  * Invalidate the local caches
  */
@@ -1179,11 +1200,8 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
        struct nfs_inode *nfsi = NFS_I(inode);
 
        if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
-               if (S_ISREG(inode->i_mode)) {
-                       if (filemap_fdatawrite(mapping) == 0)
-                               filemap_fdatawait(mapping);
-                       nfs_wb_all(inode);
-               }
+               if (S_ISREG(inode->i_mode))
+                       nfs_sync_mapping(mapping);
                invalidate_inode_pages2(mapping);
 
                spin_lock(&inode->i_lock);
index 12787a9..2516ade 100644 (file)
@@ -291,6 +291,7 @@ static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long
 /*
  * linux/fs/nfs/inode.c
  */
+extern int nfs_sync_mapping(struct address_space *mapping);
 extern void nfs_zap_caches(struct inode *);
 extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
                                struct nfs_fattr *);