Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd: Containerise filecache laundrette

Ensure that if the filecache laundrette gets stuck, it only affects
the knfsd instances of one container.

The notifier callbacks can be called from various contexts so avoid
using synchonous filesystem operations that might deadlock.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Trond Myklebust and committed by
J. Bruce Fields
9542e6a6 36ebbdb9

+207 -42
+198 -40
fs/nfsd/filecache.c
··· 44 44 45 45 static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits); 46 46 47 + struct nfsd_fcache_disposal { 48 + struct list_head list; 49 + struct work_struct work; 50 + struct net *net; 51 + spinlock_t lock; 52 + struct list_head freeme; 53 + struct rcu_head rcu; 54 + }; 55 + 56 + struct workqueue_struct *nfsd_filecache_wq __read_mostly; 57 + 47 58 static struct kmem_cache *nfsd_file_slab; 48 59 static struct kmem_cache *nfsd_file_mark_slab; 49 60 static struct nfsd_fcache_bucket *nfsd_file_hashtbl; ··· 63 52 static struct fsnotify_group *nfsd_file_fsnotify_group; 64 53 static atomic_long_t nfsd_filecache_count; 65 54 static struct delayed_work nfsd_filecache_laundrette; 55 + static DEFINE_SPINLOCK(laundrette_lock); 56 + static LIST_HEAD(laundrettes); 66 57 67 - enum nfsd_file_laundrette_ctl { 68 - NFSD_FILE_LAUNDRETTE_NOFLUSH = 0, 69 - NFSD_FILE_LAUNDRETTE_MAY_FLUSH 70 - }; 58 + static void nfsd_file_gc(void); 71 59 72 60 static void 73 - nfsd_file_schedule_laundrette(enum nfsd_file_laundrette_ctl ctl) 61 + nfsd_file_schedule_laundrette(void) 74 62 { 75 63 long count = atomic_long_read(&nfsd_filecache_count); 76 64 77 65 if (count == 0 || test_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags)) 78 66 return; 79 67 80 - /* Be more aggressive about scanning if over the threshold */ 81 - if (count > NFSD_FILE_LRU_THRESHOLD) 82 - mod_delayed_work(system_wq, &nfsd_filecache_laundrette, 0); 83 - else 84 - schedule_delayed_work(&nfsd_filecache_laundrette, NFSD_LAUNDRETTE_DELAY); 85 - 86 - if (ctl == NFSD_FILE_LAUNDRETTE_NOFLUSH) 87 - return; 88 - 89 - /* ...and don't delay flushing if we're out of control */ 90 - if (count >= NFSD_FILE_LRU_LIMIT) 91 - flush_delayed_work(&nfsd_filecache_laundrette); 68 + queue_delayed_work(system_wq, &nfsd_filecache_laundrette, 69 + NFSD_LAUNDRETTE_DELAY); 92 70 } 93 71 94 72 static void ··· 312 312 313 313 set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); 314 314 if (nfsd_file_put_noref(nf) == 1 && is_hashed && unused) 315 - nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_MAY_FLUSH); 315 + nfsd_file_schedule_laundrette(); 316 + if (atomic_long_read(&nfsd_filecache_count) >= NFSD_FILE_LRU_LIMIT) 317 + nfsd_file_gc(); 316 318 } 317 319 318 320 struct nfsd_file * ··· 353 351 } 354 352 if (flush) 355 353 flush_delayed_fput(); 354 + } 355 + 356 + static void 357 + nfsd_file_list_remove_disposal(struct list_head *dst, 358 + struct nfsd_fcache_disposal *l) 359 + { 360 + spin_lock(&l->lock); 361 + list_splice_init(&l->freeme, dst); 362 + spin_unlock(&l->lock); 363 + } 364 + 365 + static void 366 + nfsd_file_list_add_disposal(struct list_head *files, struct net *net) 367 + { 368 + struct nfsd_fcache_disposal *l; 369 + 370 + rcu_read_lock(); 371 + list_for_each_entry_rcu(l, &laundrettes, list) { 372 + if (l->net == net) { 373 + spin_lock(&l->lock); 374 + list_splice_tail_init(files, &l->freeme); 375 + spin_unlock(&l->lock); 376 + queue_work(nfsd_filecache_wq, &l->work); 377 + break; 378 + } 379 + } 380 + rcu_read_unlock(); 381 + } 382 + 383 + static void 384 + nfsd_file_list_add_pernet(struct list_head *dst, struct list_head *src, 385 + struct net *net) 386 + { 387 + struct nfsd_file *nf, *tmp; 388 + 389 + list_for_each_entry_safe(nf, tmp, src, nf_lru) { 390 + if (nf->nf_net == net) 391 + list_move_tail(&nf->nf_lru, dst); 392 + } 393 + } 394 + 395 + static void 396 + nfsd_file_dispose_list_delayed(struct list_head *dispose) 397 + { 398 + LIST_HEAD(list); 399 + struct nfsd_file *nf; 400 + 401 + while(!list_empty(dispose)) { 402 + nf = list_first_entry(dispose, struct nfsd_file, nf_lru); 403 + nfsd_file_list_add_pernet(&list, dispose, nf->nf_net); 404 + nfsd_file_list_add_disposal(&list, nf->nf_net); 405 + } 356 406 } 357 407 358 408 /* ··· 453 399 return LRU_SKIP; 454 400 } 455 401 456 - static void 457 - nfsd_file_lru_dispose(struct list_head *head) 402 + static unsigned long 403 + nfsd_file_lru_walk_list(struct shrink_control *sc) 458 404 { 405 + LIST_HEAD(head); 459 406 struct nfsd_file *nf; 407 + unsigned long ret; 460 408 461 - list_for_each_entry(nf, head, nf_lru) { 409 + if (sc) 410 + ret = list_lru_shrink_walk(&nfsd_file_lru, sc, 411 + nfsd_file_lru_cb, &head); 412 + else 413 + ret = list_lru_walk(&nfsd_file_lru, 414 + nfsd_file_lru_cb, 415 + &head, LONG_MAX); 416 + list_for_each_entry(nf, &head, nf_lru) { 462 417 spin_lock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock); 463 418 nfsd_file_do_unhash(nf); 464 419 spin_unlock(&nfsd_file_hashtbl[nf->nf_hashval].nfb_lock); 465 420 } 466 - nfsd_file_dispose_list(head); 421 + nfsd_file_dispose_list_delayed(&head); 422 + return ret; 423 + } 424 + 425 + static void 426 + nfsd_file_gc(void) 427 + { 428 + nfsd_file_lru_walk_list(NULL); 429 + } 430 + 431 + static void 432 + nfsd_file_gc_worker(struct work_struct *work) 433 + { 434 + nfsd_file_gc(); 435 + nfsd_file_schedule_laundrette(); 467 436 } 468 437 469 438 static unsigned long ··· 498 421 static unsigned long 499 422 nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc) 500 423 { 501 - LIST_HEAD(head); 502 - unsigned long ret; 503 - 504 - ret = list_lru_shrink_walk(&nfsd_file_lru, sc, nfsd_file_lru_cb, &head); 505 - nfsd_file_lru_dispose(&head); 506 - return ret; 424 + return nfsd_file_lru_walk_list(sc); 507 425 } 508 426 509 427 static struct shrinker nfsd_file_shrinker = { ··· 560 488 561 489 __nfsd_file_close_inode(inode, hashval, &dispose); 562 490 trace_nfsd_file_close_inode(inode, hashval, !list_empty(&dispose)); 563 - nfsd_file_dispose_list(&dispose); 491 + nfsd_file_dispose_list_delayed(&dispose); 564 492 } 565 493 566 494 /** ··· 576 504 nfsd_file_delayed_close(struct work_struct *work) 577 505 { 578 506 LIST_HEAD(head); 507 + struct nfsd_fcache_disposal *l = container_of(work, 508 + struct nfsd_fcache_disposal, work); 579 509 580 - list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb, &head, LONG_MAX); 581 - 582 - if (test_and_clear_bit(NFSD_FILE_LRU_RESCAN, &nfsd_file_lru_flags)) 583 - nfsd_file_schedule_laundrette(NFSD_FILE_LAUNDRETTE_NOFLUSH); 584 - 585 - if (!list_empty(&head)) { 586 - nfsd_file_lru_dispose(&head); 587 - flush_delayed_fput(); 588 - } 510 + nfsd_file_list_remove_disposal(&head, l); 511 + nfsd_file_dispose_list(&head); 589 512 } 590 513 591 514 static int ··· 641 574 if (nfsd_file_hashtbl) 642 575 return 0; 643 576 577 + nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0); 578 + if (!nfsd_filecache_wq) 579 + goto out; 580 + 644 581 nfsd_file_hashtbl = kcalloc(NFSD_FILE_HASH_SIZE, 645 582 sizeof(*nfsd_file_hashtbl), GFP_KERNEL); 646 583 if (!nfsd_file_hashtbl) { ··· 698 627 spin_lock_init(&nfsd_file_hashtbl[i].nfb_lock); 699 628 } 700 629 701 - INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_delayed_close); 630 + INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker); 702 631 out: 703 632 return ret; 704 633 out_notifier: ··· 714 643 nfsd_file_mark_slab = NULL; 715 644 kfree(nfsd_file_hashtbl); 716 645 nfsd_file_hashtbl = NULL; 646 + destroy_workqueue(nfsd_filecache_wq); 647 + nfsd_filecache_wq = NULL; 717 648 goto out; 718 649 } 719 650 ··· 754 681 } 755 682 } 756 683 684 + static struct nfsd_fcache_disposal * 685 + nfsd_alloc_fcache_disposal(struct net *net) 686 + { 687 + struct nfsd_fcache_disposal *l; 688 + 689 + l = kmalloc(sizeof(*l), GFP_KERNEL); 690 + if (!l) 691 + return NULL; 692 + INIT_WORK(&l->work, nfsd_file_delayed_close); 693 + l->net = net; 694 + spin_lock_init(&l->lock); 695 + INIT_LIST_HEAD(&l->freeme); 696 + return l; 697 + } 698 + 699 + static void 700 + nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l) 701 + { 702 + rcu_assign_pointer(l->net, NULL); 703 + cancel_work_sync(&l->work); 704 + nfsd_file_dispose_list(&l->freeme); 705 + kfree_rcu(l, rcu); 706 + } 707 + 708 + static void 709 + nfsd_add_fcache_disposal(struct nfsd_fcache_disposal *l) 710 + { 711 + spin_lock(&laundrette_lock); 712 + list_add_tail_rcu(&l->list, &laundrettes); 713 + spin_unlock(&laundrette_lock); 714 + } 715 + 716 + static void 717 + nfsd_del_fcache_disposal(struct nfsd_fcache_disposal *l) 718 + { 719 + spin_lock(&laundrette_lock); 720 + list_del_rcu(&l->list); 721 + spin_unlock(&laundrette_lock); 722 + } 723 + 724 + static int 725 + nfsd_alloc_fcache_disposal_net(struct net *net) 726 + { 727 + struct nfsd_fcache_disposal *l; 728 + 729 + l = nfsd_alloc_fcache_disposal(net); 730 + if (!l) 731 + return -ENOMEM; 732 + nfsd_add_fcache_disposal(l); 733 + return 0; 734 + } 735 + 736 + static void 737 + nfsd_free_fcache_disposal_net(struct net *net) 738 + { 739 + struct nfsd_fcache_disposal *l; 740 + 741 + rcu_read_lock(); 742 + list_for_each_entry_rcu(l, &laundrettes, list) { 743 + if (l->net != net) 744 + continue; 745 + nfsd_del_fcache_disposal(l); 746 + rcu_read_unlock(); 747 + nfsd_free_fcache_disposal(l); 748 + return; 749 + } 750 + rcu_read_unlock(); 751 + } 752 + 753 + int 754 + nfsd_file_cache_start_net(struct net *net) 755 + { 756 + return nfsd_alloc_fcache_disposal_net(net); 757 + } 758 + 759 + void 760 + nfsd_file_cache_shutdown_net(struct net *net) 761 + { 762 + nfsd_file_cache_purge(net); 763 + nfsd_free_fcache_disposal_net(net); 764 + } 765 + 757 766 void 758 767 nfsd_file_cache_shutdown(void) 759 768 { ··· 860 705 nfsd_file_mark_slab = NULL; 861 706 kfree(nfsd_file_hashtbl); 862 707 nfsd_file_hashtbl = NULL; 708 + destroy_workqueue(nfsd_filecache_wq); 709 + nfsd_filecache_wq = NULL; 863 710 } 864 711 865 712 static bool ··· 1029 872 nfsd_file_hashtbl[hashval].nfb_maxcount = max(nfsd_file_hashtbl[hashval].nfb_maxcount, 1030 873 nfsd_file_hashtbl[hashval].nfb_count); 1031 874 spin_unlock(&nfsd_file_hashtbl[hashval].nfb_lock); 1032 - atomic_long_inc(&nfsd_filecache_count); 875 + if (atomic_long_inc_return(&nfsd_filecache_count) >= NFSD_FILE_LRU_THRESHOLD) 876 + nfsd_file_gc(); 1033 877 1034 878 nf->nf_mark = nfsd_file_mark_find_or_create(nf); 1035 879 if (nf->nf_mark)
+2
fs/nfsd/filecache.h
··· 51 51 int nfsd_file_cache_init(void); 52 52 void nfsd_file_cache_purge(struct net *); 53 53 void nfsd_file_cache_shutdown(void); 54 + int nfsd_file_cache_start_net(struct net *net); 55 + void nfsd_file_cache_shutdown_net(struct net *net); 54 56 void nfsd_file_put(struct nfsd_file *nf); 55 57 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); 56 58 void nfsd_file_close_inode_sync(struct inode *inode);
+7 -2
fs/nfsd/nfssvc.c
··· 400 400 nn->lockd_up = true; 401 401 } 402 402 403 - ret = nfs4_state_start_net(net); 403 + ret = nfsd_file_cache_start_net(net); 404 404 if (ret) 405 405 goto out_lockd; 406 + ret = nfs4_state_start_net(net); 407 + if (ret) 408 + goto out_filecache; 406 409 407 410 nn->nfsd_net_up = true; 408 411 return 0; 409 412 413 + out_filecache: 414 + nfsd_file_cache_shutdown_net(net); 410 415 out_lockd: 411 416 if (nn->lockd_up) { 412 417 lockd_down(net); ··· 426 421 { 427 422 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 428 423 429 - nfsd_file_cache_purge(net); 424 + nfsd_file_cache_shutdown_net(net); 430 425 nfs4_state_shutdown_net(net); 431 426 if (nn->lockd_up) { 432 427 lockd_down(net);