[PATCH v2] NFSv4.1/pNFS: fix LAYOUTCOMMIT retry loop on OLD_STATEID
From: Lei Yin
Date: Fri Apr 24 2026 - 05:27:42 EST
From: Lei Yin <yinlei2@xxxxxxxxxx>
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 <yinlei2@xxxxxxxxxx>
---
fs/nfs/nfs4proc.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 7225b4cfa6c2..575bf45a9209 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -9989,6 +9989,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 %lu 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:
--
2.43.0