Skip to content

Commit 4bdbba5

Browse files
jtlaytonchucklever
authored andcommitted
nfsd: don't free files unconditionally in __nfsd_file_cache_purge
nfsd_file_cache_purge is called when the server is shutting down, in which case, tearing things down is generally fine, but it also gets called when the exports cache is flushed. Instead of walking the cache and freeing everything unconditionally, handle it the same as when we have a notification of conflicting access. Fixes: ac3a258 ("nfsd: rework refcounting in filecache") Reported-by: Ruben Vestergaard <rubenv@drcmr.dk> Reported-by: Torkil Svensgaard <torkil@drcmr.dk> Reported-by: Shachar Kagan <skagan@nvidia.com> Signed-off-by: Jeff Layton <jlayton@kernel.org> Tested-by: Shachar Kagan <skagan@nvidia.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 7c24fa2 commit 4bdbba5

File tree

1 file changed

+36
-25
lines changed

1 file changed

+36
-25
lines changed

fs/nfsd/filecache.c

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,39 @@ static struct shrinker nfsd_file_shrinker = {
661661
.seeks = 1,
662662
};
663663

664+
/**
665+
* nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
666+
* @nf: nfsd_file to attempt to queue
667+
* @dispose: private list to queue successfully-put objects
668+
*
669+
* Unhash an nfsd_file, try to get a reference to it, and then put that
670+
* reference. If it's the last reference, queue it to the dispose list.
671+
*/
672+
static void
673+
nfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
674+
__must_hold(RCU)
675+
{
676+
int decrement = 1;
677+
678+
/* If we raced with someone else unhashing, ignore it */
679+
if (!nfsd_file_unhash(nf))
680+
return;
681+
682+
/* If we can't get a reference, ignore it */
683+
if (!nfsd_file_get(nf))
684+
return;
685+
686+
/* Extra decrement if we remove from the LRU */
687+
if (nfsd_file_lru_remove(nf))
688+
++decrement;
689+
690+
/* If refcount goes to 0, then put on the dispose list */
691+
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
692+
list_add(&nf->nf_lru, dispose);
693+
trace_nfsd_file_closing(nf);
694+
}
695+
}
696+
664697
/**
665698
* nfsd_file_queue_for_close: try to close out any open nfsd_files for an inode
666699
* @inode: inode on which to close out nfsd_files
@@ -688,30 +721,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
688721

689722
rcu_read_lock();
690723
do {
691-
int decrement = 1;
692-
693724
nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key,
694725
nfsd_file_rhash_params);
695726
if (!nf)
696727
break;
697-
698-
/* If we raced with someone else unhashing, ignore it */
699-
if (!nfsd_file_unhash(nf))
700-
continue;
701-
702-
/* If we can't get a reference, ignore it */
703-
if (!nfsd_file_get(nf))
704-
continue;
705-
706-
/* Extra decrement if we remove from the LRU */
707-
if (nfsd_file_lru_remove(nf))
708-
++decrement;
709-
710-
/* If refcount goes to 0, then put on the dispose list */
711-
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
712-
list_add(&nf->nf_lru, dispose);
713-
trace_nfsd_file_closing(nf);
714-
}
728+
nfsd_file_cond_queue(nf, dispose);
715729
} while (1);
716730
rcu_read_unlock();
717731
}
@@ -928,11 +942,8 @@ __nfsd_file_cache_purge(struct net *net)
928942

929943
nf = rhashtable_walk_next(&iter);
930944
while (!IS_ERR_OR_NULL(nf)) {
931-
if (!net || nf->nf_net == net) {
932-
nfsd_file_unhash(nf);
933-
nfsd_file_lru_remove(nf);
934-
list_add(&nf->nf_lru, &dispose);
935-
}
945+
if (!net || nf->nf_net == net)
946+
nfsd_file_cond_queue(nf, &dispose);
936947
nf = rhashtable_walk_next(&iter);
937948
}
938949

0 commit comments

Comments
 (0)