Skip to content

Commit d68886b

Browse files
sbashirochucklever
authored andcommitted
NFSD: Fix last write offset handling in layoutcommit
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: 9cf514c ("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>
1 parent f963cf2 commit d68886b

2 files changed

Lines changed: 17 additions & 18 deletions

File tree

fs/nfsd/blocklayout.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
118118
struct iomap *iomaps, int nr_iomaps)
119119
{
120120
struct timespec64 mtime = inode_get_mtime(inode);
121-
loff_t new_size = lcp->lc_last_wr + 1;
122121
struct iattr iattr = { .ia_valid = 0 };
123122
int error;
124123

@@ -128,9 +127,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
128127
iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
129128
iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
130129

131-
if (new_size > i_size_read(inode)) {
130+
if (lcp->lc_size_chg) {
132131
iattr.ia_valid |= ATTR_SIZE;
133-
iattr.ia_size = new_size;
132+
iattr.ia_size = lcp->lc_newsize;
134133
}
135134

136135
error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,

fs/nfsd/nfs4proc.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2475,7 +2475,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
24752475
const struct nfsd4_layout_seg *seg = &lcp->lc_seg;
24762476
struct svc_fh *current_fh = &cstate->current_fh;
24772477
const struct nfsd4_layout_ops *ops;
2478-
loff_t new_size = lcp->lc_last_wr + 1;
24792478
struct inode *inode;
24802479
struct nfs4_layout_stateid *ls;
24812480
__be32 nfserr;
@@ -2491,13 +2490,21 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
24912490
goto out;
24922491
inode = d_inode(current_fh->fh_dentry);
24932492

2494-
nfserr = nfserr_inval;
2495-
if (new_size <= seg->offset)
2496-
goto out;
2497-
if (new_size > seg->offset + seg->length)
2498-
goto out;
2499-
if (!lcp->lc_newoffset && new_size > i_size_read(inode))
2500-
goto out;
2493+
lcp->lc_size_chg = false;
2494+
if (lcp->lc_newoffset) {
2495+
loff_t new_size = lcp->lc_last_wr + 1;
2496+
2497+
nfserr = nfserr_inval;
2498+
if (new_size <= seg->offset)
2499+
goto out;
2500+
if (new_size > seg->offset + seg->length)
2501+
goto out;
2502+
2503+
if (new_size > i_size_read(inode)) {
2504+
lcp->lc_size_chg = true;
2505+
lcp->lc_newsize = new_size;
2506+
}
2507+
}
25012508

25022509
nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lcp->lc_sid,
25032510
false, lcp->lc_layout_type,
@@ -2513,13 +2520,6 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
25132520
/* LAYOUTCOMMIT does not require any serialization */
25142521
mutex_unlock(&ls->ls_mutex);
25152522

2516-
if (new_size > i_size_read(inode)) {
2517-
lcp->lc_size_chg = true;
2518-
lcp->lc_newsize = new_size;
2519-
} else {
2520-
lcp->lc_size_chg = false;
2521-
}
2522-
25232523
nfserr = ops->proc_layoutcommit(inode, rqstp, lcp);
25242524
nfs4_put_stid(&ls->ls_stid);
25252525
out:

0 commit comments

Comments
 (0)