NFSD: Count delegations per network namespace

The state shrinker is allocated per network namespace, but
nfsd4_state_shrinker_count() reports num_delegations, which counts
the delegations held by the whole host. Every namespace therefore
reports every delegation on the server. Reclaim sees the
population multiplied by the number of namespaces running NFSD. A
namespace holding no delegations of its own still reports a
nonzero count and queues its reaper, which then finds nothing to
recall.

Count the delegations in each namespace and report that instead.
num_delegations stays for the admission check in
__alloc_init_deleg() and the ceiling check in nfs4_laundromat().
Both compare against max_delegations, which is sized from host
memory and so remains a host-wide limit.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260817-recall-any-keep-count-v5-5-3b2cffce701e@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
Chuck Lever 2026-08-17 21:08:40 -04:00
parent 9af39c0773
commit dda5168447
2 changed files with 8 additions and 1 deletions

View File

@ -238,6 +238,8 @@ struct nfsd_net {
int nfs4_max_clients;
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;

View File

@ -1161,6 +1161,7 @@ static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp)
*/
static void nfs4_free_deleg(struct nfs4_stid *stid)
{
struct nfsd_net *nn = net_generic(stid->sc_client->net, nfsd_net_id);
struct nfs4_delegation *dp = delegstateid(stid);
WARN_ON_ONCE(!list_empty(&stid->sc_cp_list));
@ -1171,6 +1172,7 @@ static void nfs4_free_deleg(struct nfs4_stid *stid)
nfsd41_cb_destroy_referring_call_list(&dp->dl_recall);
kmem_cache_free(deleg_slab, stid);
atomic_long_dec(&num_delegations);
atomic_long_dec(&nn->nfsd_delegations);
}
/*
@ -1255,6 +1257,7 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
struct nfs4_clnt_odstate *odstate, u32 dl_type,
void (*sc_free)(struct nfs4_stid *))
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfs4_delegation *dp;
struct nfs4_stid *stid;
long n;
@ -1263,6 +1266,7 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
return NULL;
n = atomic_long_inc_return(&num_delegations);
atomic_long_inc(&nn->nfsd_delegations);
if (n < 0 || n > max_delegations)
goto out_dec;
@ -1295,6 +1299,7 @@ __alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
return dp;
out_dec:
atomic_long_dec(&num_delegations);
atomic_long_dec(&nn->nfsd_delegations);
return NULL;
}
@ -5581,7 +5586,7 @@ nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
count = atomic_read(&nn->nfsd_courtesy_clients);
if (!count)
count = atomic_long_read(&num_delegations);
count = atomic_long_read(&nn->nfsd_delegations);
if (count)
queue_work(laundry_wq, &nn->nfsd_shrinker_work);
return (unsigned long)count;