From: Chuck Lever <cel@kernel.org>
To: Jeff Layton <jlayton@kernel.org>, NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Cc: linux-nfs@vger.kernel.org, Chuck Lever <cel@kernel.org>
Subject: [PATCH v5 6/8] NFSD: Give delegations their own state shrinker
Date: Mon, 17 Aug 2026 21:08:41 -0400 [thread overview]
Message-ID: <20260817-recall-any-keep-count-v5-6-3b2cffce701e@kernel.org> (raw)
In-Reply-To: <20260817-recall-any-keep-count-v5-0-3b2cffce701e@kernel.org>
Since commit 44df6f439a17 ("NFSD: add delegation reaper to react to
low memory condition"), nfsd_client_shrinker has managed two
unrelated populations of objects.
One population is courtesy clients. Shrinking that population can
be done synchronously and without risk of deadlock. The shrinker
callback could return a precise count of the number of objects
that were released.
The other population is delegations. Shrinking that population
requires sending a CB_RECALL_ANY; clients are not obligated to
return any delegation. The shrinker callback is structurally
unable to report progress.
What's more, the single shrinker callback falls back to
delegation reaping only when there are no courtesy clients left to
reclaim. A single courtesy client is enough to keep a namespace's
delegations out of the count it reports.
To begin to resolve these issues, refactor the existing state
shrinker into two: one for courtesy clients and one for reaping
delegations. Each manages the size of its own population, and the
shrinker names become namespace-specific.
Signed-off-by: Chuck Lever <cel@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/netns.h | 6 +++--
fs/nfsd/nfs4state.c | 77 ++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 65 insertions(+), 18 deletions(-)
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index bb62d19430bc..ef01a1cf72ac 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -240,8 +240,10 @@ struct nfsd_net {
atomic_t nfsd_courtesy_clients;
/* per-namespace; num_delegations in nfs4state.c is host-wide */
atomic_long_t nfsd_delegations;
- struct shrinker *nfsd_client_shrinker;
- struct work_struct nfsd_shrinker_work;
+ struct shrinker *nfsd_courtesy_shrinker;
+ struct shrinker *nfsd_deleg_shrinker;
+ struct work_struct nfsd_courtesy_work;
+ struct work_struct nfsd_deleg_work;
/* last time an admin-revoke happened for NFSv4.0 */
time64_t nfs40_last_revoke;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2c169bfabfbe..2dbc49a6dcaa 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -5562,16 +5562,27 @@ nfsd4_init_slabs(void)
}
static unsigned long
-nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
+nfsd4_courtesy_shrinker_count(struct shrinker *shrink,
+ struct shrink_control *sc)
{
struct nfsd_net *nn = shrink->private_data;
long count;
count = atomic_read(&nn->nfsd_courtesy_clients);
- if (!count)
- count = atomic_long_read(&nn->nfsd_delegations);
if (count)
- queue_work(laundry_wq, &nn->nfsd_shrinker_work);
+ queue_work(laundry_wq, &nn->nfsd_courtesy_work);
+ return (unsigned long)count;
+}
+
+static unsigned long
+nfsd4_deleg_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
+{
+ struct nfsd_net *nn = shrink->private_data;
+ long count;
+
+ count = atomic_long_read(&nn->nfsd_delegations);
+ if (count)
+ queue_work(laundry_wq, &nn->nfsd_deleg_work);
return (unsigned long)count;
}
@@ -5581,6 +5592,25 @@ nfsd4_state_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
return SHRINK_STOP;
}
+static struct shrinker *
+nfsd4_alloc_state_shrinker(struct nfsd_net *nn, const char *name,
+ unsigned long (*count)(struct shrinker *,
+ struct shrink_control *))
+{
+ struct shrinker *shrink;
+
+ shrink = shrinker_alloc(0, "%s:%s", name, nn->nfsd_name);
+ if (!shrink)
+ return NULL;
+
+ shrink->count_objects = count;
+ shrink->scan_objects = nfsd4_state_shrinker_scan;
+ shrink->private_data = nn;
+
+ shrinker_register(shrink);
+ return shrink;
+}
+
void
nfsd4_init_leases_net(struct nfsd_net *nn)
{
@@ -7994,12 +8024,20 @@ deleg_reaper(struct nfsd_net *nn)
}
static void
-nfsd4_state_shrinker_worker(struct work_struct *work)
+nfsd4_courtesy_shrinker_worker(struct work_struct *work)
{
struct nfsd_net *nn = container_of(work, struct nfsd_net,
- nfsd_shrinker_work);
+ nfsd_courtesy_work);
courtesy_client_reaper(nn);
+}
+
+static void
+nfsd4_deleg_shrinker_worker(struct work_struct *work)
+{
+ struct nfsd_net *nn = container_of(work, struct nfsd_net,
+ nfsd_deleg_work);
+
deleg_reaper(nn);
}
@@ -9962,21 +10000,26 @@ static int nfs4_state_create_net(struct net *net)
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
/* Make sure this cannot run until client tracking is initialised */
disable_delayed_work(&nn->laundromat_work);
- INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
+ INIT_WORK(&nn->nfsd_courtesy_work, nfsd4_courtesy_shrinker_worker);
+ INIT_WORK(&nn->nfsd_deleg_work, nfsd4_deleg_shrinker_worker);
get_net(net);
- nn->nfsd_client_shrinker = shrinker_alloc(0, "nfsd-client");
- if (!nn->nfsd_client_shrinker)
+ nn->nfsd_courtesy_shrinker =
+ nfsd4_alloc_state_shrinker(nn, "nfsd-courtesy",
+ nfsd4_courtesy_shrinker_count);
+ if (!nn->nfsd_courtesy_shrinker)
goto err_shrinker;
- nn->nfsd_client_shrinker->scan_objects = nfsd4_state_shrinker_scan;
- nn->nfsd_client_shrinker->count_objects = nfsd4_state_shrinker_count;
- nn->nfsd_client_shrinker->private_data = nn;
-
- shrinker_register(nn->nfsd_client_shrinker);
+ nn->nfsd_deleg_shrinker =
+ nfsd4_alloc_state_shrinker(nn, "nfsd-delegation",
+ nfsd4_deleg_shrinker_count);
+ if (!nn->nfsd_deleg_shrinker)
+ goto err_deleg_shrinker;
return 0;
+err_deleg_shrinker:
+ shrinker_free(nn->nfsd_courtesy_shrinker);
err_shrinker:
put_net(net);
kfree(nn->sessionid_hashtbl);
@@ -10077,8 +10120,10 @@ nfs4_state_shutdown_net(struct net *net)
struct list_head *pos, *next, reaplist;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- shrinker_free(nn->nfsd_client_shrinker);
- cancel_work_sync(&nn->nfsd_shrinker_work);
+ shrinker_free(nn->nfsd_courtesy_shrinker);
+ shrinker_free(nn->nfsd_deleg_shrinker);
+ cancel_work_sync(&nn->nfsd_courtesy_work);
+ cancel_work_sync(&nn->nfsd_deleg_work);
disable_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
--
2.54.0
next prev parent reply other threads:[~2026-08-18 1:08 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-18 1:08 [PATCH v5 0/8] NFSD: CB_RECALL_ANY fixes and a meaningful keep count Chuck Lever
2026-08-18 1:08 ` [PATCH v5 1/8] NFSD: Do not send CB_RECALL_ANY to NFSv4.0 clients Chuck Lever
2026-08-18 1:08 ` [PATCH v5 2/8] NFSD: Count the delegations held by each client Chuck Lever
2026-08-18 1:08 ` [PATCH v5 3/8] NFSD: Name directory delegations in the CB_RECALL_ANY type mask Chuck Lever
2026-08-18 1:08 ` [PATCH v5 4/8] NFSD: Send a meaningful CB_RECALL_ANY keep count Chuck Lever
2026-08-18 1:08 ` [PATCH v5 5/8] NFSD: Count delegations per network namespace Chuck Lever
2026-08-18 1:08 ` Chuck Lever [this message]
2026-08-18 1:08 ` [PATCH v5 7/8] NFSD: Pace the state shrinker's scan requests Chuck Lever
2026-08-18 1:08 ` [PATCH v5 8/8] NFSD: Apportion CB_RECALL_ANY recalls among clients Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260817-recall-any-keep-count-v5-6-3b2cffce701e@kernel.org \
--to=cel@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox