Skip to content

NAS-142112 / None / Fix nfsd client teardown hang from leaked callback count - #333

Merged
ixhamza merged 2 commits into
truenas/linux-6.12from
SEE-589
Aug 7, 2026
Merged

NAS-142112 / None / Fix nfsd client teardown hang from leaked callback count#333
ixhamza merged 2 commits into
truenas/linux-6.12from
SEE-589

Conversation

@ixhamza

@ixhamza ixhamza commented Aug 7, 2026

Copy link
Copy Markdown
Member

Problem

Support raised SEE-589 after a customer's NFS server (~87 NFSv4 clients) wedged for the third time. Once it happens nfsd can't expire clients any more, every later client teardown piles up behind the stuck one, and since the stuck task is the laundromat (nfsd's periodic worker that expires dead clients and cleans up NFSv4 state), all of that cleanup stops too. rpc.nfsd won't die on SIGKILL at that point, so only a reboot clears it.

This isn't new in 25.10.4. As per the attached debugs it hit twice on 6.12.15 (Mar 4 and Jun 20) and once on 6.12.91 (Jul 9), with the same stack every time:

INFO: task kworker/u128:11:979462 blocked for more than 120 seconds.
      Tainted: P        W  OE      6.12.15-production+truenas #1
task:kworker/u128:11 state:D stack:0     pid:979462 tgid:979462 ppid:2      flags:0x00004000
Workqueue: nfsd4 laundromat_main [nfsd]
Call Trace:
 <TASK>
 __schedule+0x461/0xa10
 schedule+0x27/0xd0
 nfsd4_shutdown_callback+0xfe/0x140 [nfsd]
 __destroy_client+0x1fa/0x2b0 [nfsd]
 nfs4_process_client_reaplist+0xa9/0x130 [nfsd]
 laundromat_main+0x1f1/0xa10 [nfsd]
 process_one_work+0x180/0x3a0
 worker_thread+0x2da/0x420
 kthread+0xcf/0x100
 ret_from_fork+0x31/0x50

Root cause

NFSv4 hands clients delegations so they can cache a file locally, and the server has to call the client back to take one away. Each of those callbacks takes a count on cl_cb_inflight, and only nfsd41_destroy_cb() drops it again.

There is one callback object per delegation, so if a delegation gets broken twice, both breaks queue the same work item. The first callback RPC fails, nfsd4_cb_release() tries to requeue it for a retry, nfsd4_queue_cb() returns false because it is already queued from the second break, and we ignore that. Nothing retries, and nothing drops the count.

nfsd4_shutdown_callback() waits for that count to hit zero before the client can be freed, so teardown blocks there forever. Hitting it needs a write delegation broken by a reader and then a writer, plus a callback that fails. The client itself doesn't have to be broken for that: a short network blip, or a reconnect that resets the backchannel, is enough.

Fix

Two upstream cherry-picks, no local changes. Neither is in 6.12.y yet.

  • 1054e8f adds a NFSD4_CALLBACK_RUNNING flag on the callback, so a second break on a delegation that is already being recalled returns early instead of queueing the same work item again. In mainline since v6.15.
  • fa183d59e531 clears that flag when the queue attempt fails. Without it the flag sticks and every later recall on that delegation gets skipped, so the first commit shouldn't go in alone. Queued in nfsd-next with a Fixes: tag against 1054e8f and Cc: stable, so it travels with it.

Testing

Two QEMU VMs, same rig: unpatched 6.12.95 and the patched build on 6.12.99. An NFS client in a netns takes a write delegation, traffic to it is dropped, the lease is broken twice (read open then write open), with a kretprobe on nfsd4_queue_cb.

Unpatched leaked on 14 of 14 iterations. Each leak left another task stuck in nfsd4_shutdown_callback, 15 by the end, and those clients could not be torn down at all.

Patched was clean on 23 of 23, no leaks and nothing stuck. Client teardown and NFS stop/start both complete immediately. 38 delegation recall cycles all served in about 1ms, and a mixed soak of clean recalls, double breaks and locks showed every queued callback destroyed with no skew. No new WARNs on either VM.

Confirmed both commits are actually in the patched kernel: struct nfsd4_callback gained cb_flags, and nfsd_break_deleg_cb disassembles to the test_and_set_bit gate plus the clear_bit on the failed queue path.

The nfsd4_callback workqueue jobs exist to queue backchannel RPCs to
rpciod. Because they run in different workqueue contexts, the rpc_task
can run concurrently with the workqueue job itself, should it become
requeued. This is problematic as there is no locking when accessing the
fields in the nfsd4_callback.

Add a new unsigned long to nfsd4_callback and declare a new
NFSD4_CALLBACK_RUNNING flag to be set in it. When attempting to run a
workqueue job, do a test_and_set_bit() on that flag first, and don't
queue the workqueue job if it returns true. Clear NFSD4_CALLBACK_RUNNING
in nfsd41_destroy_cb().

This also gives us a more reliable mechanism for handling queueing
failures in codepaths where we have to take references under spinlocks.
We can now do the test_and_set_bit on NFSD4_CALLBACK_RUNNING first, and
only take references to the objects if that returns false.

Most of the nfsd4_run_cb() callers are converted to use this new flag or
the nfsd4_try_run_cb() wrapper. The main exception is the callback
channel probe, which has its own synchronization.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
(cherry picked from commit 1054e8f)
nfsd_break_one_deleg() sets NFSD4_CALLBACK_RUNNING via test_and_set_bit
at entry to serialize recall work, then calls nfsd4_run_cb() to queue
the recall.  When the queue attempt fails the refcount bump is undone,
but the RUNNING bit is left set.  The only site that clears the bit is
nfsd41_destroy_cb() (fs/nfsd/nfs4callback.c), which runs from the
workqueue and is therefore unreachable when nothing was queued.

The bit becomes a permanent latch on dp->dl_recall.cb_flags: every
subsequent break_lease() on the same delegation hits the early-return
guard in nfsd_break_one_deleg() and silently skips the recall, so the
delegation is never broken and the conflicting open or lock stalls.

Fix by clearing NFSD4_CALLBACK_RUNNING on the !queued branch alongside
the refcount_dec.

Fixes: 1054e8f ("nfsd: prevent callback tasks running concurrently")
Cc: stable@vger.kernel.org
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260526-cb_recall_any_callback_running_stuck-v1-2-310011a028f3@kernel.org
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
(cherry picked from commit fa183d59e531e016d4495e21f00d80c63aebd907)
@bugclerk bugclerk changed the title Fix nfsd client teardown hang from leaked callback count NAS-142112 / None / Fix nfsd client teardown hang from leaked callback count Aug 7, 2026
@bugclerk

bugclerk commented Aug 7, 2026

Copy link
Copy Markdown

@ixhamza
ixhamza merged commit 3e028a9 into truenas/linux-6.12 Aug 7, 2026
6 checks passed
@ixhamza
ixhamza deleted the SEE-589 branch August 7, 2026 18:33
@bugclerk

bugclerk commented Aug 7, 2026

Copy link
Copy Markdown

Not updating JIRA ticket https://ixsystems.atlassian.net/browse/NAS-142112 target versions as no JIRA version corresponds to this PR

@bugclerk

bugclerk commented Aug 7, 2026

Copy link
Copy Markdown

This PR has been merged and conversations have been locked.
If you would like to discuss more about this issue please use our forums or raise a Jira ticket.

@truenas truenas locked as resolved and limited conversation to collaborators Aug 7, 2026
@ixhamza
ixhamza restored the SEE-589 branch August 12, 2026 19:39
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants