]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
NFSv4.1/pNFS: fix LAYOUTCOMMIT retry loop on OLD_STATEID
authorLei Yin <yinlei2@lenovo.com>
Fri, 24 Apr 2026 09:26:41 +0000 (09:26 +0000)
committerAnna Schumaker <anna.schumaker@hammerspace.com>
Mon, 8 Jun 2026 19:02:05 +0000 (15:02 -0400)
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@lenovo.com>
[Anna: Fix up dprintk() format specifier]
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
fs/nfs/nfs4proc.c

index 0d77d50f4de7b7020a3488f3be73025496e7f6d7..393ce50274d2834f3ebda9f796bdf9159b225599 100644 (file)
@@ -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: