Skip to content

Commit

Permalink
nfsd: add shrinker to reduce number of slots allocated per session
Browse files Browse the repository at this point in the history
Add a shrinker which frees unused slots and may ask the clients to use
fewer slots on each session.

We keep a global count of the number of freeable slots, which is the sum
of one less than the current "target" slots in all sessions in all
clients in all net-namespaces. This number is reported by the shrinker.

When the shrinker is asked to free some, we call xxx on each session in
a round-robin asking each to reduce the slot count by 1.  This will
reduce the "target" so the number reported by the shrinker will reduce
immediately.  The memory will only be freed later when the client
confirmed that it is no longer needed.

We use a global list of sessions and move the "head" to after the last
session that we asked to reduce, so the next callback from the shrinker
will move on to the next session.  This pressure should be applied
"evenly" across all sessions over time.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
  • Loading branch information
NeilBrown authored and Chuck Lever committed Jan 6, 2025
1 parent fc8738c commit 35e3464
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
77 changes: 72 additions & 5 deletions fs/nfsd/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,16 @@ gen_sessionid(struct nfsd4_session *ses)
*/
#define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)

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.
*/
static atomic_t nfsd_total_target_slots = ATOMIC_INIT(0);

static void
free_session_slots(struct nfsd4_session *ses, int from)
{
Expand All @@ -1930,8 +1940,11 @@ free_session_slots(struct nfsd4_session *ses, int from)
kfree(slot);
}
ses->se_fchannel.maxreqs = from;
if (ses->se_target_maxslots > from)
ses->se_target_maxslots = 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;
}
}

/**
Expand All @@ -1949,7 +1962,7 @@ free_session_slots(struct nfsd4_session *ses, int from)
* Return value:
* The number of slots that the target was reduced by.
*/
static int __maybe_unused
static int
reduce_session_slots(struct nfsd4_session *ses, int dec)
{
struct nfsd_net *nn = net_generic(ses->se_client->net,
Expand All @@ -1962,6 +1975,7 @@ reduce_session_slots(struct nfsd4_session *ses, int dec)
return ret;
ret = min(dec, ses->se_target_maxslots-1);
ses->se_target_maxslots -= ret;
atomic_sub(ret, &nfsd_total_target_slots);
ses->se_slot_gen += 1;
if (ses->se_slot_gen == 0) {
int i;
Expand Down Expand Up @@ -2021,6 +2035,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);
new->se_cb_slot_avail = ~0U;
new->se_cb_highest_slot = min(battrs->maxreqs - 1,
NFSD_BC_SLOT_TABLE_SIZE - 1);
Expand Down Expand Up @@ -2145,6 +2160,36 @@ static void free_session(struct nfsd4_session *ses)
__free_session(ses);
}

static unsigned long
nfsd_slot_count(struct shrinker *s, struct shrink_control *sc)
{
unsigned long cnt = atomic_read(&nfsd_total_target_slots);

return cnt ? cnt : SHRINK_EMPTY;
}

static unsigned long
nfsd_slot_scan(struct shrinker *s, struct shrink_control *sc)
{
struct nfsd4_session *ses;
unsigned long scanned = 0;
unsigned long freed = 0;

spin_lock(&nfsd_session_list_lock);
list_for_each_entry(ses, &nfsd_session_list, se_all_sessions) {
freed += reduce_session_slots(ses, 1);
scanned += 1;
if (scanned >= sc->nr_to_scan) {
/* Move starting point for next scan */
list_move(&nfsd_session_list, &ses->se_all_sessions);
break;
}
}
spin_unlock(&nfsd_session_list_lock);
sc->nr_scanned = scanned;
return freed;
}

static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
{
int idx;
Expand All @@ -2169,6 +2214,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&clp->cl_lock);

spin_lock(&nfsd_session_list_lock);
list_add_tail(&new->se_all_sessions, &nfsd_session_list);
spin_unlock(&nfsd_session_list_lock);

{
struct sockaddr *sa = svc_addr(rqstp);
/*
Expand Down Expand Up @@ -2238,6 +2287,9 @@ unhash_session(struct nfsd4_session *ses)
spin_lock(&ses->se_client->cl_lock);
list_del(&ses->se_perclnt);
spin_unlock(&ses->se_client->cl_lock);
spin_lock(&nfsd_session_list_lock);
list_del(&ses->se_all_sessions);
spin_unlock(&nfsd_session_list_lock);
}

/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
Expand Down Expand Up @@ -2373,8 +2425,12 @@ unhash_client_locked(struct nfs4_client *clp)
}
list_del_init(&clp->cl_lru);
spin_lock(&clp->cl_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
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);
}
spin_unlock(&nfsd_session_list_lock);
spin_unlock(&clp->cl_lock);
}

Expand Down Expand Up @@ -4380,6 +4436,8 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
GFP_NOWAIT))) {
s += 1;
session->se_fchannel.maxreqs = s;
atomic_add(s - session->se_target_maxslots,
&nfsd_total_target_slots);
session->se_target_maxslots = s;
} else {
kfree(slot);
Expand Down Expand Up @@ -8770,7 +8828,6 @@ nfs4_state_start_net(struct net *net)
}

/* initialization to perform when the nfsd service is started: */

int
nfs4_state_start(void)
{
Expand All @@ -8780,6 +8837,15 @@ nfs4_state_start(void)
if (ret)
return ret;

nfsd_slot_shrinker = shrinker_alloc(0, "nfsd-DRC-slot");
if (!nfsd_slot_shrinker) {
rhltable_destroy(&nfs4_file_rhltable);
return -ENOMEM;
}
nfsd_slot_shrinker->count_objects = nfsd_slot_count;
nfsd_slot_shrinker->scan_objects = nfsd_slot_scan;
shrinker_register(nfsd_slot_shrinker);

set_max_delegations();
return 0;
}
Expand Down Expand Up @@ -8821,6 +8887,7 @@ void
nfs4_state_shutdown(void)
{
rhltable_destroy(&nfs4_file_rhltable);
shrinker_free(nfsd_slot_shrinker);
}

static void
Expand Down
1 change: 1 addition & 0 deletions fs/nfsd/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ struct nfsd4_session {
u32 se_cb_prog;
struct list_head se_hash; /* hash by sessionid */
struct list_head se_perclnt;
struct list_head se_all_sessions;/* global list of sessions */
struct nfs4_client *se_client;
struct nfs4_sessionid se_sessionid;
struct nfsd4_channel_attrs se_fchannel;
Expand Down

0 comments on commit 35e3464

Please sign in to comment.