NFSD: Count slot 0 in nfsd_total_target_slots

nfsd_total_target_slots sums "target_slots - 1" across sessions
rather than the full target. Its sole consumer, the NFSv4.1 session
slot shrinker's count callback, must report only reclaimable slots,
and slot 0 is never reclaimable while a session is active. That
correction is open-coded where a session's full target enters and
leaves the counter, as "i - 1" on alloc and "from ?: 1" on free,
and reads as an unexplained fudge.

Give nfsd_total_target_slots the full-target meaning its name
implies, and move the reclaimability correction to the single place
that consumes it: nfsd_slot_count() subtracts nfsd_total_sessions, a
new tally of the sessions on nfsd_session_list. One correction at the
consumer is clearer than repeating it wherever a session's target
enters or leaves the counter.

The reclaimable figure the shrinker sees is unchanged: slot 0 was
never reclaimable and still is not. The change only relocates the
minus-slot-0 correction.

Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Benjamin Coddington <bcodding@hammerspace.com>
Link: https://patch.msgid.link/20260610-nfsd-slot-growth-clamp-v1-2-7b966700df0b@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
Chuck Lever 2026-06-10 21:58:54 -04:00
parent 69d1b05ca0
commit 9f4412ba30

View File

@ -2052,12 +2052,10 @@ gen_sessionid(struct nfsd4_session *ses)
static struct shrinker *nfsd_slot_shrinker;
static DEFINE_SPINLOCK(nfsd_session_list_lock);
static LIST_HEAD(nfsd_session_list);
/* The sum of "target_slots-1" on every session. The shrinker can push this
* down, though it can take a little while for the memory to actually
* be freed. The "-1" is because we can never free slot 0 while the
* session is active.
*/
/* The sum of "target_slots" on every session, slot 0 included. */
static atomic_t nfsd_total_target_slots = ATOMIC_INIT(0);
/* Session count, subtracted from the sum to exclude slot 0. */
static atomic_t nfsd_total_sessions = ATOMIC_INIT(0);
static void
free_session_slots(struct nfsd4_session *ses, int from)
@ -2081,9 +2079,11 @@ free_session_slots(struct nfsd4_session *ses, int from)
}
ses->se_fchannel.maxreqs = from;
if (ses->se_target_maxslots > from) {
int new_target = from ?: 1;
atomic_sub(ses->se_target_maxslots - new_target, &nfsd_total_target_slots);
ses->se_target_maxslots = new_target;
int delta = ses->se_target_maxslots - from;
atomic_sub(delta, &nfsd_total_target_slots);
/* Retain one slot so the session can make forward progress. */
ses->se_target_maxslots = from ?: 1;
}
}
@ -2179,7 +2179,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
fattrs->maxreqs = i;
memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs));
new->se_target_maxslots = i;
atomic_add(i - 1, &nfsd_total_target_slots);
atomic_add(i, &nfsd_total_target_slots);
new->se_cb_slot_avail = ~0U;
new->se_cb_highest_slot = min(battrs->maxreqs - 1,
NFSD_BC_SLOT_TABLE_SIZE - 1);
@ -2307,9 +2307,17 @@ static void free_session(struct nfsd4_session *ses)
static unsigned long
nfsd_slot_count(struct shrinker *s, struct shrink_control *sc)
{
unsigned long cnt = atomic_read(&nfsd_total_target_slots);
int count;
return cnt ? cnt : SHRINK_EMPTY;
/*
* To prevent session deadlock, one slot of each session (slot 0)
* is not reclaimable while the session is active. Thus the number
* of sessions is subtracted from the total number of target slots.
*/
count = atomic_read(&nfsd_total_target_slots) -
atomic_read(&nfsd_total_sessions);
return count > 0 ? count : SHRINK_EMPTY;
}
static unsigned long
@ -2360,6 +2368,7 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
spin_lock(&nfsd_session_list_lock);
list_add_tail(&new->se_all_sessions, &nfsd_session_list);
atomic_inc(&nfsd_total_sessions);
spin_unlock(&nfsd_session_list_lock);
{
@ -2433,6 +2442,7 @@ unhash_session(struct nfsd4_session *ses)
spin_unlock(&ses->se_client->cl_lock);
spin_lock(&nfsd_session_list_lock);
list_del(&ses->se_all_sessions);
atomic_dec(&nfsd_total_sessions);
spin_unlock(&nfsd_session_list_lock);
}
@ -2581,7 +2591,17 @@ unhash_client_locked(struct nfs4_client *clp)
spin_lock(&nfsd_session_list_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) {
list_del_init(&ses->se_hash);
list_del_init(&ses->se_all_sessions);
/*
* unhash_client_locked() can run more than once for a
* client; the session stays on cl_sessions across calls.
* The first pass empties se_all_sessions via
* list_del_init(), so skip the decrement on later passes
* to keep nfsd_total_sessions from being double-counted.
*/
if (!list_empty(&ses->se_all_sessions)) {
list_del_init(&ses->se_all_sessions);
atomic_dec(&nfsd_total_sessions);
}
}
spin_unlock(&nfsd_session_list_lock);
spin_unlock(&clp->cl_lock);