Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <jlayton@kernel.org>
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by

NeilBrown and committed by
Chuck Lever
35e34642 fc8738c6

+73 -5
+72 -5
fs/nfsd/nfs4state.c
··· 1909 1909 */ 1910 1910 #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44) 1911 1911 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 + 1912 1922 static void 1913 1923 free_session_slots(struct nfsd4_session *ses, int from) 1914 1924 { ··· 1940 1930 kfree(slot); 1941 1931 } 1942 1932 ses->se_fchannel.maxreqs = from; 1943 - if (ses->se_target_maxslots > from) 1944 - ses->se_target_maxslots = from; 1933 + if (ses->se_target_maxslots > from) { 1934 + int new_target = from ?: 1; 1935 + atomic_sub(ses->se_target_maxslots - new_target, &nfsd_total_target_slots); 1936 + ses->se_target_maxslots = new_target; 1937 + } 1945 1938 } 1946 1939 1947 1940 /** ··· 1962 1949 * Return value: 1963 1950 * The number of slots that the target was reduced by. 1964 1951 */ 1965 - static int __maybe_unused 1952 + static int 1966 1953 reduce_session_slots(struct nfsd4_session *ses, int dec) 1967 1954 { 1968 1955 struct nfsd_net *nn = net_generic(ses->se_client->net, ··· 1975 1962 return ret; 1976 1963 ret = min(dec, ses->se_target_maxslots-1); 1977 1964 ses->se_target_maxslots -= ret; 1965 + atomic_sub(ret, &nfsd_total_target_slots); 1978 1966 ses->se_slot_gen += 1; 1979 1967 if (ses->se_slot_gen == 0) { 1980 1968 int i; ··· 2035 2021 fattrs->maxreqs = i; 2036 2022 memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs)); 2037 2023 new->se_target_maxslots = i; 2024 + atomic_add(i - 1, &nfsd_total_target_slots); 2038 2025 new->se_cb_slot_avail = ~0U; 2039 2026 new->se_cb_highest_slot = min(battrs->maxreqs - 1, 2040 2027 NFSD_BC_SLOT_TABLE_SIZE - 1); ··· 2160 2145 __free_session(ses); 2161 2146 } 2162 2147 2148 + static unsigned long 2149 + nfsd_slot_count(struct shrinker *s, struct shrink_control *sc) 2150 + { 2151 + unsigned long cnt = atomic_read(&nfsd_total_target_slots); 2152 + 2153 + return cnt ? cnt : SHRINK_EMPTY; 2154 + } 2155 + 2156 + static unsigned long 2157 + nfsd_slot_scan(struct shrinker *s, struct shrink_control *sc) 2158 + { 2159 + struct nfsd4_session *ses; 2160 + unsigned long scanned = 0; 2161 + unsigned long freed = 0; 2162 + 2163 + spin_lock(&nfsd_session_list_lock); 2164 + list_for_each_entry(ses, &nfsd_session_list, se_all_sessions) { 2165 + freed += reduce_session_slots(ses, 1); 2166 + scanned += 1; 2167 + if (scanned >= sc->nr_to_scan) { 2168 + /* Move starting point for next scan */ 2169 + list_move(&nfsd_session_list, &ses->se_all_sessions); 2170 + break; 2171 + } 2172 + } 2173 + spin_unlock(&nfsd_session_list_lock); 2174 + sc->nr_scanned = scanned; 2175 + return freed; 2176 + } 2177 + 2163 2178 static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) 2164 2179 { 2165 2180 int idx; ··· 2213 2168 spin_lock(&clp->cl_lock); 2214 2169 list_add(&new->se_perclnt, &clp->cl_sessions); 2215 2170 spin_unlock(&clp->cl_lock); 2171 + 2172 + spin_lock(&nfsd_session_list_lock); 2173 + list_add_tail(&new->se_all_sessions, &nfsd_session_list); 2174 + spin_unlock(&nfsd_session_list_lock); 2216 2175 2217 2176 { 2218 2177 struct sockaddr *sa = svc_addr(rqstp); ··· 2287 2238 spin_lock(&ses->se_client->cl_lock); 2288 2239 list_del(&ses->se_perclnt); 2289 2240 spin_unlock(&ses->se_client->cl_lock); 2241 + spin_lock(&nfsd_session_list_lock); 2242 + list_del(&ses->se_all_sessions); 2243 + spin_unlock(&nfsd_session_list_lock); 2290 2244 } 2291 2245 2292 2246 /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ ··· 2425 2373 } 2426 2374 list_del_init(&clp->cl_lru); 2427 2375 spin_lock(&clp->cl_lock); 2428 - list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) 2376 + spin_lock(&nfsd_session_list_lock); 2377 + list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) { 2429 2378 list_del_init(&ses->se_hash); 2379 + list_del_init(&ses->se_all_sessions); 2380 + } 2381 + spin_unlock(&nfsd_session_list_lock); 2430 2382 spin_unlock(&clp->cl_lock); 2431 2383 } 2432 2384 ··· 4436 4380 GFP_NOWAIT))) { 4437 4381 s += 1; 4438 4382 session->se_fchannel.maxreqs = s; 4383 + atomic_add(s - session->se_target_maxslots, 4384 + &nfsd_total_target_slots); 4439 4385 session->se_target_maxslots = s; 4440 4386 } else { 4441 4387 kfree(slot); ··· 8828 8770 } 8829 8771 8830 8772 /* initialization to perform when the nfsd service is started: */ 8831 - 8832 8773 int 8833 8774 nfs4_state_start(void) 8834 8775 { ··· 8836 8779 ret = rhltable_init(&nfs4_file_rhltable, &nfs4_file_rhash_params); 8837 8780 if (ret) 8838 8781 return ret; 8782 + 8783 + nfsd_slot_shrinker = shrinker_alloc(0, "nfsd-DRC-slot"); 8784 + if (!nfsd_slot_shrinker) { 8785 + rhltable_destroy(&nfs4_file_rhltable); 8786 + return -ENOMEM; 8787 + } 8788 + nfsd_slot_shrinker->count_objects = nfsd_slot_count; 8789 + nfsd_slot_shrinker->scan_objects = nfsd_slot_scan; 8790 + shrinker_register(nfsd_slot_shrinker); 8839 8791 8840 8792 set_max_delegations(); 8841 8793 return 0; ··· 8887 8821 nfs4_state_shutdown(void) 8888 8822 { 8889 8823 rhltable_destroy(&nfs4_file_rhltable); 8824 + shrinker_free(nfsd_slot_shrinker); 8890 8825 } 8891 8826 8892 8827 static void
+1
fs/nfsd/state.h
··· 325 325 u32 se_cb_prog; 326 326 struct list_head se_hash; /* hash by sessionid */ 327 327 struct list_head se_perclnt; 328 + struct list_head se_all_sessions;/* global list of sessions */ 328 329 struct nfs4_client *se_client; 329 330 struct nfs4_sessionid se_sessionid; 330 331 struct nfsd4_channel_attrs se_fchannel;