[PATCH 0/9] NFSD: Do not revoke a delegation the client never recorded

Chuck Lever <[email protected]> Sun, 02 Aug 2026 13:04:27 -0400
Newsgroups org.kernel.vger.linux-nfs,org.kernel.vger.stable
Message-ID <20260802-nfsd-deleg-destroy-badhandle-v1-0-323aa7196055@kernel.org>
A nightly backup on my home directory server ran into this. NFSD had
granted a write delegation during the day, and the client lost that
delegation to the bug that "NFS: Return a delegation the client fails
to record" addresses:

  https://lore.kernel.org/r/[email protected]

When NFSD recalled that delegation, the client had no record of the
stateid and answered CB_RECALL with NFS4ERR_BAD_STATEID. NFSD added
the delegation to cl_revoked, where only a FREE_STATEID drains it.
That FREE_STATEID never arrives, so every later SEQUENCE reply
carries SEQ4_STATUS_RECALLABLE_STATE_REVOKED. The client's state
manager loops issuing TEST_STATEID but never finds the missing state.

A delegation whose recall the client rejects is now destroyed rather
than revoked (patch 8). RFC 8881 Section 20.2.4 mandates that the
NFS server complete the delegation recall in this case rather than
marking the state as revoked.

There are some complications, of course. CB_RECALL can outrun the
reply that granted the delegation; a rejection proves nothing until
the client has seen that grant. RFC 8881 Section 2.10.6.3 makes
retirement of the slot that carried the grant that proof. NFSD
retries the CB_RECALL until the slot retires and revokes in the old
way when the retries lapse.

The series also makes CB_RECALL the second consumer of referring
call lists. The CB_RECALL send buffer budget has to be correct
before a referring call list can ride in it. Unfortunately NFSD's
original referring call lists implementation got some of the XDR
sizes wrong.

---
Chuck Lever (9):
      NFSD: Budget the CB_SEQUENCE opcode and referring call array count
      NFSD: Budget the CB_RECALL truncate field
      NFSD: Budget the CB_LAYOUTRECALL recall stateid
      NFSD: Budget the CB_OFFLOAD opcode
      NFSD: Budget the CB_NOTIFY_LOCK opcode
      NFSD: Budget the CB_RECALL_ANY opcode
      NFSD: Correct locking documentation for delegation sc_status
      NFSD: Destroy a recalled delegation the client does not hold
      NFSD: Send referring calls with CB_RECALL

 fs/nfsd/nfs4callback.c |   7 +-
 fs/nfsd/nfs4state.c    | 228 ++++++++++++++++++++++++++++++++++++++++++-------
 fs/nfsd/state.h        |  24 ++++--
 fs/nfsd/xdr4cb.h       |  20 +++--
 4 files changed, 231 insertions(+), 48 deletions(-)
---
base-commit: 106c31f3d72c56fce98bb2f0e8b3de1a0024228d
change-id: 20260802-nfsd-deleg-destroy-badhandle-333a720dc332

Best regards,
--  
Chuck Lever <[email protected]>