]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFSD: Fix last write offset handling in layoutcommit
authorSergey Bashirov <sergeybashirov@gmail.com>
Mon, 20 Oct 2025 12:57:40 +0000 (08:57 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Oct 2025 13:00:01 +0000 (14:00 +0100)
[ Upstream commit d68886bae76a4b9b3484d23e5b7df086f940fa38 ]

The data type of loca_last_write_offset is newoffset4 and is switched
on a boolean value, no_newoffset, that indicates if a previous write
occurred or not. If no_newoffset is FALSE, an offset is not given.
This means that client does not try to update the file size. Thus,
server should not try to calculate new file size and check if it fits
into the segment range. See RFC 8881, section 12.5.4.2.

Sometimes the current incorrect logic may cause clients to hang when
trying to sync an inode. If layoutcommit fails, the client marks the
inode as dirty again.

Fixes: 9cf514ccfacb ("nfsd: implement pNFS operations")
Cc: stable@vger.kernel.org
Co-developed-by: Konstantin Evtushenko <koevtushenko@yandex.com>
Signed-off-by: Konstantin Evtushenko <koevtushenko@yandex.com>
Signed-off-by: Sergey Bashirov <sergeybashirov@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[ replaced inode_get_mtime() with inode->i_mtime and removed rqstp parameter from proc_layoutcommit() ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfsd/blocklayout.c
fs/nfsd/nfs4proc.c

index 9bbaa671c0799d70d8bad77b47dd7e9eb83fdf4c..e620c1662e7ba62bc9df29f8004b197a5f2d4596 100644 (file)
@@ -120,7 +120,6 @@ static __be32
 nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
                struct iomap *iomaps, int nr_iomaps)
 {
-       loff_t new_size = lcp->lc_last_wr + 1;
        struct iattr iattr = { .ia_valid = 0 };
        int error;
 
@@ -130,9 +129,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
        iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
        iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
 
-       if (new_size > i_size_read(inode)) {
+       if (lcp->lc_size_chg) {
                iattr.ia_valid |= ATTR_SIZE;
-               iattr.ia_size = new_size;
+               iattr.ia_size = lcp->lc_newsize;
        }
 
        error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
index 5600fdfa189b86a82217d1e0d6a542f1570e7ac6..df1a86dff57176307bd22defe1a771b791d21c95 100644 (file)
@@ -1701,7 +1701,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
        const struct nfsd4_layout_seg *seg = &lcp->lc_seg;
        struct svc_fh *current_fh = &cstate->current_fh;
        const struct nfsd4_layout_ops *ops;
-       loff_t new_size = lcp->lc_last_wr + 1;
        struct inode *inode;
        struct nfs4_layout_stateid *ls;
        __be32 nfserr;
@@ -1716,13 +1715,21 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
                goto out;
        inode = d_inode(current_fh->fh_dentry);
 
-       nfserr = nfserr_inval;
-       if (new_size <= seg->offset)
-               goto out;
-       if (new_size > seg->offset + seg->length)
-               goto out;
-       if (!lcp->lc_newoffset && new_size > i_size_read(inode))
-               goto out;
+       lcp->lc_size_chg = false;
+       if (lcp->lc_newoffset) {
+               loff_t new_size = lcp->lc_last_wr + 1;
+
+               nfserr = nfserr_inval;
+               if (new_size <= seg->offset)
+                       goto out;
+               if (new_size > seg->offset + seg->length)
+                       goto out;
+
+               if (new_size > i_size_read(inode)) {
+                       lcp->lc_size_chg = true;
+                       lcp->lc_newsize = new_size;
+               }
+       }
 
        nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lcp->lc_sid,
                                                false, lcp->lc_layout_type,
@@ -1738,13 +1745,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
        /* LAYOUTCOMMIT does not require any serialization */
        mutex_unlock(&ls->ls_mutex);
 
-       if (new_size > i_size_read(inode)) {
-               lcp->lc_size_chg = 1;
-               lcp->lc_newsize = new_size;
-       } else {
-               lcp->lc_size_chg = 0;
-       }
-
        nfserr = ops->proc_layoutcommit(inode, lcp);
        nfs4_put_stid(&ls->ls_stid);
 out: