NFS: Optimise sparse writes past the end of file
If we're doing a write, and the entire page lies beyond the end-of-file, then we can assume the write can be extended to cover the beginning of the page, since we know the data in that region will be all zeros. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
37eaeed1a5
commit
fc9dc40189
|
@ -626,13 +626,11 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
/*
|
||||
* O_APPEND implies that we must revalidate the file length.
|
||||
*/
|
||||
if (iocb->ki_flags & IOCB_APPEND) {
|
||||
if (iocb->ki_flags & IOCB_APPEND || iocb->ki_pos > i_size_read(inode)) {
|
||||
result = nfs_revalidate_file_size(inode, file);
|
||||
if (result)
|
||||
goto out;
|
||||
}
|
||||
if (iocb->ki_pos > i_size_read(inode))
|
||||
nfs_revalidate_mapping(inode, file->f_mapping);
|
||||
|
||||
since = filemap_sample_wb_err(file->f_mapping);
|
||||
nfs_start_io_write(inode);
|
||||
|
|
|
@ -1278,19 +1278,21 @@ bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode)
|
|||
* the PageUptodate() flag. In this case, we will need to turn off
|
||||
* write optimisations that depend on the page contents being correct.
|
||||
*/
|
||||
static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
|
||||
static bool nfs_write_pageuptodate(struct page *page, struct inode *inode,
|
||||
unsigned int pagelen)
|
||||
{
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
|
||||
if (nfs_have_delegated_attributes(inode))
|
||||
goto out;
|
||||
if (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
|
||||
if (nfsi->cache_validity &
|
||||
(NFS_INO_REVAL_PAGECACHE | NFS_INO_INVALID_SIZE))
|
||||
return false;
|
||||
smp_rmb();
|
||||
if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags))
|
||||
if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags) && pagelen != 0)
|
||||
return false;
|
||||
out:
|
||||
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
|
||||
if (nfsi->cache_validity & NFS_INO_INVALID_DATA && pagelen != 0)
|
||||
return false;
|
||||
return PageUptodate(page) != 0;
|
||||
}
|
||||
|
@ -1310,7 +1312,8 @@ is_whole_file_wrlock(struct file_lock *fl)
|
|||
* If the file is opened for synchronous writes then we can just skip the rest
|
||||
* of the checks.
|
||||
*/
|
||||
static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
|
||||
static int nfs_can_extend_write(struct file *file, struct page *page,
|
||||
struct inode *inode, unsigned int pagelen)
|
||||
{
|
||||
int ret;
|
||||
struct file_lock_context *flctx = inode->i_flctx;
|
||||
|
@ -1318,7 +1321,7 @@ static int nfs_can_extend_write(struct file *file, struct page *page, struct ino
|
|||
|
||||
if (file->f_flags & O_DSYNC)
|
||||
return 0;
|
||||
if (!nfs_write_pageuptodate(page, inode))
|
||||
if (!nfs_write_pageuptodate(page, inode, pagelen))
|
||||
return 0;
|
||||
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
|
||||
return 1;
|
||||
|
@ -1356,6 +1359,7 @@ int nfs_updatepage(struct file *file, struct page *page,
|
|||
struct nfs_open_context *ctx = nfs_file_open_context(file);
|
||||
struct address_space *mapping = page_file_mapping(page);
|
||||
struct inode *inode = mapping->host;
|
||||
unsigned int pagelen = nfs_page_length(page);
|
||||
int status = 0;
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
|
||||
|
@ -1366,8 +1370,8 @@ int nfs_updatepage(struct file *file, struct page *page,
|
|||
if (!count)
|
||||
goto out;
|
||||
|
||||
if (nfs_can_extend_write(file, page, inode)) {
|
||||
count = max(count + offset, nfs_page_length(page));
|
||||
if (nfs_can_extend_write(file, page, inode, pagelen)) {
|
||||
count = max(count + offset, pagelen);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue