diff options
-rw-r--r-- | include/linux/sunrpc/svc.h | 21 | ||||
-rw-r--r-- | net/sunrpc/sunrpc_syms.c | 2 | ||||
-rw-r--r-- | net/sunrpc/svc.c | 139 |
3 files changed, 154 insertions, 8 deletions
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 54d8e7bc2341..f2eeb833e7d8 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h | |||
@@ -17,6 +17,10 @@ | |||
17 | #include <linux/wait.h> | 17 | #include <linux/wait.h> |
18 | #include <linux/mm.h> | 18 | #include <linux/mm.h> |
19 | 19 | ||
20 | /* | ||
21 | * This is the RPC server thread function prototype | ||
22 | */ | ||
23 | typedef void (*svc_thread_fn)(struct svc_rqst *); | ||
20 | 24 | ||
21 | /* | 25 | /* |
22 | * | 26 | * |
@@ -34,6 +38,7 @@ struct svc_pool { | |||
34 | struct list_head sp_threads; /* idle server threads */ | 38 | struct list_head sp_threads; /* idle server threads */ |
35 | struct list_head sp_sockets; /* pending sockets */ | 39 | struct list_head sp_sockets; /* pending sockets */ |
36 | unsigned int sp_nrthreads; /* # of threads in pool */ | 40 | unsigned int sp_nrthreads; /* # of threads in pool */ |
41 | struct list_head sp_all_threads; /* all server threads */ | ||
37 | } ____cacheline_aligned_in_smp; | 42 | } ____cacheline_aligned_in_smp; |
38 | 43 | ||
39 | /* | 44 | /* |
@@ -68,6 +73,11 @@ struct svc_serv { | |||
68 | /* Callback to use when last thread | 73 | /* Callback to use when last thread |
69 | * exits. | 74 | * exits. |
70 | */ | 75 | */ |
76 | |||
77 | struct module * sv_module; /* optional module to count when | ||
78 | * adding threads */ | ||
79 | svc_thread_fn sv_function; /* main function for threads */ | ||
80 | int sv_kill_signal; /* signal to kill threads */ | ||
71 | }; | 81 | }; |
72 | 82 | ||
73 | /* | 83 | /* |
@@ -164,6 +174,7 @@ static inline void svc_putu32(struct kvec *iov, __be32 val) | |||
164 | */ | 174 | */ |
165 | struct svc_rqst { | 175 | struct svc_rqst { |
166 | struct list_head rq_list; /* idle list */ | 176 | struct list_head rq_list; /* idle list */ |
177 | struct list_head rq_all; /* all threads list */ | ||
167 | struct svc_sock * rq_sock; /* socket */ | 178 | struct svc_sock * rq_sock; /* socket */ |
168 | struct sockaddr_in rq_addr; /* peer address */ | 179 | struct sockaddr_in rq_addr; /* peer address */ |
169 | int rq_addrlen; | 180 | int rq_addrlen; |
@@ -218,6 +229,7 @@ struct svc_rqst { | |||
218 | * to prevent encrypting page | 229 | * to prevent encrypting page |
219 | * cache pages */ | 230 | * cache pages */ |
220 | wait_queue_head_t rq_wait; /* synchronization */ | 231 | wait_queue_head_t rq_wait; /* synchronization */ |
232 | struct task_struct *rq_task; /* service thread */ | ||
221 | }; | 233 | }; |
222 | 234 | ||
223 | /* | 235 | /* |
@@ -359,17 +371,16 @@ struct svc_procedure { | |||
359 | }; | 371 | }; |
360 | 372 | ||
361 | /* | 373 | /* |
362 | * This is the RPC server thread function prototype | ||
363 | */ | ||
364 | typedef void (*svc_thread_fn)(struct svc_rqst *); | ||
365 | |||
366 | /* | ||
367 | * Function prototypes. | 374 | * Function prototypes. |
368 | */ | 375 | */ |
369 | struct svc_serv * svc_create(struct svc_program *, unsigned int, | 376 | struct svc_serv * svc_create(struct svc_program *, unsigned int, |
370 | void (*shutdown)(struct svc_serv*)); | 377 | void (*shutdown)(struct svc_serv*)); |
371 | int svc_create_thread(svc_thread_fn, struct svc_serv *); | 378 | int svc_create_thread(svc_thread_fn, struct svc_serv *); |
372 | void svc_exit_thread(struct svc_rqst *); | 379 | void svc_exit_thread(struct svc_rqst *); |
380 | struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, | ||
381 | void (*shutdown)(struct svc_serv*), | ||
382 | svc_thread_fn, int sig, struct module *); | ||
383 | int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); | ||
373 | void svc_destroy(struct svc_serv *); | 384 | void svc_destroy(struct svc_serv *); |
374 | int svc_process(struct svc_rqst *); | 385 | int svc_process(struct svc_rqst *); |
375 | int svc_register(struct svc_serv *, int, unsigned short); | 386 | int svc_register(struct svc_serv *, int, unsigned short); |
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 26c0531d7e25..192dff5dabcb 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c | |||
@@ -70,6 +70,8 @@ EXPORT_SYMBOL(put_rpccred); | |||
70 | /* RPC server stuff */ | 70 | /* RPC server stuff */ |
71 | EXPORT_SYMBOL(svc_create); | 71 | EXPORT_SYMBOL(svc_create); |
72 | EXPORT_SYMBOL(svc_create_thread); | 72 | EXPORT_SYMBOL(svc_create_thread); |
73 | EXPORT_SYMBOL(svc_create_pooled); | ||
74 | EXPORT_SYMBOL(svc_set_num_threads); | ||
73 | EXPORT_SYMBOL(svc_exit_thread); | 75 | EXPORT_SYMBOL(svc_exit_thread); |
74 | EXPORT_SYMBOL(svc_destroy); | 76 | EXPORT_SYMBOL(svc_destroy); |
75 | EXPORT_SYMBOL(svc_drop); | 77 | EXPORT_SYMBOL(svc_drop); |
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 6750cd474f84..8c75eec4fd6a 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c | |||
@@ -12,6 +12,8 @@ | |||
12 | #include <linux/net.h> | 12 | #include <linux/net.h> |
13 | #include <linux/in.h> | 13 | #include <linux/in.h> |
14 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/module.h> | ||
15 | 17 | ||
16 | #include <linux/sunrpc/types.h> | 18 | #include <linux/sunrpc/types.h> |
17 | #include <linux/sunrpc/xdr.h> | 19 | #include <linux/sunrpc/xdr.h> |
@@ -25,8 +27,8 @@ | |||
25 | /* | 27 | /* |
26 | * Create an RPC service | 28 | * Create an RPC service |
27 | */ | 29 | */ |
28 | struct svc_serv * | 30 | static struct svc_serv * |
29 | svc_create(struct svc_program *prog, unsigned int bufsize, | 31 | __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, |
30 | void (*shutdown)(struct svc_serv *serv)) | 32 | void (*shutdown)(struct svc_serv *serv)) |
31 | { | 33 | { |
32 | struct svc_serv *serv; | 34 | struct svc_serv *serv; |
@@ -61,7 +63,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize, | |||
61 | init_timer(&serv->sv_temptimer); | 63 | init_timer(&serv->sv_temptimer); |
62 | spin_lock_init(&serv->sv_lock); | 64 | spin_lock_init(&serv->sv_lock); |
63 | 65 | ||
64 | serv->sv_nrpools = 1; | 66 | serv->sv_nrpools = npools; |
65 | serv->sv_pools = | 67 | serv->sv_pools = |
66 | kcalloc(sizeof(struct svc_pool), serv->sv_nrpools, | 68 | kcalloc(sizeof(struct svc_pool), serv->sv_nrpools, |
67 | GFP_KERNEL); | 69 | GFP_KERNEL); |
@@ -79,6 +81,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize, | |||
79 | pool->sp_id = i; | 81 | pool->sp_id = i; |
80 | INIT_LIST_HEAD(&pool->sp_threads); | 82 | INIT_LIST_HEAD(&pool->sp_threads); |
81 | INIT_LIST_HEAD(&pool->sp_sockets); | 83 | INIT_LIST_HEAD(&pool->sp_sockets); |
84 | INIT_LIST_HEAD(&pool->sp_all_threads); | ||
82 | spin_lock_init(&pool->sp_lock); | 85 | spin_lock_init(&pool->sp_lock); |
83 | } | 86 | } |
84 | 87 | ||
@@ -89,6 +92,31 @@ svc_create(struct svc_program *prog, unsigned int bufsize, | |||
89 | return serv; | 92 | return serv; |
90 | } | 93 | } |
91 | 94 | ||
95 | struct svc_serv * | ||
96 | svc_create(struct svc_program *prog, unsigned int bufsize, | ||
97 | void (*shutdown)(struct svc_serv *serv)) | ||
98 | { | ||
99 | return __svc_create(prog, bufsize, /*npools*/1, shutdown); | ||
100 | } | ||
101 | |||
102 | struct svc_serv * | ||
103 | svc_create_pooled(struct svc_program *prog, unsigned int bufsize, | ||
104 | void (*shutdown)(struct svc_serv *serv), | ||
105 | svc_thread_fn func, int sig, struct module *mod) | ||
106 | { | ||
107 | struct svc_serv *serv; | ||
108 | |||
109 | serv = __svc_create(prog, bufsize, /*npools*/1, shutdown); | ||
110 | |||
111 | if (serv != NULL) { | ||
112 | serv->sv_function = func; | ||
113 | serv->sv_kill_signal = sig; | ||
114 | serv->sv_module = mod; | ||
115 | } | ||
116 | |||
117 | return serv; | ||
118 | } | ||
119 | |||
92 | /* | 120 | /* |
93 | * Destroy an RPC service. Should be called with the BKL held | 121 | * Destroy an RPC service. Should be called with the BKL held |
94 | */ | 122 | */ |
@@ -203,6 +231,7 @@ __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, | |||
203 | serv->sv_nrthreads++; | 231 | serv->sv_nrthreads++; |
204 | spin_lock_bh(&pool->sp_lock); | 232 | spin_lock_bh(&pool->sp_lock); |
205 | pool->sp_nrthreads++; | 233 | pool->sp_nrthreads++; |
234 | list_add(&rqstp->rq_all, &pool->sp_all_threads); | ||
206 | spin_unlock_bh(&pool->sp_lock); | 235 | spin_unlock_bh(&pool->sp_lock); |
207 | rqstp->rq_server = serv; | 236 | rqstp->rq_server = serv; |
208 | rqstp->rq_pool = pool; | 237 | rqstp->rq_pool = pool; |
@@ -229,6 +258,109 @@ svc_create_thread(svc_thread_fn func, struct svc_serv *serv) | |||
229 | } | 258 | } |
230 | 259 | ||
231 | /* | 260 | /* |
261 | * Choose a pool in which to create a new thread, for svc_set_num_threads | ||
262 | */ | ||
263 | static inline struct svc_pool * | ||
264 | choose_pool(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) | ||
265 | { | ||
266 | if (pool != NULL) | ||
267 | return pool; | ||
268 | |||
269 | return &serv->sv_pools[(*state)++ % serv->sv_nrpools]; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * Choose a thread to kill, for svc_set_num_threads | ||
274 | */ | ||
275 | static inline struct task_struct * | ||
276 | choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) | ||
277 | { | ||
278 | unsigned int i; | ||
279 | struct task_struct *task = NULL; | ||
280 | |||
281 | if (pool != NULL) { | ||
282 | spin_lock_bh(&pool->sp_lock); | ||
283 | } else { | ||
284 | /* choose a pool in round-robin fashion */ | ||
285 | for (i = 0; i < serv->sv_nrpools; i++) { | ||
286 | pool = &serv->sv_pools[--(*state) % serv->sv_nrpools]; | ||
287 | spin_lock_bh(&pool->sp_lock); | ||
288 | if (!list_empty(&pool->sp_all_threads)) | ||
289 | goto found_pool; | ||
290 | spin_unlock_bh(&pool->sp_lock); | ||
291 | } | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
295 | found_pool: | ||
296 | if (!list_empty(&pool->sp_all_threads)) { | ||
297 | struct svc_rqst *rqstp; | ||
298 | |||
299 | /* | ||
300 | * Remove from the pool->sp_all_threads list | ||
301 | * so we don't try to kill it again. | ||
302 | */ | ||
303 | rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all); | ||
304 | list_del_init(&rqstp->rq_all); | ||
305 | task = rqstp->rq_task; | ||
306 | } | ||
307 | spin_unlock_bh(&pool->sp_lock); | ||
308 | |||
309 | return task; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Create or destroy enough new threads to make the number | ||
314 | * of threads the given number. If `pool' is non-NULL, applies | ||
315 | * only to threads in that pool, otherwise round-robins between | ||
316 | * all pools. Must be called with a svc_get() reference and | ||
317 | * the BKL held. | ||
318 | * | ||
319 | * Destroying threads relies on the service threads filling in | ||
320 | * rqstp->rq_task, which only the nfs ones do. Assumes the serv | ||
321 | * has been created using svc_create_pooled(). | ||
322 | * | ||
323 | * Based on code that used to be in nfsd_svc() but tweaked | ||
324 | * to be pool-aware. | ||
325 | */ | ||
326 | int | ||
327 | svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) | ||
328 | { | ||
329 | struct task_struct *victim; | ||
330 | int error = 0; | ||
331 | unsigned int state = serv->sv_nrthreads-1; | ||
332 | |||
333 | if (pool == NULL) { | ||
334 | /* The -1 assumes caller has done a svc_get() */ | ||
335 | nrservs -= (serv->sv_nrthreads-1); | ||
336 | } else { | ||
337 | spin_lock_bh(&pool->sp_lock); | ||
338 | nrservs -= pool->sp_nrthreads; | ||
339 | spin_unlock_bh(&pool->sp_lock); | ||
340 | } | ||
341 | |||
342 | /* create new threads */ | ||
343 | while (nrservs > 0) { | ||
344 | nrservs--; | ||
345 | __module_get(serv->sv_module); | ||
346 | error = __svc_create_thread(serv->sv_function, serv, | ||
347 | choose_pool(serv, pool, &state)); | ||
348 | if (error < 0) { | ||
349 | module_put(serv->sv_module); | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | /* destroy old threads */ | ||
354 | while (nrservs < 0 && | ||
355 | (victim = choose_victim(serv, pool, &state)) != NULL) { | ||
356 | send_sig(serv->sv_kill_signal, victim, 1); | ||
357 | nrservs++; | ||
358 | } | ||
359 | |||
360 | return error; | ||
361 | } | ||
362 | |||
363 | /* | ||
232 | * Called from a server thread as it's exiting. Caller must hold BKL. | 364 | * Called from a server thread as it's exiting. Caller must hold BKL. |
233 | */ | 365 | */ |
234 | void | 366 | void |
@@ -244,6 +376,7 @@ svc_exit_thread(struct svc_rqst *rqstp) | |||
244 | 376 | ||
245 | spin_lock_bh(&pool->sp_lock); | 377 | spin_lock_bh(&pool->sp_lock); |
246 | pool->sp_nrthreads--; | 378 | pool->sp_nrthreads--; |
379 | list_del(&rqstp->rq_all); | ||
247 | spin_unlock_bh(&pool->sp_lock); | 380 | spin_unlock_bh(&pool->sp_lock); |
248 | 381 | ||
249 | kfree(rqstp); | 382 | kfree(rqstp); |