diff options
| -rw-r--r-- | fs/nfsd/nfs4state.c | 77 | ||||
| -rw-r--r-- | fs/nfsd/state.h | 1 | 
2 files changed, 73 insertions, 5 deletions
| diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d29737018f04..b38b3a1c0307 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -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)  { @@ -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; +	}  }  /** @@ -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, @@ -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; @@ -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); @@ -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; @@ -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);  		/* @@ -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 */ @@ -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);  } @@ -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); @@ -8770,7 +8828,6 @@ skip_grace:  }  /* initialization to perform when the nfsd service is started: */ -  int  nfs4_state_start(void)  { @@ -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;  } @@ -8821,6 +8887,7 @@ void  nfs4_state_shutdown(void)  {  	rhltable_destroy(&nfs4_file_rhltable); +	shrinker_free(nfsd_slot_shrinker);  }  static void diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 2b0e6148a87a..b31a8523c8e5 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -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; | 
