|
@@ -327,6 +327,42 @@ nfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
|
|
return nfs_do_fsync(ctx, inode);
|
|
return nfs_do_fsync(ctx, inode);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Decide whether a read/modify/write cycle may be more efficient
|
|
|
|
+ * then a modify/write/read cycle when writing to a page in the
|
|
|
|
+ * page cache.
|
|
|
|
+ *
|
|
|
|
+ * The modify/write/read cycle may occur if a page is read before
|
|
|
|
+ * being completely filled by the writer. In this situation, the
|
|
|
|
+ * page must be completely written to stable storage on the server
|
|
|
|
+ * before it can be refilled by reading in the page from the server.
|
|
|
|
+ * This can lead to expensive, small, FILE_SYNC mode writes being
|
|
|
|
+ * done.
|
|
|
|
+ *
|
|
|
|
+ * It may be more efficient to read the page first if the file is
|
|
|
|
+ * open for reading in addition to writing, the page is not marked
|
|
|
|
+ * as Uptodate, it is not dirty or waiting to be committed,
|
|
|
|
+ * indicating that it was previously allocated and then modified,
|
|
|
|
+ * that there were valid bytes of data in that range of the file,
|
|
|
|
+ * and that the new data won't completely replace the old data in
|
|
|
|
+ * that range of the file.
|
|
|
|
+ */
|
|
|
|
+static int nfs_want_read_modify_write(struct file *file, struct page *page,
|
|
|
|
+ loff_t pos, unsigned len)
|
|
|
|
+{
|
|
|
|
+ unsigned int pglen = nfs_page_length(page);
|
|
|
|
+ unsigned int offset = pos & (PAGE_CACHE_SIZE - 1);
|
|
|
|
+ unsigned int end = offset + len;
|
|
|
|
+
|
|
|
|
+ if ((file->f_mode & FMODE_READ) && /* open for read? */
|
|
|
|
+ !PageUptodate(page) && /* Uptodate? */
|
|
|
|
+ !PagePrivate(page) && /* i/o request already? */
|
|
|
|
+ pglen && /* valid bytes of file? */
|
|
|
|
+ (end < pglen || offset)) /* replace all valid bytes? */
|
|
|
|
+ return 1;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This does the "real" work of the write. We must allocate and lock the
|
|
* This does the "real" work of the write. We must allocate and lock the
|
|
* page to be sent back to the generic routine, which then copies the
|
|
* page to be sent back to the generic routine, which then copies the
|
|
@@ -340,15 +376,16 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
|
|
struct page **pagep, void **fsdata)
|
|
struct page **pagep, void **fsdata)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
- pgoff_t index;
|
|
|
|
|
|
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
|
struct page *page;
|
|
struct page *page;
|
|
- index = pos >> PAGE_CACHE_SHIFT;
|
|
|
|
|
|
+ int once_thru = 0;
|
|
|
|
|
|
dfprintk(PAGECACHE, "NFS: write_begin(%s/%s(%ld), %u@%lld)\n",
|
|
dfprintk(PAGECACHE, "NFS: write_begin(%s/%s(%ld), %u@%lld)\n",
|
|
file->f_path.dentry->d_parent->d_name.name,
|
|
file->f_path.dentry->d_parent->d_name.name,
|
|
file->f_path.dentry->d_name.name,
|
|
file->f_path.dentry->d_name.name,
|
|
mapping->host->i_ino, len, (long long) pos);
|
|
mapping->host->i_ino, len, (long long) pos);
|
|
|
|
|
|
|
|
+start:
|
|
/*
|
|
/*
|
|
* Prevent starvation issues if someone is doing a consistency
|
|
* Prevent starvation issues if someone is doing a consistency
|
|
* sync-to-disk
|
|
* sync-to-disk
|
|
@@ -367,6 +404,13 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
|
|
if (ret) {
|
|
if (ret) {
|
|
unlock_page(page);
|
|
unlock_page(page);
|
|
page_cache_release(page);
|
|
page_cache_release(page);
|
|
|
|
+ } else if (!once_thru &&
|
|
|
|
+ nfs_want_read_modify_write(file, page, pos, len)) {
|
|
|
|
+ once_thru = 1;
|
|
|
|
+ ret = nfs_readpage(file, page);
|
|
|
|
+ page_cache_release(page);
|
|
|
|
+ if (!ret)
|
|
|
|
+ goto start;
|
|
}
|
|
}
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|