NFS: Fix another O_DIRECT race Ensure we call unmap_mapping_range() and sync dirty pages to disk before doing an NFS direct write. Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 43 ++++++++++++++++++++++++------------------- 1 files changed, 24 insertions(+), 19 deletions(-) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index a2d2814..ce83000 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -111,6 +111,21 @@ nfs_get_user_pages(int rw, unsigned long return result; } +static int nfs_sync_mapping(struct address_space *mapping, loff_t offset, size_t len) +{ + int ret; + if (mapping->nrpages) + return 0; + + unmap_mapping_range(mapping, offset, len, 0); + ret = filemap_fdatawrite(mapping); + if (ret == 0) + ret = filemap_fdatawait(mapping); + if (ret == 0) + ret = nfs_wb_all(mapping->host); + return ret; +} + /** * nfs_free_user_pages - tear down page struct array * @pages: array of page struct pointers underlying target buffer @@ -676,15 +691,9 @@ nfs_file_direct_read(struct kiocb *iocb, 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, pos, count); + if (retval) + goto out; retval = nfs_direct_read(inode, ctx, &iov, pos, 1); if (retval > 0) @@ -762,19 +771,15 @@ nfs_file_direct_write(struct kiocb *iocb 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, pos, count); + if (retval) + goto out; retval = nfs_direct_write(inode, ctx, &iov, pos, 1); if (mapping->nrpages) - invalidate_inode_pages2(mapping); + invalidate_inode_pages2_range(mapping, + pos >> PAGE_CACHE_SHIFT, + (pos + count - 1) >> PAGE_CACHE_SHIFT); if (retval > 0) *ppos = pos + retval;