]> www.infradead.org Git - users/hch/misc.git/commitdiff
NFS: O_DIRECT writes must check and adjust the file length
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 1 Feb 2025 19:59:02 +0000 (14:59 -0500)
committerAnna Schumaker <anna.schumaker@oracle.com>
Wed, 19 Feb 2025 21:45:23 +0000 (16:45 -0500)
While it is uncommon for delegations to be held while O_DIRECT writes
are in progress, it is possible. The xfstests generic/647 and
generic/729 both end up triggering that state, and end up failing due to
the fact that the file size is not adjusted.

Reported-by: Chuck Lever <chuck.lever@oracle.com>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=219738
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
fs/nfs/direct.c

index f45beea92d034d77c47434effa8b2c6316c15650..40e13c9a2873f91d60a6786e6d208d09d4142599 100644 (file)
@@ -130,6 +130,20 @@ static void nfs_direct_truncate_request(struct nfs_direct_req *dreq,
                dreq->count = req_start;
 }
 
+static void nfs_direct_file_adjust_size_locked(struct inode *inode,
+                                              loff_t offset, size_t count)
+{
+       loff_t newsize = offset + (loff_t)count;
+       loff_t oldsize = i_size_read(inode);
+
+       if (newsize > oldsize) {
+               i_size_write(inode, newsize);
+               NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
+               trace_nfs_size_grow(inode, newsize);
+               nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
+       }
+}
+
 /**
  * nfs_swap_rw - NFS address space operation for swap I/O
  * @iocb: target I/O control block
@@ -741,6 +755,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
        struct nfs_direct_req *dreq = hdr->dreq;
        struct nfs_commit_info cinfo;
        struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+       struct inode *inode = dreq->inode;
        int flags = NFS_ODIRECT_DONE;
 
        trace_nfs_direct_write_completion(dreq);
@@ -762,6 +777,10 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
        }
        spin_unlock(&dreq->lock);
 
+       spin_lock(&inode->i_lock);
+       nfs_direct_file_adjust_size_locked(inode, dreq->io_start, dreq->count);
+       spin_unlock(&inode->i_lock);
+
        while (!list_empty(&hdr->pages)) {
 
                req = nfs_list_entry(hdr->pages.next);