NFS: O_DIRECT writes must check and adjust the file length

JIRA: https://issues.redhat.com/browse/RHEL-87767

commit fcf857ee1958e9247298251f7615d0c76f1e9b38
Author: Trond Myklebust <trond.myklebust@hammerspace.com>
Date:   Sat Feb 1 14:59:02 2025 -0500

    NFS: O_DIRECT writes must check and adjust the file length

    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>

Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
This commit is contained in:
Benjamin Coddington 2025-04-17 14:49:07 -04:00
parent 29b928da8e
commit 42fcb65f11
1 changed files with 19 additions and 0 deletions

View File

@ -130,6 +130,20 @@ static void nfs_direct_truncate_request(struct nfs_direct_req *dreq,
dreq->count = req_start; 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 * nfs_swap_rw - NFS address space operation for swap I/O
* @iocb: target I/O control block * @iocb: target I/O control block
@ -732,6 +746,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
struct nfs_direct_req *dreq = hdr->dreq; struct nfs_direct_req *dreq = hdr->dreq;
struct nfs_commit_info cinfo; struct nfs_commit_info cinfo;
struct nfs_page *req = nfs_list_entry(hdr->pages.next); struct nfs_page *req = nfs_list_entry(hdr->pages.next);
struct inode *inode = dreq->inode;
int flags = NFS_ODIRECT_DONE; int flags = NFS_ODIRECT_DONE;
trace_nfs_direct_write_completion(dreq); trace_nfs_direct_write_completion(dreq);
@ -753,6 +768,10 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
} }
spin_unlock(&dreq->lock); 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)) { while (!list_empty(&hdr->pages)) {
req = nfs_list_entry(hdr->pages.next); req = nfs_list_entry(hdr->pages.next);