Skip to content

Commit ffb4025

Browse files
neilbrownchucklever
authored andcommitted
nfsd: Don't leave work of closing files to a work queue
The work of closing a file can have non-trivial cost. Doing it in a separate work queue thread means that cost isn't imposed on the nfsd threads and an imbalance can be created. This can result in files being queued for the work queue more quickly that the work queue can process them, resulting in unbounded growth of the queue and memory exhaustion. To avoid this work imbalance that exhausts memory, this patch moves all closing of files into the nfsd threads. This means that when the work imposes a cost, that cost appears where it would be expected - in the work of the nfsd thread. A subsequent patch will ensure the final __fput() is called in the same (nfsd) thread which calls filp_close(). Files opened for NFSv3 are never explicitly closed by the client and are kept open by the server in the "filecache", which responds to memory pressure, is garbage collected even when there is no pressure, and sometimes closes files when there is particular need such as for rename. These files currently have filp_close() called in a dedicated work queue, so their __fput() can have no effect on nfsd threads. This patch discards the work queue and instead has each nfsd thread call flip_close() on as many as 8 files from the filecache each time it acts on a client request (or finds there are no pending client requests). If there are more to be closed, more threads are woken. This spreads the work of __fput() over multiple threads and imposes any cost on those threads. The number 8 is somewhat arbitrary. It needs to be greater than 1 to ensure that files are closed more quickly than they can be added to the cache. It needs to be small enough to limit the per-request delays that will be imposed on clients when all threads are busy closing files. Signed-off-by: NeilBrown <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent 561141d commit ffb4025

File tree

3 files changed

+34
-36
lines changed

3 files changed

+34
-36
lines changed

fs/nfsd/filecache.c

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,10 @@ static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
6161
static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
6262

6363
struct nfsd_fcache_disposal {
64-
struct work_struct work;
6564
spinlock_t lock;
6665
struct list_head freeme;
6766
};
6867

69-
static struct workqueue_struct *nfsd_filecache_wq __read_mostly;
70-
7168
static struct kmem_cache *nfsd_file_slab;
7269
static struct kmem_cache *nfsd_file_mark_slab;
7370
static struct list_lru nfsd_file_lru;
@@ -421,7 +418,37 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose)
421418
spin_lock(&l->lock);
422419
list_move_tail(&nf->nf_lru, &l->freeme);
423420
spin_unlock(&l->lock);
424-
queue_work(nfsd_filecache_wq, &l->work);
421+
svc_wake_up(nn->nfsd_serv);
422+
}
423+
}
424+
425+
/**
426+
* nfsd_file_net_dispose - deal with nfsd_files waiting to be disposed.
427+
* @nn: nfsd_net in which to find files to be disposed.
428+
*
429+
* When files held open for nfsv3 are removed from the filecache, whether
430+
* due to memory pressure or garbage collection, they are queued to
431+
* a per-net-ns queue. This function completes the disposal, either
432+
* directly or by waking another nfsd thread to help with the work.
433+
*/
434+
void nfsd_file_net_dispose(struct nfsd_net *nn)
435+
{
436+
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
437+
438+
if (!list_empty(&l->freeme)) {
439+
LIST_HEAD(dispose);
440+
int i;
441+
442+
spin_lock(&l->lock);
443+
for (i = 0; i < 8 && !list_empty(&l->freeme); i++)
444+
list_move(l->freeme.next, &dispose);
445+
spin_unlock(&l->lock);
446+
if (!list_empty(&l->freeme))
447+
/* Wake up another thread to share the work
448+
* *before* doing any actual disposing.
449+
*/
450+
svc_wake_up(nn->nfsd_serv);
451+
nfsd_file_dispose_list(&dispose);
425452
}
426453
}
427454

@@ -634,27 +661,6 @@ nfsd_file_close_inode_sync(struct inode *inode)
634661
flush_delayed_fput();
635662
}
636663

637-
/**
638-
* nfsd_file_delayed_close - close unused nfsd_files
639-
* @work: dummy
640-
*
641-
* Scrape the freeme list for this nfsd_net, and then dispose of them
642-
* all.
643-
*/
644-
static void
645-
nfsd_file_delayed_close(struct work_struct *work)
646-
{
647-
LIST_HEAD(head);
648-
struct nfsd_fcache_disposal *l = container_of(work,
649-
struct nfsd_fcache_disposal, work);
650-
651-
spin_lock(&l->lock);
652-
list_splice_init(&l->freeme, &head);
653-
spin_unlock(&l->lock);
654-
655-
nfsd_file_dispose_list(&head);
656-
}
657-
658664
static int
659665
nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg,
660666
void *data)
@@ -717,10 +723,6 @@ nfsd_file_cache_init(void)
717723
return ret;
718724

719725
ret = -ENOMEM;
720-
nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", WQ_UNBOUND, 0);
721-
if (!nfsd_filecache_wq)
722-
goto out;
723-
724726
nfsd_file_slab = kmem_cache_create("nfsd_file",
725727
sizeof(struct nfsd_file), 0, 0, NULL);
726728
if (!nfsd_file_slab) {
@@ -735,7 +737,6 @@ nfsd_file_cache_init(void)
735737
goto out_err;
736738
}
737739

738-
739740
ret = list_lru_init(&nfsd_file_lru);
740741
if (ret) {
741742
pr_err("nfsd: failed to init nfsd_file_lru: %d\n", ret);
@@ -785,8 +786,6 @@ nfsd_file_cache_init(void)
785786
nfsd_file_slab = NULL;
786787
kmem_cache_destroy(nfsd_file_mark_slab);
787788
nfsd_file_mark_slab = NULL;
788-
destroy_workqueue(nfsd_filecache_wq);
789-
nfsd_filecache_wq = NULL;
790789
rhltable_destroy(&nfsd_file_rhltable);
791790
goto out;
792791
}
@@ -832,7 +831,6 @@ nfsd_alloc_fcache_disposal(void)
832831
l = kmalloc(sizeof(*l), GFP_KERNEL);
833832
if (!l)
834833
return NULL;
835-
INIT_WORK(&l->work, nfsd_file_delayed_close);
836834
spin_lock_init(&l->lock);
837835
INIT_LIST_HEAD(&l->freeme);
838836
return l;
@@ -841,7 +839,6 @@ nfsd_alloc_fcache_disposal(void)
841839
static void
842840
nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
843841
{
844-
cancel_work_sync(&l->work);
845842
nfsd_file_dispose_list(&l->freeme);
846843
kfree(l);
847844
}
@@ -910,8 +907,6 @@ nfsd_file_cache_shutdown(void)
910907
fsnotify_wait_marks_destroyed();
911908
kmem_cache_destroy(nfsd_file_mark_slab);
912909
nfsd_file_mark_slab = NULL;
913-
destroy_workqueue(nfsd_filecache_wq);
914-
nfsd_filecache_wq = NULL;
915910
rhltable_destroy(&nfsd_file_rhltable);
916911

917912
for_each_possible_cpu(i) {

fs/nfsd/filecache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ void nfsd_file_cache_shutdown_net(struct net *net);
5656
void nfsd_file_put(struct nfsd_file *nf);
5757
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
5858
void nfsd_file_close_inode_sync(struct inode *inode);
59+
void nfsd_file_net_dispose(struct nfsd_net *nn);
5960
bool nfsd_file_is_cached(struct inode *inode);
6061
__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
6162
unsigned int may_flags, struct nfsd_file **nfp);

fs/nfsd/nfssvc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,8 @@ nfsd(void *vrqstp)
941941
rqstp->rq_server->sv_maxconn = nn->max_connections;
942942

943943
svc_recv(rqstp);
944+
945+
nfsd_file_net_dispose(nn);
944946
}
945947

946948
atomic_dec(&nfsdstats.th_cnt);

0 commit comments

Comments
 (0)