| From stable+bounces-188080-greg=kroah.com@vger.kernel.org Mon Oct 20 14:56:25 2025 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 20 Oct 2025 08:56:02 -0400 |
| Subject: NFSD: Fix last write offset handling in layoutcommit |
| To: stable@vger.kernel.org |
| Cc: Sergey Bashirov <sergeybashirov@gmail.com>, Konstantin Evtushenko <koevtushenko@yandex.com>, Christoph Hellwig <hch@lst.de>, Jeff Layton <jlayton@kernel.org>, Chuck Lever <chuck.lever@oracle.com>, Sasha Levin <sashal@kernel.org> |
| Message-ID: <20251020125602.1761375-3-sashal@kernel.org> |
| |
| From: Sergey Bashirov <sergeybashirov@gmail.com> |
| |
| [ 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> |
| [ adapted for direct inode->i_mtime access and 2-parameter proc_layoutcommit callback ] |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/nfsd/blocklayout.c | 5 ++--- |
| fs/nfsd/nfs4proc.c | 30 +++++++++++++++--------------- |
| 2 files changed, 17 insertions(+), 18 deletions(-) |
| |
| --- a/fs/nfsd/blocklayout.c |
| +++ b/fs/nfsd/blocklayout.c |
| @@ -121,7 +121,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; |
| |
| @@ -131,9 +130,9 @@ nfsd4_block_commit_blocks(struct inode * |
| 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, |
| --- a/fs/nfsd/nfs4proc.c |
| +++ b/fs/nfsd/nfs4proc.c |
| @@ -2262,7 +2262,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqst |
| 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; |
| @@ -2277,13 +2276,21 @@ nfsd4_layoutcommit(struct svc_rqst *rqst |
| 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, |
| @@ -2299,13 +2306,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqst |
| /* 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: |