diff options
author | Neil Brown <neilb@suse.de> | 2008-06-10 08:40:35 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2008-06-23 13:02:49 -0400 |
commit | bedbdd8bada194a690d2901801bf8451965086b3 (patch) | |
tree | dc7ea15dd52370429bd63cd6803d6402cebbd50b | |
parent | 0d169ca136357d51a65d686f3c84866a8ba20ae9 (diff) |
knfsd: Replace lock_kernel with a mutex for nfsd thread startup/shutdown locking.
This removes the BKL from the RPC service creation codepath. The BKL
really isn't adequate for this job since some of this info needs
protection across sleeps.
Also, add some comments to try and clarify how the locking should work
and to make it clear that the BKL isn't necessary as long as there is
adequate locking between tasks when touching the svc_serv fields.
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
-rw-r--r-- | fs/nfsd/nfsctl.c | 37 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 45 | ||||
-rw-r--r-- | include/linux/nfsd/nfsd.h | 1 | ||||
-rw-r--r-- | net/sunrpc/svc.c | 15 |
4 files changed, 65 insertions, 33 deletions
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 5ac00c4fee91..049d2a9c7715 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -450,22 +450,26 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) | |||
450 | int i; | 450 | int i; |
451 | int rv; | 451 | int rv; |
452 | int len; | 452 | int len; |
453 | int npools = nfsd_nrpools(); | 453 | int npools; |
454 | int *nthreads; | 454 | int *nthreads; |
455 | 455 | ||
456 | mutex_lock(&nfsd_mutex); | ||
457 | npools = nfsd_nrpools(); | ||
456 | if (npools == 0) { | 458 | if (npools == 0) { |
457 | /* | 459 | /* |
458 | * NFS is shut down. The admin can start it by | 460 | * NFS is shut down. The admin can start it by |
459 | * writing to the threads file but NOT the pool_threads | 461 | * writing to the threads file but NOT the pool_threads |
460 | * file, sorry. Report zero threads. | 462 | * file, sorry. Report zero threads. |
461 | */ | 463 | */ |
464 | mutex_unlock(&nfsd_mutex); | ||
462 | strcpy(buf, "0\n"); | 465 | strcpy(buf, "0\n"); |
463 | return strlen(buf); | 466 | return strlen(buf); |
464 | } | 467 | } |
465 | 468 | ||
466 | nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); | 469 | nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); |
470 | rv = -ENOMEM; | ||
467 | if (nthreads == NULL) | 471 | if (nthreads == NULL) |
468 | return -ENOMEM; | 472 | goto out_free; |
469 | 473 | ||
470 | if (size > 0) { | 474 | if (size > 0) { |
471 | for (i = 0; i < npools; i++) { | 475 | for (i = 0; i < npools; i++) { |
@@ -496,10 +500,12 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) | |||
496 | mesg += len; | 500 | mesg += len; |
497 | } | 501 | } |
498 | 502 | ||
503 | mutex_unlock(&nfsd_mutex); | ||
499 | return (mesg-buf); | 504 | return (mesg-buf); |
500 | 505 | ||
501 | out_free: | 506 | out_free: |
502 | kfree(nthreads); | 507 | kfree(nthreads); |
508 | mutex_unlock(&nfsd_mutex); | ||
503 | return rv; | 509 | return rv; |
504 | } | 510 | } |
505 | 511 | ||
@@ -566,14 +572,13 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size) | |||
566 | return len; | 572 | return len; |
567 | } | 573 | } |
568 | 574 | ||
569 | static ssize_t write_ports(struct file *file, char *buf, size_t size) | 575 | static ssize_t __write_ports(struct file *file, char *buf, size_t size) |
570 | { | 576 | { |
571 | if (size == 0) { | 577 | if (size == 0) { |
572 | int len = 0; | 578 | int len = 0; |
573 | lock_kernel(); | 579 | |
574 | if (nfsd_serv) | 580 | if (nfsd_serv) |
575 | len = svc_xprt_names(nfsd_serv, buf, 0); | 581 | len = svc_xprt_names(nfsd_serv, buf, 0); |
576 | unlock_kernel(); | ||
577 | return len; | 582 | return len; |
578 | } | 583 | } |
579 | /* Either a single 'fd' number is written, in which | 584 | /* Either a single 'fd' number is written, in which |
@@ -603,9 +608,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) | |||
603 | /* Decrease the count, but don't shutdown the | 608 | /* Decrease the count, but don't shutdown the |
604 | * the service | 609 | * the service |
605 | */ | 610 | */ |
606 | lock_kernel(); | ||
607 | nfsd_serv->sv_nrthreads--; | 611 | nfsd_serv->sv_nrthreads--; |
608 | unlock_kernel(); | ||
609 | } | 612 | } |
610 | return err < 0 ? err : 0; | 613 | return err < 0 ? err : 0; |
611 | } | 614 | } |
@@ -614,10 +617,8 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) | |||
614 | int len = 0; | 617 | int len = 0; |
615 | if (!toclose) | 618 | if (!toclose) |
616 | return -ENOMEM; | 619 | return -ENOMEM; |
617 | lock_kernel(); | ||
618 | if (nfsd_serv) | 620 | if (nfsd_serv) |
619 | len = svc_sock_names(buf, nfsd_serv, toclose); | 621 | len = svc_sock_names(buf, nfsd_serv, toclose); |
620 | unlock_kernel(); | ||
621 | if (len >= 0) | 622 | if (len >= 0) |
622 | lockd_down(); | 623 | lockd_down(); |
623 | kfree(toclose); | 624 | kfree(toclose); |
@@ -655,7 +656,6 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) | |||
655 | if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) { | 656 | if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) { |
656 | if (port == 0) | 657 | if (port == 0) |
657 | return -EINVAL; | 658 | return -EINVAL; |
658 | lock_kernel(); | ||
659 | if (nfsd_serv) { | 659 | if (nfsd_serv) { |
660 | xprt = svc_find_xprt(nfsd_serv, transport, | 660 | xprt = svc_find_xprt(nfsd_serv, transport, |
661 | AF_UNSPEC, port); | 661 | AF_UNSPEC, port); |
@@ -666,13 +666,22 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) | |||
666 | } else | 666 | } else |
667 | err = -ENOTCONN; | 667 | err = -ENOTCONN; |
668 | } | 668 | } |
669 | unlock_kernel(); | ||
670 | return err < 0 ? err : 0; | 669 | return err < 0 ? err : 0; |
671 | } | 670 | } |
672 | } | 671 | } |
673 | return -EINVAL; | 672 | return -EINVAL; |
674 | } | 673 | } |
675 | 674 | ||
675 | static ssize_t write_ports(struct file *file, char *buf, size_t size) | ||
676 | { | ||
677 | ssize_t rv; | ||
678 | mutex_lock(&nfsd_mutex); | ||
679 | rv = __write_ports(file, buf, size); | ||
680 | mutex_unlock(&nfsd_mutex); | ||
681 | return rv; | ||
682 | } | ||
683 | |||
684 | |||
676 | int nfsd_max_blksize; | 685 | int nfsd_max_blksize; |
677 | 686 | ||
678 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) | 687 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) |
@@ -691,13 +700,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) | |||
691 | if (bsize > NFSSVC_MAXBLKSIZE) | 700 | if (bsize > NFSSVC_MAXBLKSIZE) |
692 | bsize = NFSSVC_MAXBLKSIZE; | 701 | bsize = NFSSVC_MAXBLKSIZE; |
693 | bsize &= ~(1024-1); | 702 | bsize &= ~(1024-1); |
694 | lock_kernel(); | 703 | mutex_lock(&nfsd_mutex); |
695 | if (nfsd_serv && nfsd_serv->sv_nrthreads) { | 704 | if (nfsd_serv && nfsd_serv->sv_nrthreads) { |
696 | unlock_kernel(); | 705 | mutex_unlock(&nfsd_mutex); |
697 | return -EBUSY; | 706 | return -EBUSY; |
698 | } | 707 | } |
699 | nfsd_max_blksize = bsize; | 708 | nfsd_max_blksize = bsize; |
700 | unlock_kernel(); | 709 | mutex_unlock(&nfsd_mutex); |
701 | } | 710 | } |
702 | return sprintf(buf, "%d\n", nfsd_max_blksize); | 711 | return sprintf(buf, "%d\n", nfsd_max_blksize); |
703 | } | 712 | } |
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 941041f4b136..512bd04c6dda 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -53,11 +53,27 @@ | |||
53 | extern struct svc_program nfsd_program; | 53 | extern struct svc_program nfsd_program; |
54 | static void nfsd(struct svc_rqst *rqstp); | 54 | static void nfsd(struct svc_rqst *rqstp); |
55 | struct timeval nfssvc_boot; | 55 | struct timeval nfssvc_boot; |
56 | struct svc_serv *nfsd_serv; | ||
57 | static atomic_t nfsd_busy; | 56 | static atomic_t nfsd_busy; |
58 | static unsigned long nfsd_last_call; | 57 | static unsigned long nfsd_last_call; |
59 | static DEFINE_SPINLOCK(nfsd_call_lock); | 58 | static DEFINE_SPINLOCK(nfsd_call_lock); |
60 | 59 | ||
60 | /* | ||
61 | * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members | ||
62 | * of the svc_serv struct. In particular, ->sv_nrthreads but also to some | ||
63 | * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt | ||
64 | * | ||
65 | * If (out side the lock) nfsd_serv is non-NULL, then it must point to a | ||
66 | * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number | ||
67 | * of nfsd threads must exist and each must listed in ->sp_all_threads in each | ||
68 | * entry of ->sv_pools[]. | ||
69 | * | ||
70 | * Transitions of the thread count between zero and non-zero are of particular | ||
71 | * interest since the svc_serv needs to be created and initialized at that | ||
72 | * point, or freed. | ||
73 | */ | ||
74 | DEFINE_MUTEX(nfsd_mutex); | ||
75 | struct svc_serv *nfsd_serv; | ||
76 | |||
61 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 77 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
62 | static struct svc_stat nfsd_acl_svcstats; | 78 | static struct svc_stat nfsd_acl_svcstats; |
63 | static struct svc_version * nfsd_acl_version[] = { | 79 | static struct svc_version * nfsd_acl_version[] = { |
@@ -190,13 +206,14 @@ void nfsd_reset_versions(void) | |||
190 | } | 206 | } |
191 | } | 207 | } |
192 | 208 | ||
209 | |||
193 | int nfsd_create_serv(void) | 210 | int nfsd_create_serv(void) |
194 | { | 211 | { |
195 | int err = 0; | 212 | int err = 0; |
196 | lock_kernel(); | 213 | |
214 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | ||
197 | if (nfsd_serv) { | 215 | if (nfsd_serv) { |
198 | svc_get(nfsd_serv); | 216 | svc_get(nfsd_serv); |
199 | unlock_kernel(); | ||
200 | return 0; | 217 | return 0; |
201 | } | 218 | } |
202 | if (nfsd_max_blksize == 0) { | 219 | if (nfsd_max_blksize == 0) { |
@@ -223,7 +240,7 @@ int nfsd_create_serv(void) | |||
223 | nfsd, SIG_NOCLEAN, THIS_MODULE); | 240 | nfsd, SIG_NOCLEAN, THIS_MODULE); |
224 | if (nfsd_serv == NULL) | 241 | if (nfsd_serv == NULL) |
225 | err = -ENOMEM; | 242 | err = -ENOMEM; |
226 | unlock_kernel(); | 243 | |
227 | do_gettimeofday(&nfssvc_boot); /* record boot time */ | 244 | do_gettimeofday(&nfssvc_boot); /* record boot time */ |
228 | return err; | 245 | return err; |
229 | } | 246 | } |
@@ -282,6 +299,8 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
282 | int tot = 0; | 299 | int tot = 0; |
283 | int err = 0; | 300 | int err = 0; |
284 | 301 | ||
302 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | ||
303 | |||
285 | if (nfsd_serv == NULL || n <= 0) | 304 | if (nfsd_serv == NULL || n <= 0) |
286 | return 0; | 305 | return 0; |
287 | 306 | ||
@@ -316,7 +335,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
316 | nthreads[0] = 1; | 335 | nthreads[0] = 1; |
317 | 336 | ||
318 | /* apply the new numbers */ | 337 | /* apply the new numbers */ |
319 | lock_kernel(); | ||
320 | svc_get(nfsd_serv); | 338 | svc_get(nfsd_serv); |
321 | for (i = 0; i < n; i++) { | 339 | for (i = 0; i < n; i++) { |
322 | err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], | 340 | err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], |
@@ -325,7 +343,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
325 | break; | 343 | break; |
326 | } | 344 | } |
327 | svc_destroy(nfsd_serv); | 345 | svc_destroy(nfsd_serv); |
328 | unlock_kernel(); | ||
329 | 346 | ||
330 | return err; | 347 | return err; |
331 | } | 348 | } |
@@ -334,8 +351,8 @@ int | |||
334 | nfsd_svc(unsigned short port, int nrservs) | 351 | nfsd_svc(unsigned short port, int nrservs) |
335 | { | 352 | { |
336 | int error; | 353 | int error; |
337 | 354 | ||
338 | lock_kernel(); | 355 | mutex_lock(&nfsd_mutex); |
339 | dprintk("nfsd: creating service\n"); | 356 | dprintk("nfsd: creating service\n"); |
340 | error = -EINVAL; | 357 | error = -EINVAL; |
341 | if (nrservs <= 0) | 358 | if (nrservs <= 0) |
@@ -363,7 +380,7 @@ nfsd_svc(unsigned short port, int nrservs) | |||
363 | failure: | 380 | failure: |
364 | svc_destroy(nfsd_serv); /* Release server */ | 381 | svc_destroy(nfsd_serv); /* Release server */ |
365 | out: | 382 | out: |
366 | unlock_kernel(); | 383 | mutex_unlock(&nfsd_mutex); |
367 | return error; | 384 | return error; |
368 | } | 385 | } |
369 | 386 | ||
@@ -399,7 +416,7 @@ nfsd(struct svc_rqst *rqstp) | |||
399 | sigset_t shutdown_mask, allowed_mask; | 416 | sigset_t shutdown_mask, allowed_mask; |
400 | 417 | ||
401 | /* Lock module and set up kernel thread */ | 418 | /* Lock module and set up kernel thread */ |
402 | lock_kernel(); | 419 | mutex_lock(&nfsd_mutex); |
403 | daemonize("nfsd"); | 420 | daemonize("nfsd"); |
404 | 421 | ||
405 | /* After daemonize() this kernel thread shares current->fs | 422 | /* After daemonize() this kernel thread shares current->fs |
@@ -417,11 +434,13 @@ nfsd(struct svc_rqst *rqstp) | |||
417 | siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); | 434 | siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); |
418 | siginitsetinv(&allowed_mask, ALLOWED_SIGS); | 435 | siginitsetinv(&allowed_mask, ALLOWED_SIGS); |
419 | 436 | ||
437 | |||
420 | nfsdstats.th_cnt++; | 438 | nfsdstats.th_cnt++; |
421 | 439 | ||
422 | rqstp->rq_task = current; | 440 | rqstp->rq_task = current; |
423 | 441 | ||
424 | unlock_kernel(); | 442 | mutex_unlock(&nfsd_mutex); |
443 | |||
425 | 444 | ||
426 | /* | 445 | /* |
427 | * We want less throttling in balance_dirty_pages() so that nfs to | 446 | * We want less throttling in balance_dirty_pages() so that nfs to |
@@ -477,7 +496,7 @@ nfsd(struct svc_rqst *rqstp) | |||
477 | /* Clear signals before calling svc_exit_thread() */ | 496 | /* Clear signals before calling svc_exit_thread() */ |
478 | flush_signals(current); | 497 | flush_signals(current); |
479 | 498 | ||
480 | lock_kernel(); | 499 | mutex_lock(&nfsd_mutex); |
481 | 500 | ||
482 | nfsdstats.th_cnt --; | 501 | nfsdstats.th_cnt --; |
483 | 502 | ||
@@ -486,7 +505,7 @@ out: | |||
486 | svc_exit_thread(rqstp); | 505 | svc_exit_thread(rqstp); |
487 | 506 | ||
488 | /* Release module */ | 507 | /* Release module */ |
489 | unlock_kernel(); | 508 | mutex_unlock(&nfsd_mutex); |
490 | module_put_and_exit(0); | 509 | module_put_and_exit(0); |
491 | } | 510 | } |
492 | 511 | ||
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 41d30c9c9de6..88d85b964429 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h | |||
@@ -54,6 +54,7 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int); | |||
54 | extern struct svc_program nfsd_program; | 54 | extern struct svc_program nfsd_program; |
55 | extern struct svc_version nfsd_version2, nfsd_version3, | 55 | extern struct svc_version nfsd_version2, nfsd_version3, |
56 | nfsd_version4; | 56 | nfsd_version4; |
57 | extern struct mutex nfsd_mutex; | ||
57 | extern struct svc_serv *nfsd_serv; | 58 | extern struct svc_serv *nfsd_serv; |
58 | 59 | ||
59 | extern struct seq_operations nfs_exports_op; | 60 | extern struct seq_operations nfs_exports_op; |
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 01c7e311b904..7bffaff2a3ab 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c | |||
@@ -461,7 +461,8 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, | |||
461 | EXPORT_SYMBOL(svc_create_pooled); | 461 | EXPORT_SYMBOL(svc_create_pooled); |
462 | 462 | ||
463 | /* | 463 | /* |
464 | * Destroy an RPC service. Should be called with the BKL held | 464 | * Destroy an RPC service. Should be called with appropriate locking to |
465 | * protect the sv_nrthreads, sv_permsocks and sv_tempsocks. | ||
465 | */ | 466 | */ |
466 | void | 467 | void |
467 | svc_destroy(struct svc_serv *serv) | 468 | svc_destroy(struct svc_serv *serv) |
@@ -578,9 +579,10 @@ out_enomem: | |||
578 | EXPORT_SYMBOL(svc_prepare_thread); | 579 | EXPORT_SYMBOL(svc_prepare_thread); |
579 | 580 | ||
580 | /* | 581 | /* |
581 | * Create a thread in the given pool. Caller must hold BKL. | 582 | * Create a thread in the given pool. Caller must hold BKL or another lock to |
582 | * On a NUMA or SMP machine, with a multi-pool serv, the thread | 583 | * serialize access to the svc_serv struct. On a NUMA or SMP machine, with a |
583 | * will be restricted to run on the cpus belonging to the pool. | 584 | * multi-pool serv, the thread will be restricted to run on the cpus belonging |
585 | * to the pool. | ||
584 | */ | 586 | */ |
585 | static int | 587 | static int |
586 | __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, | 588 | __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, |
@@ -674,7 +676,7 @@ found_pool: | |||
674 | * of threads the given number. If `pool' is non-NULL, applies | 676 | * of threads the given number. If `pool' is non-NULL, applies |
675 | * only to threads in that pool, otherwise round-robins between | 677 | * only to threads in that pool, otherwise round-robins between |
676 | * all pools. Must be called with a svc_get() reference and | 678 | * all pools. Must be called with a svc_get() reference and |
677 | * the BKL held. | 679 | * the BKL or another lock to protect access to svc_serv fields. |
678 | * | 680 | * |
679 | * Destroying threads relies on the service threads filling in | 681 | * Destroying threads relies on the service threads filling in |
680 | * rqstp->rq_task, which only the nfs ones do. Assumes the serv | 682 | * rqstp->rq_task, which only the nfs ones do. Assumes the serv |
@@ -722,7 +724,8 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) | |||
722 | EXPORT_SYMBOL(svc_set_num_threads); | 724 | EXPORT_SYMBOL(svc_set_num_threads); |
723 | 725 | ||
724 | /* | 726 | /* |
725 | * Called from a server thread as it's exiting. Caller must hold BKL. | 727 | * Called from a server thread as it's exiting. Caller must hold the BKL or |
728 | * the "service mutex", whichever is appropriate for the service. | ||
726 | */ | 729 | */ |
727 | void | 730 | void |
728 | svc_exit_thread(struct svc_rqst *rqstp) | 731 | svc_exit_thread(struct svc_rqst *rqstp) |