diff options
Diffstat (limited to 'fs/nfsd/nfssvc.c')
-rw-r--r-- | fs/nfsd/nfssvc.c | 148 |
1 files changed, 76 insertions, 72 deletions
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 941041f4b136..80292ff5e924 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/smp_lock.h> | 21 | #include <linux/smp_lock.h> |
22 | #include <linux/freezer.h> | 22 | #include <linux/freezer.h> |
23 | #include <linux/fs_struct.h> | 23 | #include <linux/fs_struct.h> |
24 | #include <linux/kthread.h> | ||
24 | 25 | ||
25 | #include <linux/sunrpc/types.h> | 26 | #include <linux/sunrpc/types.h> |
26 | #include <linux/sunrpc/stats.h> | 27 | #include <linux/sunrpc/stats.h> |
@@ -36,28 +37,38 @@ | |||
36 | 37 | ||
37 | #define NFSDDBG_FACILITY NFSDDBG_SVC | 38 | #define NFSDDBG_FACILITY NFSDDBG_SVC |
38 | 39 | ||
39 | /* these signals will be delivered to an nfsd thread | ||
40 | * when handling a request | ||
41 | */ | ||
42 | #define ALLOWED_SIGS (sigmask(SIGKILL)) | ||
43 | /* these signals will be delivered to an nfsd thread | ||
44 | * when not handling a request. i.e. when waiting | ||
45 | */ | ||
46 | #define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT)) | ||
47 | /* if the last thread dies with SIGHUP, then the exports table is | ||
48 | * left unchanged ( like 2.4-{0-9} ). Any other signal will clear | ||
49 | * the exports table (like 2.2). | ||
50 | */ | ||
51 | #define SIG_NOCLEAN SIGHUP | ||
52 | |||
53 | extern struct svc_program nfsd_program; | 40 | extern struct svc_program nfsd_program; |
54 | static void nfsd(struct svc_rqst *rqstp); | 41 | static int nfsd(void *vrqstp); |
55 | struct timeval nfssvc_boot; | 42 | struct timeval nfssvc_boot; |
56 | struct svc_serv *nfsd_serv; | ||
57 | static atomic_t nfsd_busy; | 43 | static atomic_t nfsd_busy; |
58 | static unsigned long nfsd_last_call; | 44 | static unsigned long nfsd_last_call; |
59 | static DEFINE_SPINLOCK(nfsd_call_lock); | 45 | static DEFINE_SPINLOCK(nfsd_call_lock); |
60 | 46 | ||
47 | /* | ||
48 | * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members | ||
49 | * of the svc_serv struct. In particular, ->sv_nrthreads but also to some | ||
50 | * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt | ||
51 | * | ||
52 | * If (out side the lock) nfsd_serv is non-NULL, then it must point to a | ||
53 | * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number | ||
54 | * of nfsd threads must exist and each must listed in ->sp_all_threads in each | ||
55 | * entry of ->sv_pools[]. | ||
56 | * | ||
57 | * Transitions of the thread count between zero and non-zero are of particular | ||
58 | * interest since the svc_serv needs to be created and initialized at that | ||
59 | * point, or freed. | ||
60 | * | ||
61 | * Finally, the nfsd_mutex also protects some of the global variables that are | ||
62 | * accessed when nfsd starts and that are settable via the write_* routines in | ||
63 | * nfsctl.c. In particular: | ||
64 | * | ||
65 | * user_recovery_dirname | ||
66 | * user_lease_time | ||
67 | * nfsd_versions | ||
68 | */ | ||
69 | DEFINE_MUTEX(nfsd_mutex); | ||
70 | struct svc_serv *nfsd_serv; | ||
71 | |||
61 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 72 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
62 | static struct svc_stat nfsd_acl_svcstats; | 73 | static struct svc_stat nfsd_acl_svcstats; |
63 | static struct svc_version * nfsd_acl_version[] = { | 74 | static struct svc_version * nfsd_acl_version[] = { |
@@ -145,13 +156,14 @@ int nfsd_vers(int vers, enum vers_op change) | |||
145 | 156 | ||
146 | int nfsd_nrthreads(void) | 157 | int nfsd_nrthreads(void) |
147 | { | 158 | { |
148 | if (nfsd_serv == NULL) | 159 | int rv = 0; |
149 | return 0; | 160 | mutex_lock(&nfsd_mutex); |
150 | else | 161 | if (nfsd_serv) |
151 | return nfsd_serv->sv_nrthreads; | 162 | rv = nfsd_serv->sv_nrthreads; |
163 | mutex_unlock(&nfsd_mutex); | ||
164 | return rv; | ||
152 | } | 165 | } |
153 | 166 | ||
154 | static int killsig; /* signal that was used to kill last nfsd */ | ||
155 | static void nfsd_last_thread(struct svc_serv *serv) | 167 | static void nfsd_last_thread(struct svc_serv *serv) |
156 | { | 168 | { |
157 | /* When last nfsd thread exits we need to do some clean-up */ | 169 | /* When last nfsd thread exits we need to do some clean-up */ |
@@ -162,11 +174,9 @@ static void nfsd_last_thread(struct svc_serv *serv) | |||
162 | nfsd_racache_shutdown(); | 174 | nfsd_racache_shutdown(); |
163 | nfs4_state_shutdown(); | 175 | nfs4_state_shutdown(); |
164 | 176 | ||
165 | printk(KERN_WARNING "nfsd: last server has exited\n"); | 177 | printk(KERN_WARNING "nfsd: last server has exited, flushing export " |
166 | if (killsig != SIG_NOCLEAN) { | 178 | "cache\n"); |
167 | printk(KERN_WARNING "nfsd: unexporting all filesystems\n"); | 179 | nfsd_export_flush(); |
168 | nfsd_export_flush(); | ||
169 | } | ||
170 | } | 180 | } |
171 | 181 | ||
172 | void nfsd_reset_versions(void) | 182 | void nfsd_reset_versions(void) |
@@ -190,13 +200,14 @@ void nfsd_reset_versions(void) | |||
190 | } | 200 | } |
191 | } | 201 | } |
192 | 202 | ||
203 | |||
193 | int nfsd_create_serv(void) | 204 | int nfsd_create_serv(void) |
194 | { | 205 | { |
195 | int err = 0; | 206 | int err = 0; |
196 | lock_kernel(); | 207 | |
208 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | ||
197 | if (nfsd_serv) { | 209 | if (nfsd_serv) { |
198 | svc_get(nfsd_serv); | 210 | svc_get(nfsd_serv); |
199 | unlock_kernel(); | ||
200 | return 0; | 211 | return 0; |
201 | } | 212 | } |
202 | if (nfsd_max_blksize == 0) { | 213 | if (nfsd_max_blksize == 0) { |
@@ -217,13 +228,11 @@ int nfsd_create_serv(void) | |||
217 | } | 228 | } |
218 | 229 | ||
219 | atomic_set(&nfsd_busy, 0); | 230 | atomic_set(&nfsd_busy, 0); |
220 | nfsd_serv = svc_create_pooled(&nfsd_program, | 231 | nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, |
221 | nfsd_max_blksize, | 232 | nfsd_last_thread, nfsd, THIS_MODULE); |
222 | nfsd_last_thread, | ||
223 | nfsd, SIG_NOCLEAN, THIS_MODULE); | ||
224 | if (nfsd_serv == NULL) | 233 | if (nfsd_serv == NULL) |
225 | err = -ENOMEM; | 234 | err = -ENOMEM; |
226 | unlock_kernel(); | 235 | |
227 | do_gettimeofday(&nfssvc_boot); /* record boot time */ | 236 | do_gettimeofday(&nfssvc_boot); /* record boot time */ |
228 | return err; | 237 | return err; |
229 | } | 238 | } |
@@ -282,6 +291,8 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
282 | int tot = 0; | 291 | int tot = 0; |
283 | int err = 0; | 292 | int err = 0; |
284 | 293 | ||
294 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | ||
295 | |||
285 | if (nfsd_serv == NULL || n <= 0) | 296 | if (nfsd_serv == NULL || n <= 0) |
286 | return 0; | 297 | return 0; |
287 | 298 | ||
@@ -316,7 +327,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
316 | nthreads[0] = 1; | 327 | nthreads[0] = 1; |
317 | 328 | ||
318 | /* apply the new numbers */ | 329 | /* apply the new numbers */ |
319 | lock_kernel(); | ||
320 | svc_get(nfsd_serv); | 330 | svc_get(nfsd_serv); |
321 | for (i = 0; i < n; i++) { | 331 | for (i = 0; i < n; i++) { |
322 | err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], | 332 | err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], |
@@ -325,7 +335,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
325 | break; | 335 | break; |
326 | } | 336 | } |
327 | svc_destroy(nfsd_serv); | 337 | svc_destroy(nfsd_serv); |
328 | unlock_kernel(); | ||
329 | 338 | ||
330 | return err; | 339 | return err; |
331 | } | 340 | } |
@@ -334,8 +343,8 @@ int | |||
334 | nfsd_svc(unsigned short port, int nrservs) | 343 | nfsd_svc(unsigned short port, int nrservs) |
335 | { | 344 | { |
336 | int error; | 345 | int error; |
337 | 346 | ||
338 | lock_kernel(); | 347 | mutex_lock(&nfsd_mutex); |
339 | dprintk("nfsd: creating service\n"); | 348 | dprintk("nfsd: creating service\n"); |
340 | error = -EINVAL; | 349 | error = -EINVAL; |
341 | if (nrservs <= 0) | 350 | if (nrservs <= 0) |
@@ -363,7 +372,7 @@ nfsd_svc(unsigned short port, int nrservs) | |||
363 | failure: | 372 | failure: |
364 | svc_destroy(nfsd_serv); /* Release server */ | 373 | svc_destroy(nfsd_serv); /* Release server */ |
365 | out: | 374 | out: |
366 | unlock_kernel(); | 375 | mutex_unlock(&nfsd_mutex); |
367 | return error; | 376 | return error; |
368 | } | 377 | } |
369 | 378 | ||
@@ -391,18 +400,17 @@ update_thread_usage(int busy_threads) | |||
391 | /* | 400 | /* |
392 | * This is the NFS server kernel thread | 401 | * This is the NFS server kernel thread |
393 | */ | 402 | */ |
394 | static void | 403 | static int |
395 | nfsd(struct svc_rqst *rqstp) | 404 | nfsd(void *vrqstp) |
396 | { | 405 | { |
406 | struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; | ||
397 | struct fs_struct *fsp; | 407 | struct fs_struct *fsp; |
398 | int err; | 408 | int err, preverr = 0; |
399 | sigset_t shutdown_mask, allowed_mask; | ||
400 | 409 | ||
401 | /* Lock module and set up kernel thread */ | 410 | /* Lock module and set up kernel thread */ |
402 | lock_kernel(); | 411 | mutex_lock(&nfsd_mutex); |
403 | daemonize("nfsd"); | ||
404 | 412 | ||
405 | /* After daemonize() this kernel thread shares current->fs | 413 | /* At this point, the thread shares current->fs |
406 | * with the init process. We need to create files with a | 414 | * with the init process. We need to create files with a |
407 | * umask of 0 instead of init's umask. */ | 415 | * umask of 0 instead of init's umask. */ |
408 | fsp = copy_fs_struct(current->fs); | 416 | fsp = copy_fs_struct(current->fs); |
@@ -414,14 +422,17 @@ nfsd(struct svc_rqst *rqstp) | |||
414 | current->fs = fsp; | 422 | current->fs = fsp; |
415 | current->fs->umask = 0; | 423 | current->fs->umask = 0; |
416 | 424 | ||
417 | siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); | 425 | /* |
418 | siginitsetinv(&allowed_mask, ALLOWED_SIGS); | 426 | * thread is spawned with all signals set to SIG_IGN, re-enable |
427 | * the ones that will bring down the thread | ||
428 | */ | ||
429 | allow_signal(SIGKILL); | ||
430 | allow_signal(SIGHUP); | ||
431 | allow_signal(SIGINT); | ||
432 | allow_signal(SIGQUIT); | ||
419 | 433 | ||
420 | nfsdstats.th_cnt++; | 434 | nfsdstats.th_cnt++; |
421 | 435 | mutex_unlock(&nfsd_mutex); | |
422 | rqstp->rq_task = current; | ||
423 | |||
424 | unlock_kernel(); | ||
425 | 436 | ||
426 | /* | 437 | /* |
427 | * We want less throttling in balance_dirty_pages() so that nfs to | 438 | * We want less throttling in balance_dirty_pages() so that nfs to |
@@ -435,26 +446,30 @@ nfsd(struct svc_rqst *rqstp) | |||
435 | * The main request loop | 446 | * The main request loop |
436 | */ | 447 | */ |
437 | for (;;) { | 448 | for (;;) { |
438 | /* Block all but the shutdown signals */ | ||
439 | sigprocmask(SIG_SETMASK, &shutdown_mask, NULL); | ||
440 | |||
441 | /* | 449 | /* |
442 | * Find a socket with data available and call its | 450 | * Find a socket with data available and call its |
443 | * recvfrom routine. | 451 | * recvfrom routine. |
444 | */ | 452 | */ |
445 | while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN) | 453 | while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN) |
446 | ; | 454 | ; |
447 | if (err < 0) | 455 | if (err == -EINTR) |
448 | break; | 456 | break; |
457 | else if (err < 0) { | ||
458 | if (err != preverr) { | ||
459 | printk(KERN_WARNING "%s: unexpected error " | ||
460 | "from svc_recv (%d)\n", __func__, -err); | ||
461 | preverr = err; | ||
462 | } | ||
463 | schedule_timeout_uninterruptible(HZ); | ||
464 | continue; | ||
465 | } | ||
466 | |||
449 | update_thread_usage(atomic_read(&nfsd_busy)); | 467 | update_thread_usage(atomic_read(&nfsd_busy)); |
450 | atomic_inc(&nfsd_busy); | 468 | atomic_inc(&nfsd_busy); |
451 | 469 | ||
452 | /* Lock the export hash tables for reading. */ | 470 | /* Lock the export hash tables for reading. */ |
453 | exp_readlock(); | 471 | exp_readlock(); |
454 | 472 | ||
455 | /* Process request with signals blocked. */ | ||
456 | sigprocmask(SIG_SETMASK, &allowed_mask, NULL); | ||
457 | |||
458 | svc_process(rqstp); | 473 | svc_process(rqstp); |
459 | 474 | ||
460 | /* Unlock export hash tables */ | 475 | /* Unlock export hash tables */ |
@@ -463,22 +478,10 @@ nfsd(struct svc_rqst *rqstp) | |||
463 | atomic_dec(&nfsd_busy); | 478 | atomic_dec(&nfsd_busy); |
464 | } | 479 | } |
465 | 480 | ||
466 | if (err != -EINTR) { | ||
467 | printk(KERN_WARNING "nfsd: terminating on error %d\n", -err); | ||
468 | } else { | ||
469 | unsigned int signo; | ||
470 | |||
471 | for (signo = 1; signo <= _NSIG; signo++) | ||
472 | if (sigismember(¤t->pending.signal, signo) && | ||
473 | !sigismember(¤t->blocked, signo)) | ||
474 | break; | ||
475 | killsig = signo; | ||
476 | } | ||
477 | /* Clear signals before calling svc_exit_thread() */ | 481 | /* Clear signals before calling svc_exit_thread() */ |
478 | flush_signals(current); | 482 | flush_signals(current); |
479 | 483 | ||
480 | lock_kernel(); | 484 | mutex_lock(&nfsd_mutex); |
481 | |||
482 | nfsdstats.th_cnt --; | 485 | nfsdstats.th_cnt --; |
483 | 486 | ||
484 | out: | 487 | out: |
@@ -486,8 +489,9 @@ out: | |||
486 | svc_exit_thread(rqstp); | 489 | svc_exit_thread(rqstp); |
487 | 490 | ||
488 | /* Release module */ | 491 | /* Release module */ |
489 | unlock_kernel(); | 492 | mutex_unlock(&nfsd_mutex); |
490 | module_put_and_exit(0); | 493 | module_put_and_exit(0); |
494 | return 0; | ||
491 | } | 495 | } |
492 | 496 | ||
493 | static __be32 map_new_errors(u32 vers, __be32 nfserr) | 497 | static __be32 map_new_errors(u32 vers, __be32 nfserr) |