Commit a7455442 authored by Greg Banks's avatar Greg Banks Committed by Linus Torvalds
Browse files

[PATCH] knfsd: add svc_set_num_threads



Currently knfsd keeps its own list of all nfsd threads in nfssvc.c; add a new
way of managing the list of all threads in a svc_serv.  Add
svc_create_pooled() to allow creation of a svc_serv whose threads are managed
by the sunrpc code.  Add svc_set_num_threads() to manage the number of threads
in a service, either per-pool or globally across the service.

Signed-off-by: default avatarGreg Banks <gnb@melbourne.sgi.com>
Signed-off-by: default avatarNeil Brown <neilb@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 9a24ab57
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -17,6 +17,10 @@
#include <linux/wait.h>
#include <linux/mm.h>

/*
 * This is the RPC server thread function prototype
 */
typedef void		(*svc_thread_fn)(struct svc_rqst *);

/*
 *
@@ -34,6 +38,7 @@ struct svc_pool {
	struct list_head	sp_threads;	/* idle server threads */
	struct list_head	sp_sockets;	/* pending sockets */
	unsigned int		sp_nrthreads;	/* # of threads in pool */
	struct list_head	sp_all_threads;	/* all server threads */
} ____cacheline_aligned_in_smp;

/*
@@ -68,6 +73,11 @@ struct svc_serv {
						/* Callback to use when last thread
						 * exits.
						 */

	struct module *		sv_module;	/* optional module to count when
						 * adding threads */
	svc_thread_fn		sv_function;	/* main function for threads */
	int			sv_kill_signal;	/* signal to kill threads */
};

/*
@@ -164,6 +174,7 @@ static inline void svc_putu32(struct kvec *iov, __be32 val)
 */
struct svc_rqst {
	struct list_head	rq_list;	/* idle list */
	struct list_head	rq_all;		/* all threads list */
	struct svc_sock *	rq_sock;	/* socket */
	struct sockaddr_in	rq_addr;	/* peer address */
	int			rq_addrlen;
@@ -218,6 +229,7 @@ struct svc_rqst {
						 * to prevent encrypting page
						 * cache pages */
	wait_queue_head_t	rq_wait;	/* synchronization */
	struct task_struct	*rq_task;	/* service thread */
};

/*
@@ -358,11 +370,6 @@ struct svc_procedure {
	unsigned int		pc_xdrressize;	/* maximum size of XDR reply */
};

/*
 * This is the RPC server thread function prototype
 */
typedef void		(*svc_thread_fn)(struct svc_rqst *);

/*
 * Function prototypes.
 */
@@ -370,6 +377,10 @@ struct svc_serv * svc_create(struct svc_program *, unsigned int,
			      void (*shutdown)(struct svc_serv*));
int		   svc_create_thread(svc_thread_fn, struct svc_serv *);
void		   svc_exit_thread(struct svc_rqst *);
struct svc_serv *  svc_create_pooled(struct svc_program *, unsigned int,
			void (*shutdown)(struct svc_serv*),
			svc_thread_fn, int sig, struct module *);
int		   svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
void		   svc_destroy(struct svc_serv *);
int		   svc_process(struct svc_rqst *);
int		   svc_register(struct svc_serv *, int, unsigned short);
+2 −0
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@ EXPORT_SYMBOL(put_rpccred);
/* RPC server stuff */
EXPORT_SYMBOL(svc_create);
EXPORT_SYMBOL(svc_create_thread);
EXPORT_SYMBOL(svc_create_pooled);
EXPORT_SYMBOL(svc_set_num_threads);
EXPORT_SYMBOL(svc_exit_thread);
EXPORT_SYMBOL(svc_destroy);
EXPORT_SYMBOL(svc_drop);
+136 −3
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#include <linux/net.h>
#include <linux/in.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/module.h>

#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
@@ -25,8 +27,8 @@
/*
 * Create an RPC service
 */
struct svc_serv *
svc_create(struct svc_program *prog, unsigned int bufsize,
static struct svc_serv *
__svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
	   void (*shutdown)(struct svc_serv *serv))
{
	struct svc_serv	*serv;
@@ -61,7 +63,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize,
	init_timer(&serv->sv_temptimer);
	spin_lock_init(&serv->sv_lock);

	serv->sv_nrpools = 1;
	serv->sv_nrpools = npools;
	serv->sv_pools =
		kcalloc(sizeof(struct svc_pool), serv->sv_nrpools,
			GFP_KERNEL);
@@ -79,6 +81,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize,
		pool->sp_id = i;
		INIT_LIST_HEAD(&pool->sp_threads);
		INIT_LIST_HEAD(&pool->sp_sockets);
		INIT_LIST_HEAD(&pool->sp_all_threads);
		spin_lock_init(&pool->sp_lock);
	}

@@ -89,6 +92,31 @@ svc_create(struct svc_program *prog, unsigned int bufsize,
	return serv;
}

struct svc_serv *
svc_create(struct svc_program *prog, unsigned int bufsize,
		void (*shutdown)(struct svc_serv *serv))
{
	return __svc_create(prog, bufsize, /*npools*/1, shutdown);
}

struct svc_serv *
svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
		void (*shutdown)(struct svc_serv *serv),
		  svc_thread_fn func, int sig, struct module *mod)
{
	struct svc_serv *serv;

	serv = __svc_create(prog, bufsize, /*npools*/1, shutdown);

	if (serv != NULL) {
		serv->sv_function = func;
		serv->sv_kill_signal = sig;
		serv->sv_module = mod;
	}

	return serv;
}

/*
 * Destroy an RPC service.  Should be called with the BKL held
 */
@@ -203,6 +231,7 @@ __svc_create_thread(svc_thread_fn func, struct svc_serv *serv,
	serv->sv_nrthreads++;
	spin_lock_bh(&pool->sp_lock);
	pool->sp_nrthreads++;
	list_add(&rqstp->rq_all, &pool->sp_all_threads);
	spin_unlock_bh(&pool->sp_lock);
	rqstp->rq_server = serv;
	rqstp->rq_pool = pool;
@@ -228,6 +257,109 @@ svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
	return __svc_create_thread(func, serv, &serv->sv_pools[0]);
}

/*
 * Choose a pool in which to create a new thread, for svc_set_num_threads
 */
static inline struct svc_pool *
choose_pool(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
{
	if (pool != NULL)
		return pool;

 	return &serv->sv_pools[(*state)++ % serv->sv_nrpools];
}

/*
 * Choose a thread to kill, for svc_set_num_threads
 */
static inline struct task_struct *
choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
{
	unsigned int i;
	struct task_struct *task = NULL;

	if (pool != NULL) {
		spin_lock_bh(&pool->sp_lock);
	} else {
		/* choose a pool in round-robin fashion */
 		for (i = 0; i < serv->sv_nrpools; i++) {
 			pool = &serv->sv_pools[--(*state) % serv->sv_nrpools];
			spin_lock_bh(&pool->sp_lock);
 			if (!list_empty(&pool->sp_all_threads))
 				goto found_pool;
			spin_unlock_bh(&pool->sp_lock);
 		}
		return NULL;
	}

found_pool:
	if (!list_empty(&pool->sp_all_threads)) {
		struct svc_rqst *rqstp;

		/*
		 * Remove from the pool->sp_all_threads list
		 * so we don't try to kill it again.
		 */
		rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all);
		list_del_init(&rqstp->rq_all);
		task = rqstp->rq_task;
    	}
	spin_unlock_bh(&pool->sp_lock);

	return task;
}

/*
 * 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.  Must be called with a svc_get() reference and
 * the BKL held.
 *
 * 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)
{
	struct task_struct *victim;
	int error = 0;
	unsigned int state = serv->sv_nrthreads-1;

	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);
	}

	/* create new threads */
	while (nrservs > 0) {
		nrservs--;
		__module_get(serv->sv_module);
		error = __svc_create_thread(serv->sv_function, serv,
					    choose_pool(serv, pool, &state));
		if (error < 0) {
			module_put(serv->sv_module);
			break;
		}
	}
	/* destroy old threads */
	while (nrservs < 0 &&
	       (victim = choose_victim(serv, pool, &state)) != NULL) {
		send_sig(serv->sv_kill_signal, victim, 1);
		nrservs++;
	}

	return error;
}

/*
 * Called from a server thread as it's exiting.  Caller must hold BKL.
 */
@@ -244,6 +376,7 @@ svc_exit_thread(struct svc_rqst *rqstp)

	spin_lock_bh(&pool->sp_lock);
	pool->sp_nrthreads--;
	list_del(&rqstp->rq_all);
	spin_unlock_bh(&pool->sp_lock);

	kfree(rqstp);