From: Chuck Lever <cel@kernel.org>
To: NeilBrown <neilb@ownmail.net>, Jeff Layton <jlayton@kernel.org>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <dai.ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Cc: <linux-nfs@vger.kernel.org>, <linux-fsdevel@vger.kernel.org>,
Chuck Lever <chuck.lever@oracle.com>
Subject: [PATCH v2 6/6] nfsd: close cached files on filesystem unmount
Date: Wed, 7 Jan 2026 19:40:16 -0500 [thread overview]
Message-ID: <20260108004016.3907158-7-cel@kernel.org> (raw)
In-Reply-To: <20260108004016.3907158-1-cel@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
When a filesystem is unmounted while NFS is exporting it, the
unmount can fail with EBUSY even after NFSv4 state has been revoked.
This is because the nfsd_file cache can hold open NFSv2/3 file
handles that pin the filesystem, preventing the unmount from
completing.
Extend the mechanism that revokes NFSv4 state on unmount to also
close cached file handles. nfsd_file_close_sb() walks the nfsd_file
cache and disposes of entries belonging to the target superblock.
It runs after NFSv4 state revocation, so it handles only NFSv2/3
file handles that remain in the cache.
Entries still under construction (with nf_file not yet set) are
skipped; these have no open file to close.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfsd/filecache.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
fs/nfsd/filecache.h | 1 +
fs/nfsd/pin.c | 6 ++++--
3 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 93798575b807..b921a9553f36 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -894,6 +894,50 @@ __nfsd_file_cache_purge(struct net *net)
nfsd_file_dispose_list(&dispose);
}
+/**
+ * nfsd_file_close_sb - close GC-managed cached files for a superblock
+ * @sb: target superblock
+ *
+ * Walk the nfsd_file cache and close out GC-managed entries (those
+ * acquired via nfsd_file_acquire_gc) that belong to @sb. Called during
+ * filesystem unmount after NFSv4 state revocation to release remaining
+ * cached file handles that may be pinning the filesystem.
+ */
+void nfsd_file_close_sb(struct super_block *sb)
+{
+ struct rhashtable_iter iter;
+ struct nfsd_file *nf;
+ LIST_HEAD(dispose);
+
+ mutex_lock(&nfsd_mutex);
+ if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0) {
+ mutex_unlock(&nfsd_mutex);
+ return;
+ }
+
+ rhltable_walk_enter(&nfsd_file_rhltable, &iter);
+ do {
+ rhashtable_walk_start(&iter);
+
+ nf = rhashtable_walk_next(&iter);
+ while (!IS_ERR_OR_NULL(nf)) {
+ if (test_bit(NFSD_FILE_GC, &nf->nf_flags) == 0)
+ goto next;
+ /* Skip entries under construction (nf_file not yet set) */
+ if (nf->nf_file && file_inode(nf->nf_file)->i_sb == sb)
+ nfsd_file_cond_queue(nf, &dispose);
+next:
+ nf = rhashtable_walk_next(&iter);
+ }
+
+ rhashtable_walk_stop(&iter);
+ } while (nf == ERR_PTR(-EAGAIN));
+ rhashtable_walk_exit(&iter);
+ mutex_unlock(&nfsd_mutex);
+
+ nfsd_file_dispose_list(&dispose);
+}
+
static struct nfsd_fcache_disposal *
nfsd_alloc_fcache_disposal(void)
{
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index b383dbc5b921..66ca7fc6189b 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -70,6 +70,7 @@ struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
struct file *nfsd_file_file(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode);
+void nfsd_file_close_sb(struct super_block *sb);
void nfsd_file_net_dispose(struct nfsd_net *nn);
bool nfsd_file_is_cached(struct inode *inode);
__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
diff --git a/fs/nfsd/pin.c b/fs/nfsd/pin.c
index eefa4baff82c..a404611c20a0 100644
--- a/fs/nfsd/pin.c
+++ b/fs/nfsd/pin.c
@@ -19,6 +19,7 @@
#include "nfsd.h"
#include "netns.h"
#include "state.h"
+#include "filecache.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -49,8 +50,8 @@ static void nfsd_fs_pin_free_rcu(struct rcu_head *rcu)
/*
* Work function for nfsd_fs_pin - runs in process context.
- * Cancels async COPYs, releases NLM locks, and revokes NFSv4 state for
- * the superblock.
+ * Cancels async COPYs, releases NLM locks, revokes NFSv4 state, and closes
+ * cached NFSv2/3 files for the superblock.
*/
static void nfsd_fs_pin_work(struct work_struct *work)
{
@@ -63,6 +64,7 @@ static void nfsd_fs_pin_work(struct work_struct *work)
/* Errors are logged by lockd; no recovery is possible. */
(void)nlmsvc_unlock_all_by_sb(p->sb);
nfsd4_revoke_states(nn, p->sb);
+ nfsd_file_close_sb(p->sb);
pr_info("nfsd: state revocation for %s complete\n", p->sb->s_id);
--
2.52.0
next prev parent reply other threads:[~2026-01-08 0:40 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-08 0:40 [PATCH v2 0/6] Automatic NFSv4 state revocation on filesystem unmount Chuck Lever
2026-01-08 0:40 ` [PATCH v2 1/6] nfsd: cancel async COPY operations when admin revokes filesystem state Chuck Lever
2026-01-08 0:40 ` [PATCH v2 2/6] fs: export pin_insert and pin_remove for modular filesystems Chuck Lever
2026-01-08 0:40 ` [PATCH v2 3/6] fs: add pin_insert_sb() for superblock-only pins Chuck Lever
2026-01-08 0:40 ` [PATCH v2 4/6] fs: invoke group_pin_kill() during mount teardown Chuck Lever
2026-01-09 8:38 ` NeilBrown
2026-01-09 16:04 ` Chuck Lever
2026-01-10 16:49 ` Al Viro
2026-01-10 20:07 ` Chuck Lever
2026-01-10 21:52 ` NeilBrown
2026-01-10 22:08 ` Al Viro
2026-01-10 22:31 ` Chuck Lever
2026-01-08 0:40 ` [PATCH v2 5/6] nfsd: revoke NFSv4 state when filesystem is unmounted Chuck Lever
2026-01-09 9:06 ` NeilBrown
2026-01-08 0:40 ` Chuck Lever [this message]
2026-01-09 16:25 ` [PATCH v2 0/6] Automatic NFSv4 state revocation on filesystem unmount Jeff Layton
2026-01-12 9:16 ` Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260108004016.3907158-7-cel@kernel.org \
--to=cel@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=dai.ngo@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neilb@ownmail.net \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.