From: Lei Yin Date: Fri, 24 Apr 2026 09:26:41 +0000 (+0000) Subject: NFSv4.1/pNFS: fix LAYOUTCOMMIT retry loop on OLD_STATEID X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d616d8bec3b11962735c9c9ff53fb4972162b324;p=thirdparty%2Flinux.git NFSv4.1/pNFS: fix LAYOUTCOMMIT retry loop on OLD_STATEID Handle -NFS4ERR_OLD_STATEID in nfs4_layoutcommit_done(). This issue was reproduced on NFSv4.2. Without refreshing data->args.stateid, LAYOUTCOMMIT can keep retrying with the same stale stateid after OLD_STATEID, resulting in an unbounded retry loop. Refresh the layout stateid with nfs4_layout_refresh_old_stateid() and restart the RPC only after a successful refresh. Changes since v1: update refreshed stateid in inode layout header. Signed-off-by: Lei Yin [Anna: Fix up dprintk() format specifier] Signed-off-by: Anna Schumaker --- diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0d77d50f4de7b..393ce50274d28 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -9988,6 +9988,38 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) case -NFS4ERR_GRACE: /* loca_recalim always false */ task->tk_status = 0; break; + case -NFS4ERR_OLD_STATEID: { + u32 old_seqid = be32_to_cpu(data->args.stateid.seqid); + struct pnfs_layout_range range = { + .iomode = IOMODE_ANY, + .offset = 0, + .length = NFS4_MAX_UINT64, + }; + + if (nfs4_layout_refresh_old_stateid(&data->args.stateid, + &range, + data->args.inode)) { + struct pnfs_layout_hdr *lo; + + spin_lock(&data->args.inode->i_lock); + lo = NFS_I(data->args.inode)->layout; + if (lo && pnfs_layout_is_valid(lo) && + nfs4_stateid_match_other(&data->args.stateid, + &lo->plh_stateid)) + pnfs_set_layout_stateid(lo, &data->args.stateid, + NULL, false); + spin_unlock(&data->args.inode->i_lock); + + dprintk("%s: refreshed OLD_STATEID inode %llu seq %u->%u\n", + __func__, data->args.inode->i_ino, + old_seqid, + be32_to_cpu(data->args.stateid.seqid)); + + rpc_restart_call_prepare(task); + return; + } + fallthrough; + } case 0: break; default: