diff options
Diffstat (limited to 'net/sunrpc/svc.c')
| -rw-r--r-- | net/sunrpc/svc.c | 175 | 
1 files changed, 74 insertions, 101 deletions
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 4292278a9552..2aabec2b4bec 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -37,18 +37,37 @@  static void svc_unregister(const struct svc_serv *serv, struct net *net); -#define svc_serv_is_pooled(serv)    ((serv)->sv_ops->svo_function) -  #define SVC_POOL_DEFAULT	SVC_POOL_GLOBAL  /* + * Mode for mapping cpus to pools. + */ +enum { +	SVC_POOL_AUTO = -1,	/* choose one of the others */ +	SVC_POOL_GLOBAL,	/* no mapping, just a single global pool +				 * (legacy & UP mode) */ +	SVC_POOL_PERCPU,	/* one pool per cpu */ +	SVC_POOL_PERNODE	/* one pool per numa node */ +}; + +/*   * Structure for mapping cpus to pools and vice versa.   * Setup once during sunrpc initialisation.   */ -struct svc_pool_map svc_pool_map = { + +struct svc_pool_map { +	int count;			/* How many svc_servs use us */ +	int mode;			/* Note: int not enum to avoid +					 * warnings about "enumeration value +					 * not handled in switch" */ +	unsigned int npools; +	unsigned int *pool_to;		/* maps pool id to cpu or node */ +	unsigned int *to_pool;		/* maps cpu or node to pool id */ +}; + +static struct svc_pool_map svc_pool_map = {  	.mode = SVC_POOL_DEFAULT  }; -EXPORT_SYMBOL_GPL(svc_pool_map);  static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */ @@ -219,10 +238,12 @@ svc_pool_map_init_pernode(struct svc_pool_map *m)  /*   * Add a reference to the global map of cpus to pools (and - * vice versa).  Initialise the map if we're the first user. - * Returns the number of pools. + * vice versa) if pools are in use. + * Initialise the map if we're the first user. + * Returns the number of pools. If this is '1', no reference + * was taken.   */ -unsigned int +static unsigned int  svc_pool_map_get(void)  {  	struct svc_pool_map *m = &svc_pool_map; @@ -232,6 +253,7 @@ svc_pool_map_get(void)  	if (m->count++) {  		mutex_unlock(&svc_pool_map_mutex); +		WARN_ON_ONCE(m->npools <= 1);  		return m->npools;  	} @@ -247,30 +269,36 @@ svc_pool_map_get(void)  		break;  	} -	if (npools < 0) { +	if (npools <= 0) {  		/* default, or memory allocation failure */  		npools = 1;  		m->mode = SVC_POOL_GLOBAL;  	}  	m->npools = npools; +	if (npools == 1) +		/* service is unpooled, so doesn't hold a reference */ +		m->count--; +  	mutex_unlock(&svc_pool_map_mutex); -	return m->npools; +	return npools;  } -EXPORT_SYMBOL_GPL(svc_pool_map_get);  /* - * Drop a reference to the global map of cpus to pools. + * Drop a reference to the global map of cpus to pools, if + * pools were in use, i.e. if npools > 1.   * When the last reference is dropped, the map data is   * freed; this allows the sysadmin to change the pool   * mode using the pool_mode module option without   * rebooting or re-loading sunrpc.ko.   */ -void -svc_pool_map_put(void) +static void +svc_pool_map_put(int npools)  {  	struct svc_pool_map *m = &svc_pool_map; +	if (npools <= 1) +		return;  	mutex_lock(&svc_pool_map_mutex);  	if (!--m->count) { @@ -283,7 +311,6 @@ svc_pool_map_put(void)  	mutex_unlock(&svc_pool_map_mutex);  } -EXPORT_SYMBOL_GPL(svc_pool_map_put);  static int svc_pool_map_get_node(unsigned int pidx)  { @@ -340,21 +367,18 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu)  	struct svc_pool_map *m = &svc_pool_map;  	unsigned int pidx = 0; -	/* -	 * An uninitialised map happens in a pure client when -	 * lockd is brought up, so silently treat it the -	 * same as SVC_POOL_GLOBAL. -	 */ -	if (svc_serv_is_pooled(serv)) { -		switch (m->mode) { -		case SVC_POOL_PERCPU: -			pidx = m->to_pool[cpu]; -			break; -		case SVC_POOL_PERNODE: -			pidx = m->to_pool[cpu_to_node(cpu)]; -			break; -		} +	if (serv->sv_nrpools <= 1) +		return serv->sv_pools; + +	switch (m->mode) { +	case SVC_POOL_PERCPU: +		pidx = m->to_pool[cpu]; +		break; +	case SVC_POOL_PERNODE: +		pidx = m->to_pool[cpu_to_node(cpu)]; +		break;  	} +  	return &serv->sv_pools[pidx % serv->sv_nrpools];  } @@ -435,7 +459,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,  		return NULL;  	serv->sv_name      = prog->pg_name;  	serv->sv_program   = prog; -	serv->sv_nrthreads = 1; +	kref_init(&serv->sv_refcnt);  	serv->sv_stats     = prog->pg_stats;  	if (bufsize > RPCSVC_MAXPAYLOAD)  		bufsize = RPCSVC_MAXPAYLOAD; @@ -507,7 +531,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize,  		goto out_err;  	return serv;  out_err: -	svc_pool_map_put(); +	svc_pool_map_put(npools);  	return NULL;  }  EXPORT_SYMBOL_GPL(svc_create_pooled); @@ -523,23 +547,14 @@ EXPORT_SYMBOL_GPL(svc_shutdown_net);  /*   * Destroy an RPC service. Should be called with appropriate locking to - * protect the sv_nrthreads, sv_permsocks and sv_tempsocks. + * protect sv_permsocks and sv_tempsocks.   */  void -svc_destroy(struct svc_serv *serv) +svc_destroy(struct kref *ref)  { -	dprintk("svc: svc_destroy(%s, %d)\n", -				serv->sv_program->pg_name, -				serv->sv_nrthreads); - -	if (serv->sv_nrthreads) { -		if (--(serv->sv_nrthreads) != 0) { -			svc_sock_update_bufs(serv); -			return; -		} -	} else -		printk("svc_destroy: no threads for serv=%p!\n", serv); +	struct svc_serv *serv = container_of(ref, struct svc_serv, sv_refcnt); +	dprintk("svc: svc_destroy(%s)\n", serv->sv_program->pg_name);  	del_timer_sync(&serv->sv_temptimer);  	/* @@ -551,8 +566,7 @@ svc_destroy(struct svc_serv *serv)  	cache_clean_deferred(serv); -	if (svc_serv_is_pooled(serv)) -		svc_pool_map_put(); +	svc_pool_map_put(serv->sv_nrpools);  	kfree(serv->sv_pools);  	kfree(serv); @@ -638,7 +652,7 @@ out_enomem:  }  EXPORT_SYMBOL_GPL(svc_rqst_alloc); -struct svc_rqst * +static struct svc_rqst *  svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)  {  	struct svc_rqst	*rqstp; @@ -647,14 +661,17 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)  	if (!rqstp)  		return ERR_PTR(-ENOMEM); -	serv->sv_nrthreads++; +	svc_get(serv); +	spin_lock_bh(&serv->sv_lock); +	serv->sv_nrthreads += 1; +	spin_unlock_bh(&serv->sv_lock); +  	spin_lock_bh(&pool->sp_lock);  	pool->sp_nrthreads++;  	list_add_rcu(&rqstp->rq_all, &pool->sp_all_threads);  	spin_unlock_bh(&pool->sp_lock);  	return rqstp;  } -EXPORT_SYMBOL_GPL(svc_prepare_thread);  /*   * Choose a pool in which to create a new thread, for svc_set_num_threads @@ -748,59 +765,13 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)  	return 0;  } - -/* destroy old threads */ -static int -svc_signal_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) -{ -	struct task_struct *task; -	unsigned int state = serv->sv_nrthreads-1; - -	/* destroy old threads */ -	do { -		task = choose_victim(serv, pool, &state); -		if (task == NULL) -			break; -		send_sig(SIGINT, task, 1); -		nrservs++; -	} while (nrservs < 0); - -	return 0; -} -  /*   * Create or destroy enough new threads to make the number   * of threads the given number.  If `pool' is non-NULL, applies   * only to threads in that pool, otherwise round-robins between   * all pools.  Caller must ensure that mutual exclusion between this and   * server startup or shutdown. - * - * Destroying threads relies on the service threads filling in - * rqstp->rq_task, which only the nfs ones do.  Assumes the serv - * has been created using svc_create_pooled(). - * - * Based on code that used to be in nfsd_svc() but tweaked - * to be pool-aware.   */ -int -svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) -{ -	if (pool == NULL) { -		/* The -1 assumes caller has done a svc_get() */ -		nrservs -= (serv->sv_nrthreads-1); -	} else { -		spin_lock_bh(&pool->sp_lock); -		nrservs -= pool->sp_nrthreads; -		spin_unlock_bh(&pool->sp_lock); -	} - -	if (nrservs > 0) -		return svc_start_kthreads(serv, pool, nrservs); -	if (nrservs < 0) -		return svc_signal_kthreads(serv, pool, nrservs); -	return 0; -} -EXPORT_SYMBOL_GPL(svc_set_num_threads);  /* destroy old threads */  static int @@ -821,11 +792,10 @@ svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)  }  int -svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrservs) +svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)  {  	if (pool == NULL) { -		/* The -1 assumes caller has done a svc_get() */ -		nrservs -= (serv->sv_nrthreads-1); +		nrservs -= serv->sv_nrthreads;  	} else {  		spin_lock_bh(&pool->sp_lock);  		nrservs -= pool->sp_nrthreads; @@ -838,7 +808,7 @@ svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrser  		return svc_stop_kthreads(serv, pool, nrservs);  	return 0;  } -EXPORT_SYMBOL_GPL(svc_set_num_threads_sync); +EXPORT_SYMBOL_GPL(svc_set_num_threads);  /**   * svc_rqst_replace_page - Replace one page in rq_pages[] @@ -890,11 +860,14 @@ svc_exit_thread(struct svc_rqst *rqstp)  		list_del_rcu(&rqstp->rq_all);  	spin_unlock_bh(&pool->sp_lock); +	spin_lock_bh(&serv->sv_lock); +	serv->sv_nrthreads -= 1; +	spin_unlock_bh(&serv->sv_lock); +	svc_sock_update_bufs(serv); +  	svc_rqst_free(rqstp); -	/* Release the server */ -	if (serv) -		svc_destroy(serv); +	svc_put(serv);  }  EXPORT_SYMBOL_GPL(svc_exit_thread);  |