Re: another nfs puzzle

From: Trond Myklebust
Date: Tue Dec 06 2005 - 22:24:17 EST


On Tue, 2005-12-06 at 14:04 -0800, Kenny Simpson wrote:
> Hi again,
> I am seeing some odd behavior with O_DIRECT. If a file opened with O_DIRECT has a page mmap'd,
> and the file is extended via pwrite, then the mmap'd region seems to get lost - i.e. it neither
> takes up system memory, nor does it get written out.
>

Does the attached patch fix it?

Cheers,
Trond

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 <Trond.Myklebust@xxxxxxxxxx>
---

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;