[PATCH] knfsd: add svc_set_num_threads
authorGreg Banks <gnb@melbourne.sgi.com>
Mon, 2 Oct 2006 09:17:59 +0000 (02:17 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 2 Oct 2006 14:57:19 +0000 (07:57 -0700)
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: Greg Banks <gnb@melbourne.sgi.com>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
include/linux/sunrpc/svc.h
net/sunrpc/sunrpc_syms.c
net/sunrpc/svc.c

index 54d8e7bc23411d51764686133f40517a3a38cdd2..f2eeb833e7d888b61af85bf325245af935b7d5fe 100644 (file)
 #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);
index 26c0531d7e253739d5e0a7b9f38225ac0e311da6..192dff5dabcbc76258a378179d7132f5e3845bf8 100644 (file)
@@ -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);
index 6750cd474f844b89fea05c3ab7ad2b649fc6393a..8c75eec4fd6aea5b4de54f1b49404681ff180584 100644 (file)
@@ -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);