Skip to content

Commit 35e3464

Browse files
neilbrownchucklever
authored andcommitted
nfsd: add shrinker to reduce number of slots allocated per session
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 <[email protected]> Signed-off-by: NeilBrown <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent fc8738c commit 35e3464

File tree

2 files changed

+73
-5
lines changed

2 files changed

+73
-5
lines changed

fs/nfsd/nfs4state.c

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,16 @@ gen_sessionid(struct nfsd4_session *ses)
19091909
*/
19101910
#define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)
19111911

1912+
static struct shrinker *nfsd_slot_shrinker;
1913+
static DEFINE_SPINLOCK(nfsd_session_list_lock);
1914+
static LIST_HEAD(nfsd_session_list);
1915+
/* The sum of "target_slots-1" on every session. The shrinker can push this
1916+
* down, though it can take a little while for the memory to actually
1917+
* be freed. The "-1" is because we can never free slot 0 while the
1918+
* session is active.
1919+
*/
1920+
static atomic_t nfsd_total_target_slots = ATOMIC_INIT(0);
1921+
19121922
static void
19131923
free_session_slots(struct nfsd4_session *ses, int from)
19141924
{
@@ -1930,8 +1940,11 @@ free_session_slots(struct nfsd4_session *ses, int from)
19301940
kfree(slot);
19311941
}
19321942
ses->se_fchannel.maxreqs = from;
1933-
if (ses->se_target_maxslots > from)
1934-
ses->se_target_maxslots = from;
1943+
if (ses->se_target_maxslots > from) {
1944+
int new_target = from ?: 1;
1945+
atomic_sub(ses->se_target_maxslots - new_target, &nfsd_total_target_slots);
1946+
ses->se_target_maxslots = new_target;
1947+
}
19351948
}
19361949

19371950
/**
@@ -1949,7 +1962,7 @@ free_session_slots(struct nfsd4_session *ses, int from)
19491962
* Return value:
19501963
* The number of slots that the target was reduced by.
19511964
*/
1952-
static int __maybe_unused
1965+
static int
19531966
reduce_session_slots(struct nfsd4_session *ses, int dec)
19541967
{
19551968
struct nfsd_net *nn = net_generic(ses->se_client->net,
@@ -1962,6 +1975,7 @@ reduce_session_slots(struct nfsd4_session *ses, int dec)
19621975
return ret;
19631976
ret = min(dec, ses->se_target_maxslots-1);
19641977
ses->se_target_maxslots -= ret;
1978+
atomic_sub(ret, &nfsd_total_target_slots);
19651979
ses->se_slot_gen += 1;
19661980
if (ses->se_slot_gen == 0) {
19671981
int i;
@@ -2021,6 +2035,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
20212035
fattrs->maxreqs = i;
20222036
memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs));
20232037
new->se_target_maxslots = i;
2038+
atomic_add(i - 1, &nfsd_total_target_slots);
20242039
new->se_cb_slot_avail = ~0U;
20252040
new->se_cb_highest_slot = min(battrs->maxreqs - 1,
20262041
NFSD_BC_SLOT_TABLE_SIZE - 1);
@@ -2145,6 +2160,36 @@ static void free_session(struct nfsd4_session *ses)
21452160
__free_session(ses);
21462161
}
21472162

2163+
static unsigned long
2164+
nfsd_slot_count(struct shrinker *s, struct shrink_control *sc)
2165+
{
2166+
unsigned long cnt = atomic_read(&nfsd_total_target_slots);
2167+
2168+
return cnt ? cnt : SHRINK_EMPTY;
2169+
}
2170+
2171+
static unsigned long
2172+
nfsd_slot_scan(struct shrinker *s, struct shrink_control *sc)
2173+
{
2174+
struct nfsd4_session *ses;
2175+
unsigned long scanned = 0;
2176+
unsigned long freed = 0;
2177+
2178+
spin_lock(&nfsd_session_list_lock);
2179+
list_for_each_entry(ses, &nfsd_session_list, se_all_sessions) {
2180+
freed += reduce_session_slots(ses, 1);
2181+
scanned += 1;
2182+
if (scanned >= sc->nr_to_scan) {
2183+
/* Move starting point for next scan */
2184+
list_move(&nfsd_session_list, &ses->se_all_sessions);
2185+
break;
2186+
}
2187+
}
2188+
spin_unlock(&nfsd_session_list_lock);
2189+
sc->nr_scanned = scanned;
2190+
return freed;
2191+
}
2192+
21482193
static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
21492194
{
21502195
int idx;
@@ -2169,6 +2214,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
21692214
list_add(&new->se_perclnt, &clp->cl_sessions);
21702215
spin_unlock(&clp->cl_lock);
21712216

2217+
spin_lock(&nfsd_session_list_lock);
2218+
list_add_tail(&new->se_all_sessions, &nfsd_session_list);
2219+
spin_unlock(&nfsd_session_list_lock);
2220+
21722221
{
21732222
struct sockaddr *sa = svc_addr(rqstp);
21742223
/*
@@ -2238,6 +2287,9 @@ unhash_session(struct nfsd4_session *ses)
22382287
spin_lock(&ses->se_client->cl_lock);
22392288
list_del(&ses->se_perclnt);
22402289
spin_unlock(&ses->se_client->cl_lock);
2290+
spin_lock(&nfsd_session_list_lock);
2291+
list_del(&ses->se_all_sessions);
2292+
spin_unlock(&nfsd_session_list_lock);
22412293
}
22422294

22432295
/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
@@ -2373,8 +2425,12 @@ unhash_client_locked(struct nfs4_client *clp)
23732425
}
23742426
list_del_init(&clp->cl_lru);
23752427
spin_lock(&clp->cl_lock);
2376-
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
2428+
spin_lock(&nfsd_session_list_lock);
2429+
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) {
23772430
list_del_init(&ses->se_hash);
2431+
list_del_init(&ses->se_all_sessions);
2432+
}
2433+
spin_unlock(&nfsd_session_list_lock);
23782434
spin_unlock(&clp->cl_lock);
23792435
}
23802436

@@ -4380,6 +4436,8 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
43804436
GFP_NOWAIT))) {
43814437
s += 1;
43824438
session->se_fchannel.maxreqs = s;
4439+
atomic_add(s - session->se_target_maxslots,
4440+
&nfsd_total_target_slots);
43834441
session->se_target_maxslots = s;
43844442
} else {
43854443
kfree(slot);
@@ -8770,7 +8828,6 @@ nfs4_state_start_net(struct net *net)
87708828
}
87718829

87728830
/* initialization to perform when the nfsd service is started: */
8773-
87748831
int
87758832
nfs4_state_start(void)
87768833
{
@@ -8780,6 +8837,15 @@ nfs4_state_start(void)
87808837
if (ret)
87818838
return ret;
87828839

8840+
nfsd_slot_shrinker = shrinker_alloc(0, "nfsd-DRC-slot");
8841+
if (!nfsd_slot_shrinker) {
8842+
rhltable_destroy(&nfs4_file_rhltable);
8843+
return -ENOMEM;
8844+
}
8845+
nfsd_slot_shrinker->count_objects = nfsd_slot_count;
8846+
nfsd_slot_shrinker->scan_objects = nfsd_slot_scan;
8847+
shrinker_register(nfsd_slot_shrinker);
8848+
87838849
set_max_delegations();
87848850
return 0;
87858851
}
@@ -8821,6 +8887,7 @@ void
88218887
nfs4_state_shutdown(void)
88228888
{
88238889
rhltable_destroy(&nfs4_file_rhltable);
8890+
shrinker_free(nfsd_slot_shrinker);
88248891
}
88258892

88268893
static void

fs/nfsd/state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ struct nfsd4_session {
325325
u32 se_cb_prog;
326326
struct list_head se_hash; /* hash by sessionid */
327327
struct list_head se_perclnt;
328+
struct list_head se_all_sessions;/* global list of sessions */
328329
struct nfs4_client *se_client;
329330
struct nfs4_sessionid se_sessionid;
330331
struct nfsd4_channel_attrs se_fchannel;

0 commit comments

Comments
 (0)