* [PATCH 0/3 RFC] improve some nfsd_mutex locking
@ 2025-06-18 21:31 NeilBrown
2025-06-18 21:31 ` [PATCH 1/3] nfsd: provide proper locking for all write_ function NeilBrown
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: NeilBrown @ 2025-06-18 21:31 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
The first patch hopefully fixes a bug with locking: some write_foo
functions aren't locked properly.
The other two improve the locking code, particulary so that we don't
need a global mutex to change per-netns data.
NeilBrown
[PATCH 1/3] nfsd: provide proper locking for all write_ function
[PATCH 2/3] nfsd: use kref and new mutex for global config management
[PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/3] nfsd: provide proper locking for all write_ function
2025-06-18 21:31 [PATCH 0/3 RFC] improve some nfsd_mutex locking NeilBrown
@ 2025-06-18 21:31 ` NeilBrown
2025-06-20 12:59 ` Jeff Layton
2025-06-18 21:31 ` [PATCH 2/3] nfsd: use kref and new mutex for global config management NeilBrown
2025-06-18 21:31 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
2 siblings, 1 reply; 11+ messages in thread
From: NeilBrown @ 2025-06-18 21:31 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
write_foo functions are called to handle IO to files in /proc/fs/nfsd/.
The can be called at any time and so generally need locking to ensure
they don't happen at an awkward time.
Many already take nfsd_mutex and check if nfsd_serv has been set. This
ensures they only run when the server is fully configured.
write_filehandle() does *not* need locking. It interacts with the
export table which is set up when the netns is set up, so it is always
valid and it has its own locking. write_filehandle() is needed before
the nfs server is started so checking nfsd_serv would be wrong.
The remaining files which do not have any locking are
write_v4_end_grace(), write_unlock_ip(), and write_unlock_fs().
None of these make sense when the nfs server is not running and there is
evidence that write_v4_end_grace() can race with ->client_tracking_op
setup/shutdown and cause problems.
This patch adds locking to these three and ensures the "unlock"
functions abort if ->nfsd_serv is not set.
Reported-by: Li Lingfeng <lilingfeng3@huawei.com>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/nfsd/nfsctl.c | 115 +++++++++++++++++++++++++++++++----------------
1 file changed, 77 insertions(+), 38 deletions(-)
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 3f3e9f6c4250..3710a1992d17 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -200,27 +200,18 @@ static inline struct net *netns(struct file *file)
return file_inode(file)->i_sb->s_fs_info;
}
-/*
- * write_unlock_ip - Release all locks used by a client
- *
- * Experimental.
- *
- * Input:
- * buf: '\n'-terminated C string containing a
- * presentation format IP address
- * size: length of C string in @buf
- * Output:
- * On success: returns zero if all specified locks were released;
- * returns one if one or more locks were not released
- * On error: return code is negative errno value
- */
-static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
+static ssize_t __write_unlock_ip(struct file *file, char *buf, size_t size)
{
struct sockaddr_storage address;
struct sockaddr *sap = (struct sockaddr *)&address;
size_t salen = sizeof(address);
char *fo_path;
struct net *net = netns(file);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+ if (!nn->nfsd_serv)
+ /* There cannot be any files to unlock */
+ return -EINVAL;
/* sanity check */
if (size == 0)
@@ -241,24 +232,39 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
}
/*
- * write_unlock_fs - Release all locks on a local file system
+ * write_unlock_ip - Release all locks used by a client
*
* Experimental.
*
* Input:
- * buf: '\n'-terminated C string containing the
- * absolute pathname of a local file system
+ * buf: '\n'-terminated C string containing a
+ * presentation format IP address
* size: length of C string in @buf
* Output:
* On success: returns zero if all specified locks were released;
* returns one if one or more locks were not released
* On error: return code is negative errno value
*/
-static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
+static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
+{
+ ssize_t rv;
+
+ mutex_lock(&nfsd_mutex);
+ rv = __write_unlock_ip(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
+}
+
+static ssize_t __write_unlock_fs(struct file *file, char *buf, size_t size)
{
struct path path;
char *fo_path;
int error;
+ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
+
+ if (!nn->nfsd_serv)
+ /* There cannot be any files to unlock */
+ return -EINVAL;
/* sanity check */
if (size == 0)
@@ -291,6 +297,30 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
return error;
}
+/*
+ * write_unlock_fs - Release all locks on a local file system
+ *
+ * Experimental.
+ *
+ * Input:
+ * buf: '\n'-terminated C string containing the
+ * absolute pathname of a local file system
+ * size: length of C string in @buf
+ * Output:
+ * On success: returns zero if all specified locks were released;
+ * returns one if one or more locks were not released
+ * On error: return code is negative errno value
+ */
+static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
+{
+ ssize_t rv;
+
+ mutex_lock(&nfsd_mutex);
+ rv = __write_unlock_fs(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
+}
+
/*
* write_filehandle - Get a variable-length NFS file handle by path
*
@@ -1053,6 +1083,29 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
}
#endif
+static ssize_t __write_v4_end_grace(struct file *file, char *buf, size_t size)
+{
+ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
+
+ if (size > 0) {
+ switch(buf[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ if (!nn->nfsd_serv)
+ return -EBUSY;
+ trace_nfsd_end_grace(netns(file));
+ nfsd4_end_grace(nn);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n",
+ nn->grace_ended ? 'Y' : 'N');
+}
+
/*
* write_v4_end_grace - release grace period for nfsd's v4.x lock manager
*
@@ -1075,27 +1128,13 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
*/
static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
{
- struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
-
- if (size > 0) {
- switch(buf[0]) {
- case 'Y':
- case 'y':
- case '1':
- if (!nn->nfsd_serv)
- return -EBUSY;
- trace_nfsd_end_grace(netns(file));
- nfsd4_end_grace(nn);
- break;
- default:
- return -EINVAL;
- }
- }
+ ssize_t rv;
- return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n",
- nn->grace_ended ? 'Y' : 'N');
+ mutex_lock(&nfsd_mutex);
+ rv = __write_v4_end_grace(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
}
-
#endif
/*----------------------------------------------------------------------------*/
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/3] nfsd: use kref and new mutex for global config management
2025-06-18 21:31 [PATCH 0/3 RFC] improve some nfsd_mutex locking NeilBrown
2025-06-18 21:31 ` [PATCH 1/3] nfsd: provide proper locking for all write_ function NeilBrown
@ 2025-06-18 21:31 ` NeilBrown
2025-06-19 14:06 ` Chuck Lever
2025-06-20 13:01 ` Jeff Layton
2025-06-18 21:31 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
2 siblings, 2 replies; 11+ messages in thread
From: NeilBrown @ 2025-06-18 21:31 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
nfsd_mutex is used for two quite different things:
1/ it prevents races when start/stoping global resources:
the filecache and the v4 state table
2/ it prevents races for per-netns config, typically
ensure config changes are synchronised w.r.t. server
startup/shutdown.
This patch splits out the first used. A subsequent patch improves the
second.
"nfsd_users" is changed to a kref which is can be taken to delay
the shutdown of global services. nfsd_startup_get(), it is succeeds,
ensure the global services will remain until nfsd_startup_put().
The new mutex, nfsd_startup_mutex, is only take for startup and
shutdown. It is not needed to protect the kref.
The locking needed by nfsd_file_cache_purge() is now provided internally
by that function so calls don't need to be concerned.
This replaces NFSD_FILE_CACHE_UP which is effective just a flag which
says nfsd_users is non-zero.
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/nfsd/export.c | 6 ------
fs/nfsd/filecache.c | 31 +++++++++++--------------------
fs/nfsd/nfsd.h | 3 +++
fs/nfsd/nfssvc.c | 41 +++++++++++++++++++++++++++--------------
4 files changed, 41 insertions(+), 40 deletions(-)
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index cadfc2bae60e..1ea3d72ef5c9 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -243,13 +243,7 @@ static struct cache_head *expkey_alloc(void)
static void expkey_flush(void)
{
- /*
- * Take the nfsd_mutex here to ensure that the file cache is not
- * destroyed while we're in the middle of flushing.
- */
- mutex_lock(&nfsd_mutex);
nfsd_file_cache_purge(current->nsproxy->net_ns);
- mutex_unlock(&nfsd_mutex);
}
static const struct cache_detail svc_expkey_cache_template = {
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index e108b6c705b4..0a9116b7530c 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -50,8 +50,6 @@
#define NFSD_LAUNDRETTE_DELAY (2 * HZ)
-#define NFSD_FILE_CACHE_UP (0)
-
/* We only care about NFSD_MAY_READ/WRITE for this cache */
#define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
@@ -70,7 +68,6 @@ struct nfsd_fcache_disposal {
static struct kmem_cache *nfsd_file_slab;
static struct kmem_cache *nfsd_file_mark_slab;
static struct list_lru nfsd_file_lru;
-static unsigned long nfsd_file_flags;
static struct fsnotify_group *nfsd_file_fsnotify_group;
static struct delayed_work nfsd_filecache_laundrette;
static struct rhltable nfsd_file_rhltable
@@ -112,9 +109,12 @@ static const struct rhashtable_params nfsd_file_rhash_params = {
static void
nfsd_file_schedule_laundrette(void)
{
- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
- queue_delayed_work(system_unbound_wq, &nfsd_filecache_laundrette,
+ if (nfsd_startup_get()) {
+ queue_delayed_work(system_unbound_wq,
+ &nfsd_filecache_laundrette,
NFSD_LAUNDRETTE_DELAY);
+ nfsd_startup_put();
+ }
}
static void
@@ -795,10 +795,6 @@ nfsd_file_cache_init(void)
{
int ret;
- lockdep_assert_held(&nfsd_mutex);
- if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
- return 0;
-
ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params);
if (ret)
goto out;
@@ -853,8 +849,6 @@ nfsd_file_cache_init(void)
INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
out:
- if (ret)
- clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
return ret;
out_notifier:
lease_unregister_notifier(&nfsd_file_lease_notifier);
@@ -958,9 +952,10 @@ nfsd_file_cache_start_net(struct net *net)
void
nfsd_file_cache_purge(struct net *net)
{
- lockdep_assert_held(&nfsd_mutex);
- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
+ if (nfsd_startup_get()) {
__nfsd_file_cache_purge(net);
+ nfsd_startup_put();
+ }
}
void
@@ -975,10 +970,6 @@ nfsd_file_cache_shutdown(void)
{
int i;
- lockdep_assert_held(&nfsd_mutex);
- if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
- return;
-
lease_unregister_notifier(&nfsd_file_lease_notifier);
shrinker_free(nfsd_file_shrinker);
/*
@@ -1347,8 +1338,7 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
unsigned long lru = 0, total_age = 0;
/* Serialize with server shutdown */
- mutex_lock(&nfsd_mutex);
- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
+ if (nfsd_startup_get()) {
struct bucket_table *tbl;
struct rhashtable *ht;
@@ -1360,8 +1350,9 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
tbl = rht_dereference_rcu(ht->tbl, ht);
buckets = tbl->size;
rcu_read_unlock();
+
+ nfsd_startup_put();
}
- mutex_unlock(&nfsd_mutex);
for_each_possible_cpu(i) {
hits += per_cpu(nfsd_file_cache_hits, i);
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 1bfd0b4e9af7..8ad9fcc23789 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -80,6 +80,9 @@ extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
extern struct mutex nfsd_mutex;
extern atomic_t nfsd_th_cnt; /* number of available threads */
+bool nfsd_startup_get(void);
+void nfsd_startup_put(void);
+
extern const struct seq_operations nfs_exports_op;
/*
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 82b0111ac469..b2080e5a71e6 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -270,38 +270,51 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
return 0;
}
-static int nfsd_users = 0;
+static struct kref nfsd_users = KREF_INIT(0);
+static DEFINE_MUTEX(nfsd_startup_mutex);
static int nfsd_startup_generic(void)
{
- int ret;
+ int ret = 0;
- if (nfsd_users++)
+ if (kref_get_unless_zero(&nfsd_users))
return 0;
+ mutex_lock(&nfsd_startup_mutex);
+ if (kref_get_unless_zero(&nfsd_users))
+ goto out_unlock;
ret = nfsd_file_cache_init();
if (ret)
- goto dec_users;
+ goto out_unlock;
ret = nfs4_state_start();
if (ret)
goto out_file_cache;
- return 0;
+ kref_init(&nfsd_users);
+out_unlock:
+ mutex_unlock(&nfsd_startup_mutex);
+ return ret;
out_file_cache:
nfsd_file_cache_shutdown();
-dec_users:
- nfsd_users--;
- return ret;
+ goto out_unlock;
}
-static void nfsd_shutdown_generic(void)
+static void nfsd_shutdown_cb(struct kref *kref)
{
- if (--nfsd_users)
- return;
-
nfs4_state_shutdown();
nfsd_file_cache_shutdown();
+ mutex_unlock(&nfsd_startup_mutex);
+}
+
+bool nfsd_startup_get(void)
+{
+ return kref_get_unless_zero(&nfsd_users);
+}
+
+void nfsd_startup_put(void)
+{
+ kref_put_mutex(&nfsd_users, nfsd_shutdown_cb, &nfsd_startup_mutex);
}
static bool nfsd_needs_lockd(struct nfsd_net *nn)
@@ -416,7 +429,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
nn->lockd_up = false;
}
out_socks:
- nfsd_shutdown_generic();
+ nfsd_startup_put();
return ret;
}
@@ -443,7 +456,7 @@ static void nfsd_shutdown_net(struct net *net)
percpu_ref_exit(&nn->nfsd_net_ref);
nn->nfsd_net_up = false;
- nfsd_shutdown_generic();
+ nfsd_startup_put();
}
static DEFINE_SPINLOCK(nfsd_notifier_lock);
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
2025-06-18 21:31 [PATCH 0/3 RFC] improve some nfsd_mutex locking NeilBrown
2025-06-18 21:31 ` [PATCH 1/3] nfsd: provide proper locking for all write_ function NeilBrown
2025-06-18 21:31 ` [PATCH 2/3] nfsd: use kref and new mutex for global config management NeilBrown
@ 2025-06-18 21:31 ` NeilBrown
2025-06-19 12:33 ` kernel test robot
2025-06-20 13:13 ` Jeff Layton
2 siblings, 2 replies; 11+ messages in thread
From: NeilBrown @ 2025-06-18 21:31 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
The remaining uses for nfsd_mutex are all to protect per-netns
resources.
This patch replaces the global mutex with one per netns. The "svc_info"
struct now contains that mutex rather than a pointer to the mutex.
Macros are provided to make it easy to take the mutex given a file or net.
Signed-off-by: NeilBrown <neil@brown.name>
---
.../admin-guide/nfs/nfsd-admin-interfaces.rst | 2 +-
fs/nfsd/nfsctl.c | 113 +++++++++---------
fs/nfsd/nfsd.h | 1 -
fs/nfsd/nfssvc.c | 33 ++---
include/linux/sunrpc/svc.h | 2 +-
net/sunrpc/svc_xprt.c | 4 +-
6 files changed, 72 insertions(+), 83 deletions(-)
diff --git a/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst b/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
index c05926f79054..9548e4ab35b6 100644
--- a/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
+++ b/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
@@ -37,4 +37,4 @@ Implementation notes
Note that the rpc server requires the caller to serialize addition and
removal of listening sockets, and startup and shutdown of the server.
-For nfsd this is done using nfsd_mutex.
+For nfsd this is done using nfsd_info.mutex in struct nfsd_net.
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 3710a1992d17..70eddf2640f0 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -95,6 +95,13 @@ static ssize_t (*const write_op[])(struct file *, char *, size_t) = {
#endif
};
+#define with_nfsd_net_locked(__net) \
+ for (struct nfsd_net *__nn = net_generic(__net, nfsd_net_id); \
+ __nn ? ({mutex_lock(&__nn->nfsd_info.mutex); 1; }) : 0; \
+ ({mutex_unlock(&__nn->nfsd_info.mutex); __nn = NULL;}))
+#define with_nfsd_file_locked(__file) \
+ with_nfsd_net_locked(netns(__file))
+
static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
{
ino_t ino = file_inode(file)->i_ino;
@@ -249,9 +256,8 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
{
ssize_t rv;
- mutex_lock(&nfsd_mutex);
- rv = __write_unlock_ip(file, buf, size);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __write_unlock_ip(file, buf, size);
return rv;
}
@@ -315,9 +321,8 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
{
ssize_t rv;
- mutex_lock(&nfsd_mutex);
- rv = __write_unlock_fs(file, buf, size);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __write_unlock_fs(file, buf, size);
return rv;
}
@@ -440,9 +445,8 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
if (newthreads < 0)
return -EINVAL;
trace_nfsd_ctl_threads(net, newthreads);
- mutex_lock(&nfsd_mutex);
- rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_net_locked(net)
+ rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
if (rv < 0)
return rv;
} else
@@ -473,7 +477,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
-static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
+static ssize_t __write_pool_threads(struct file *file, char *buf, size_t size)
{
/* if size > 0, look for an array of number of threads per node
* and apply them then write out number of threads per node as reply
@@ -486,7 +490,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
int *nthreads;
struct net *net = netns(file);
- mutex_lock(&nfsd_mutex);
npools = nfsd_nrpools(net);
if (npools == 0) {
/*
@@ -494,7 +497,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
* writing to the threads file but NOT the pool_threads
* file, sorry. Report zero threads.
*/
- mutex_unlock(&nfsd_mutex);
strcpy(buf, "0\n");
return strlen(buf);
}
@@ -544,10 +546,18 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
rv = mesg - buf;
out_free:
kfree(nthreads);
- mutex_unlock(&nfsd_mutex);
return rv;
}
+static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
+{
+ ssize_t ret;
+
+ with_nfsd_file_locked(file)
+ ret = __write_pool_threads(file, buf, size);
+ return ret;
+}
+
static ssize_t
nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining,
const char *sep, unsigned vers, int minor)
@@ -709,9 +719,9 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
{
ssize_t rv;
- mutex_lock(&nfsd_mutex);
- rv = __write_versions(file, buf, size);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __write_versions(file, buf, size);
+
return rv;
}
@@ -868,9 +878,8 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
{
ssize_t rv;
- mutex_lock(&nfsd_mutex);
- rv = __write_ports(file, buf, size, netns(file));
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __write_ports(file, buf, size, netns(file));
return rv;
}
@@ -916,13 +925,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
bsize = max_t(int, bsize, 1024);
bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE);
bsize &= ~(1024-1);
- mutex_lock(&nfsd_mutex);
+ mutex_lock(&nn->nfsd_info.mutex);
if (nn->nfsd_serv) {
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return -EBUSY;
}
nfsd_max_blksize = bsize;
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
}
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
@@ -971,9 +980,8 @@ static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
{
ssize_t rv;
- mutex_lock(&nfsd_mutex);
- rv = __nfsd4_write_time(file, buf, size, time, nn);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __nfsd4_write_time(file, buf, size, time, nn);
return rv;
}
@@ -1076,9 +1084,8 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
ssize_t rv;
struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
- mutex_lock(&nfsd_mutex);
- rv = __write_recoverydir(file, buf, size, nn);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __write_recoverydir(file, buf, size, nn);
return rv;
}
#endif
@@ -1130,9 +1137,8 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
{
ssize_t rv;
- mutex_lock(&nfsd_mutex);
- rv = __write_v4_end_grace(file, buf, size);
- mutex_unlock(&nfsd_mutex);
+ with_nfsd_file_locked(file)
+ rv = __write_v4_end_grace(file, buf, size);
return rv;
}
#endif
@@ -1552,9 +1558,8 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
int i, ret, rqstp_index = 0;
struct nfsd_net *nn;
- mutex_lock(&nfsd_mutex);
-
nn = net_generic(sock_net(skb->sk), nfsd_net_id);
+ mutex_lock(&nn->nfsd_info.mutex);
if (!nn->nfsd_serv) {
ret = -ENODEV;
goto out_unlock;
@@ -1636,7 +1641,7 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
out:
rcu_read_unlock();
out_unlock:
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return ret;
}
@@ -1665,7 +1670,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
count++;
}
- mutex_lock(&nfsd_mutex);
+ mutex_lock(&nn->nfsd_info.mutex);
nrpools = max(count, nfsd_nrpools(net));
nthreads = kcalloc(nrpools, sizeof(int), GFP_KERNEL);
@@ -1720,7 +1725,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
if (ret > 0)
ret = 0;
out_unlock:
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
kfree(nthreads);
return ret;
}
@@ -1749,7 +1754,7 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_free_msg;
}
- mutex_lock(&nfsd_mutex);
+ mutex_lock(&nn->nfsd_info.mutex);
err = nla_put_u32(skb, NFSD_A_SERVER_GRACETIME,
nn->nfsd4_grace) ||
@@ -1777,14 +1782,14 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_unlock;
}
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_unlock:
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
err_free_msg:
nlmsg_free(skb);
@@ -1807,11 +1812,10 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION))
return -EINVAL;
- mutex_lock(&nfsd_mutex);
-
nn = net_generic(genl_info_net(info), nfsd_net_id);
+ mutex_lock(&nn->nfsd_info.mutex);
if (nn->nfsd_serv) {
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return -EBUSY;
}
@@ -1856,7 +1860,7 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
}
}
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return 0;
}
@@ -1884,7 +1888,7 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_free_msg;
}
- mutex_lock(&nfsd_mutex);
+ mutex_lock(&nn->nfsd_info.mutex);
nn = net_generic(genl_info_net(info), nfsd_net_id);
for (i = 2; i <= 4; i++) {
@@ -1928,13 +1932,13 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
}
}
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_nfsd_unlock:
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
err_free_msg:
nlmsg_free(skb);
@@ -1959,15 +1963,16 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
bool delete = false;
int err, rem;
- mutex_lock(&nfsd_mutex);
+ nn = net_generic(net, nfsd_net_id);
+
+ mutex_lock(&nn->nfsd_info.mutex);
err = nfsd_create_serv(net);
if (err) {
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return err;
}
- nn = net_generic(net, nfsd_net_id);
serv = nn->nfsd_serv;
spin_lock_bh(&serv->sv_lock);
@@ -2083,7 +2088,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
nfsd_destroy_serv(net);
out_unlock_mtx:
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return err;
}
@@ -2113,8 +2118,8 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_free_msg;
}
- mutex_lock(&nfsd_mutex);
nn = net_generic(genl_info_net(info), nfsd_net_id);
+ mutex_lock(&nn->nfsd_info.mutex);
/* no nfs server? Just send empty socket list */
if (!nn->nfsd_serv)
@@ -2144,14 +2149,14 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
}
spin_unlock_bh(&serv->sv_lock);
out_unlock_mtx:
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_serv_unlock:
spin_unlock_bh(&serv->sv_lock);
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
err_free_msg:
nlmsg_free(skb);
@@ -2253,7 +2258,7 @@ static __net_init int nfsd_net_init(struct net *net)
nn->nfsd_versions[i] = nfsd_support_version(i);
for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++)
nn->nfsd4_minorversions[i] = nfsd_support_version(4);
- nn->nfsd_info.mutex = &nfsd_mutex;
+ mutex_init(&nn->nfsd_info.mutex);
nn->nfsd_serv = NULL;
nfsd4_init_leases_net(nn);
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 8ad9fcc23789..3cbca4d34f48 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -77,7 +77,6 @@ struct nfsd_genl_rqstp {
extern struct svc_program nfsd_programs[];
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
-extern struct mutex nfsd_mutex;
extern atomic_t nfsd_th_cnt; /* number of available threads */
bool nfsd_startup_get(void);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index b2080e5a71e6..9f70b1fbc55e 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -56,20 +56,6 @@ static __be32 nfsd_init_request(struct svc_rqst *,
const struct svc_program *,
struct svc_process_info *);
-/*
- * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
- * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
- *
- * Finally, the nfsd_mutex also protects some of the global variables that are
- * accessed when nfsd starts and that are settable via the write_* routines in
- * nfsctl.c. In particular:
- *
- * user_recovery_dirname
- * user_lease_time
- * nfsd_versions
- */
-DEFINE_MUTEX(nfsd_mutex);
-
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
static const struct svc_version *localio_versions[] = {
[1] = &localio_version1,
@@ -242,10 +228,10 @@ int nfsd_nrthreads(struct net *net)
int rv = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- mutex_lock(&nfsd_mutex);
+ mutex_lock(&nn->nfsd_info.mutex);
if (nn->nfsd_serv)
rv = nn->nfsd_serv->sv_nrthreads;
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return rv;
}
@@ -522,7 +508,6 @@ static struct notifier_block nfsd_inet6addr_notifier = {
};
#endif
-/* Only used under nfsd_mutex, so this atomic may be overkill: */
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
/**
@@ -534,7 +519,7 @@ void nfsd_destroy_serv(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv = nn->nfsd_serv;
- lockdep_assert_held(&nfsd_mutex);
+ lockdep_assert_held(&nn->nfsd_info.mutex);
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = NULL;
@@ -606,17 +591,17 @@ void nfsd_shutdown_threads(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
- mutex_lock(&nfsd_mutex);
+ mutex_lock(&nn->nfsd_info.mutex);
serv = nn->nfsd_serv;
if (serv == NULL) {
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
return;
}
/* Kill outstanding nfsd threads */
svc_set_num_threads(serv, NULL, 0);
nfsd_destroy_serv(net);
- mutex_unlock(&nfsd_mutex);
+ mutex_unlock(&nn->nfsd_info.mutex);
}
struct svc_rqst *nfsd_current_rqst(void)
@@ -632,7 +617,7 @@ int nfsd_create_serv(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
- WARN_ON(!mutex_is_locked(&nfsd_mutex));
+ WARN_ON(!mutex_is_locked(&nn->nfsd_info.mutex));
if (nn->nfsd_serv)
return 0;
@@ -714,7 +699,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
int err = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- lockdep_assert_held(&nfsd_mutex);
+ lockdep_assert_held(&nn->nfsd_info.mutex);
if (nn->nfsd_serv == NULL || n <= 0)
return 0;
@@ -787,7 +772,7 @@ nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const c
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
- lockdep_assert_held(&nfsd_mutex);
+ lockdep_assert_held(&nn->nfsd_info.mutex);
dprintk("nfsd: creating service\n");
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 48666b83fe68..a12fe99156ec 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -98,7 +98,7 @@ struct svc_serv {
/* This is used by pool_stats to find and lock an svc */
struct svc_info {
struct svc_serv *serv;
- struct mutex *mutex;
+ struct mutex mutex;
};
void svc_destroy(struct svc_serv **svcp);
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 8b1837228799..b8352b7d6860 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -1399,7 +1399,7 @@ static void *svc_pool_stats_start(struct seq_file *m, loff_t *pos)
dprintk("svc_pool_stats_start, *pidx=%u\n", pidx);
- mutex_lock(si->mutex);
+ mutex_lock(&si->mutex);
if (!pidx)
return SEQ_START_TOKEN;
@@ -1436,7 +1436,7 @@ static void svc_pool_stats_stop(struct seq_file *m, void *p)
{
struct svc_info *si = m->private;
- mutex_unlock(si->mutex);
+ mutex_unlock(&si->mutex);
}
static int svc_pool_stats_show(struct seq_file *m, void *p)
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
2025-06-18 21:31 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
@ 2025-06-19 12:33 ` kernel test robot
2025-06-20 13:13 ` Jeff Layton
1 sibling, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-06-19 12:33 UTC (permalink / raw)
To: NeilBrown, Chuck Lever, Jeff Layton
Cc: llvm, oe-kbuild-all, linux-nfs, Olga Kornievskaia, Dai Ngo,
Tom Talpey, Li Lingfeng
Hi NeilBrown,
kernel test robot noticed the following build warnings:
[auto build test WARNING on brauner-vfs/vfs.all]
[also build test WARNING on trondmy-nfs/linux-next linus/master v6.16-rc2 next-20250619]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/NeilBrown/nfsd-provide-proper-locking-for-all-write_-function/20250619-053514
base: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
patch link: https://lore.kernel.org/r/20250618213347.425503-4-neil%40brown.name
patch subject: [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20250619/202506192052.L9tj28RJ-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250619/202506192052.L9tj28RJ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506192052.L9tj28RJ-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> fs/nfsd/nfsctl.c:1891:14: warning: variable 'nn' is uninitialized when used here [-Wuninitialized]
1891 | mutex_lock(&nn->nfsd_info.mutex);
| ^~
fs/nfsd/nfsctl.c:1877:21: note: initialize the variable 'nn' to silence this warning
1877 | struct nfsd_net *nn;
| ^
| = NULL
1 warning generated.
vim +/nn +1891 fs/nfsd/nfsctl.c
1867
1868 /**
1869 * nfsd_nl_version_get_doit - get the enabled status for all supported nfs versions
1870 * @skb: reply buffer
1871 * @info: netlink metadata and command arguments
1872 *
1873 * Return 0 on success or a negative errno.
1874 */
1875 int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
1876 {
1877 struct nfsd_net *nn;
1878 int i, err;
1879 void *hdr;
1880
1881 skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
1882 if (!skb)
1883 return -ENOMEM;
1884
1885 hdr = genlmsg_iput(skb, info);
1886 if (!hdr) {
1887 err = -EMSGSIZE;
1888 goto err_free_msg;
1889 }
1890
> 1891 mutex_lock(&nn->nfsd_info.mutex);
1892 nn = net_generic(genl_info_net(info), nfsd_net_id);
1893
1894 for (i = 2; i <= 4; i++) {
1895 int j;
1896
1897 for (j = 0; j <= NFSD_SUPPORTED_MINOR_VERSION; j++) {
1898 struct nlattr *attr;
1899
1900 /* Don't record any versions the kernel doesn't have
1901 * compiled in
1902 */
1903 if (!nfsd_support_version(i))
1904 continue;
1905
1906 /* NFSv{2,3} does not support minor numbers */
1907 if (i < 4 && j)
1908 continue;
1909
1910 attr = nla_nest_start(skb,
1911 NFSD_A_SERVER_PROTO_VERSION);
1912 if (!attr) {
1913 err = -EINVAL;
1914 goto err_nfsd_unlock;
1915 }
1916
1917 if (nla_put_u32(skb, NFSD_A_VERSION_MAJOR, i) ||
1918 nla_put_u32(skb, NFSD_A_VERSION_MINOR, j)) {
1919 err = -EINVAL;
1920 goto err_nfsd_unlock;
1921 }
1922
1923 /* Set the enabled flag if the version is enabled */
1924 if (nfsd_vers(nn, i, NFSD_TEST) &&
1925 (i < 4 || nfsd_minorversion(nn, j, NFSD_TEST)) &&
1926 nla_put_flag(skb, NFSD_A_VERSION_ENABLED)) {
1927 err = -EINVAL;
1928 goto err_nfsd_unlock;
1929 }
1930
1931 nla_nest_end(skb, attr);
1932 }
1933 }
1934
1935 mutex_unlock(&nn->nfsd_info.mutex);
1936 genlmsg_end(skb, hdr);
1937
1938 return genlmsg_reply(skb, info);
1939
1940 err_nfsd_unlock:
1941 mutex_unlock(&nn->nfsd_info.mutex);
1942 err_free_msg:
1943 nlmsg_free(skb);
1944
1945 return err;
1946 }
1947
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] nfsd: use kref and new mutex for global config management
2025-06-18 21:31 ` [PATCH 2/3] nfsd: use kref and new mutex for global config management NeilBrown
@ 2025-06-19 14:06 ` Chuck Lever
2025-06-20 13:01 ` Jeff Layton
1 sibling, 0 replies; 11+ messages in thread
From: Chuck Lever @ 2025-06-19 14:06 UTC (permalink / raw)
To: NeilBrown, Jeff Layton
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng,
chenxiaosong
On 6/18/25 5:31 PM, NeilBrown wrote:
> nfsd_mutex is used for two quite different things:
> 1/ it prevents races when start/stoping global resources:
> the filecache and the v4 state table
> 2/ it prevents races for per-netns config, typically
> ensure config changes are synchronised w.r.t. server
> startup/shutdown.
>
> This patch splits out the first used. A subsequent patch improves the
> second.
>
> "nfsd_users" is changed to a kref which is can be taken to delay
> the shutdown of global services. nfsd_startup_get(), it is succeeds,
> ensure the global services will remain until nfsd_startup_put().
>
> The new mutex, nfsd_startup_mutex, is only take for startup and
> shutdown. It is not needed to protect the kref.
>
> The locking needed by nfsd_file_cache_purge() is now provided internally
> by that function so calls don't need to be concerned.
>
> This replaces NFSD_FILE_CACHE_UP which is effective just a flag which
> says nfsd_users is non-zero.
>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> fs/nfsd/export.c | 6 ------
> fs/nfsd/filecache.c | 31 +++++++++++--------------------
> fs/nfsd/nfsd.h | 3 +++
> fs/nfsd/nfssvc.c | 41 +++++++++++++++++++++++++++--------------
> 4 files changed, 41 insertions(+), 40 deletions(-)
>
> diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> index cadfc2bae60e..1ea3d72ef5c9 100644
> --- a/fs/nfsd/export.c
> +++ b/fs/nfsd/export.c
> @@ -243,13 +243,7 @@ static struct cache_head *expkey_alloc(void)
>
> static void expkey_flush(void)
> {
> - /*
> - * Take the nfsd_mutex here to ensure that the file cache is not
> - * destroyed while we're in the middle of flushing.
> - */
> - mutex_lock(&nfsd_mutex);
> nfsd_file_cache_purge(current->nsproxy->net_ns);
> - mutex_unlock(&nfsd_mutex);
> }
>
> static const struct cache_detail svc_expkey_cache_template = {
> diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
> index e108b6c705b4..0a9116b7530c 100644
> --- a/fs/nfsd/filecache.c
> +++ b/fs/nfsd/filecache.c
> @@ -50,8 +50,6 @@
>
> #define NFSD_LAUNDRETTE_DELAY (2 * HZ)
>
> -#define NFSD_FILE_CACHE_UP (0)
> -
> /* We only care about NFSD_MAY_READ/WRITE for this cache */
> #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
>
> @@ -70,7 +68,6 @@ struct nfsd_fcache_disposal {
> static struct kmem_cache *nfsd_file_slab;
> static struct kmem_cache *nfsd_file_mark_slab;
> static struct list_lru nfsd_file_lru;
> -static unsigned long nfsd_file_flags;
> static struct fsnotify_group *nfsd_file_fsnotify_group;
> static struct delayed_work nfsd_filecache_laundrette;
> static struct rhltable nfsd_file_rhltable
> @@ -112,9 +109,12 @@ static const struct rhashtable_params nfsd_file_rhash_params = {
> static void
> nfsd_file_schedule_laundrette(void)
> {
> - if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
> - queue_delayed_work(system_unbound_wq, &nfsd_filecache_laundrette,
> + if (nfsd_startup_get()) {
> + queue_delayed_work(system_unbound_wq,
> + &nfsd_filecache_laundrette,
> NFSD_LAUNDRETTE_DELAY);
> + nfsd_startup_put();
> + }
> }
>
> static void
> @@ -795,10 +795,6 @@ nfsd_file_cache_init(void)
> {
> int ret;
>
> - lockdep_assert_held(&nfsd_mutex);
> - if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
> - return 0;
> -
> ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params);
> if (ret)
> goto out;
> @@ -853,8 +849,6 @@ nfsd_file_cache_init(void)
>
> INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
> out:
> - if (ret)
> - clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
> return ret;
> out_notifier:
> lease_unregister_notifier(&nfsd_file_lease_notifier);
> @@ -958,9 +952,10 @@ nfsd_file_cache_start_net(struct net *net)
> void
> nfsd_file_cache_purge(struct net *net)
> {
> - lockdep_assert_held(&nfsd_mutex);
> - if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
> + if (nfsd_startup_get()) {
> __nfsd_file_cache_purge(net);
> + nfsd_startup_put();
> + }
> }
>
> void
> @@ -975,10 +970,6 @@ nfsd_file_cache_shutdown(void)
> {
> int i;
>
> - lockdep_assert_held(&nfsd_mutex);
> - if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
> - return;
> -
> lease_unregister_notifier(&nfsd_file_lease_notifier);
> shrinker_free(nfsd_file_shrinker);
> /*
> @@ -1347,8 +1338,7 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
> unsigned long lru = 0, total_age = 0;
>
> /* Serialize with server shutdown */
> - mutex_lock(&nfsd_mutex);
> - if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
> + if (nfsd_startup_get()) {
> struct bucket_table *tbl;
> struct rhashtable *ht;
>
> @@ -1360,8 +1350,9 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
> tbl = rht_dereference_rcu(ht->tbl, ht);
> buckets = tbl->size;
> rcu_read_unlock();
> +
> + nfsd_startup_put();
> }
> - mutex_unlock(&nfsd_mutex);
>
> for_each_possible_cpu(i) {
> hits += per_cpu(nfsd_file_cache_hits, i);
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index 1bfd0b4e9af7..8ad9fcc23789 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -80,6 +80,9 @@ extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
> extern struct mutex nfsd_mutex;
> extern atomic_t nfsd_th_cnt; /* number of available threads */
>
> +bool nfsd_startup_get(void);
> +void nfsd_startup_put(void);
> +
> extern const struct seq_operations nfs_exports_op;
>
> /*
> diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
> index 82b0111ac469..b2080e5a71e6 100644
> --- a/fs/nfsd/nfssvc.c
> +++ b/fs/nfsd/nfssvc.c
> @@ -270,38 +270,51 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
> return 0;
> }
>
> -static int nfsd_users = 0;
> +static struct kref nfsd_users = KREF_INIT(0);
> +static DEFINE_MUTEX(nfsd_startup_mutex);
>
> static int nfsd_startup_generic(void)
> {
> - int ret;
> + int ret = 0;
>
> - if (nfsd_users++)
> + if (kref_get_unless_zero(&nfsd_users))
> return 0;
> + mutex_lock(&nfsd_startup_mutex);
> + if (kref_get_unless_zero(&nfsd_users))
> + goto out_unlock;
>
> ret = nfsd_file_cache_init();
> if (ret)
> - goto dec_users;
> + goto out_unlock;
>
> ret = nfs4_state_start();
> if (ret)
> goto out_file_cache;
> - return 0;
> + kref_init(&nfsd_users);
> +out_unlock:
> + mutex_unlock(&nfsd_startup_mutex);
> + return ret;
>
> out_file_cache:
> nfsd_file_cache_shutdown();
> -dec_users:
> - nfsd_users--;
> - return ret;
> + goto out_unlock;
> }
>
> -static void nfsd_shutdown_generic(void)
> +static void nfsd_shutdown_cb(struct kref *kref)
> {
> - if (--nfsd_users)
> - return;
> -
> nfs4_state_shutdown();
> nfsd_file_cache_shutdown();
> + mutex_unlock(&nfsd_startup_mutex);
> +}
> +
> +bool nfsd_startup_get(void)
> +{
> + return kref_get_unless_zero(&nfsd_users);
> +}
> +
> +void nfsd_startup_put(void)
> +{
> + kref_put_mutex(&nfsd_users, nfsd_shutdown_cb, &nfsd_startup_mutex);
> }
>
> static bool nfsd_needs_lockd(struct nfsd_net *nn)
> @@ -416,7 +429,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
> nn->lockd_up = false;
> }
> out_socks:
> - nfsd_shutdown_generic();
> + nfsd_startup_put();
> return ret;
> }
>
> @@ -443,7 +456,7 @@ static void nfsd_shutdown_net(struct net *net)
> percpu_ref_exit(&nn->nfsd_net_ref);
>
> nn->nfsd_net_up = false;
> - nfsd_shutdown_generic();
> + nfsd_startup_put();
> }
>
> static DEFINE_SPINLOCK(nfsd_notifier_lock);
[ Adding Cc: chenxiaosong@chenxiaosong.com who seems to be working on a
crasher related to nfsd_users. ]
I like where this is going.
- I'm struggling a little with the names of nfsd_startup_get/put. But I
don't have a better suggestion. I think we're marking a critical
section of sorts. pin/unpin maybe?
- Maybe someone has a way to exercise NFSD service startup and shutdown.
We don't have much testing in this area, upstream. A CI-style "do this
thing 1000 times while the server is under load" might be good.
--
Chuck Lever
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] nfsd: provide proper locking for all write_ function
2025-06-18 21:31 ` [PATCH 1/3] nfsd: provide proper locking for all write_ function NeilBrown
@ 2025-06-20 12:59 ` Jeff Layton
0 siblings, 0 replies; 11+ messages in thread
From: Jeff Layton @ 2025-06-20 12:59 UTC (permalink / raw)
To: NeilBrown, Chuck Lever
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
On Thu, 2025-06-19 at 07:31 +1000, NeilBrown wrote:
> write_foo functions are called to handle IO to files in /proc/fs/nfsd/.
> The can be called at any time and so generally need locking to ensure
> they don't happen at an awkward time.
>
> Many already take nfsd_mutex and check if nfsd_serv has been set. This
> ensures they only run when the server is fully configured.
>
> write_filehandle() does *not* need locking. It interacts with the
> export table which is set up when the netns is set up, so it is always
> valid and it has its own locking. write_filehandle() is needed before
> the nfs server is started so checking nfsd_serv would be wrong.
>
> The remaining files which do not have any locking are
> write_v4_end_grace(), write_unlock_ip(), and write_unlock_fs().
> None of these make sense when the nfs server is not running and there is
> evidence that write_v4_end_grace() can race with ->client_tracking_op
> setup/shutdown and cause problems.
>
> This patch adds locking to these three and ensures the "unlock"
> functions abort if ->nfsd_serv is not set.
>
> Reported-by: Li Lingfeng <lilingfeng3@huawei.com>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> fs/nfsd/nfsctl.c | 115 +++++++++++++++++++++++++++++++----------------
> 1 file changed, 77 insertions(+), 38 deletions(-)
>
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index 3f3e9f6c4250..3710a1992d17 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -200,27 +200,18 @@ static inline struct net *netns(struct file *file)
> return file_inode(file)->i_sb->s_fs_info;
> }
>
> -/*
> - * write_unlock_ip - Release all locks used by a client
> - *
> - * Experimental.
> - *
> - * Input:
> - * buf: '\n'-terminated C string containing a
> - * presentation format IP address
> - * size: length of C string in @buf
> - * Output:
> - * On success: returns zero if all specified locks were released;
> - * returns one if one or more locks were not released
> - * On error: return code is negative errno value
> - */
> -static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
> +static ssize_t __write_unlock_ip(struct file *file, char *buf, size_t size)
> {
> struct sockaddr_storage address;
> struct sockaddr *sap = (struct sockaddr *)&address;
> size_t salen = sizeof(address);
> char *fo_path;
> struct net *net = netns(file);
> + struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> +
> + if (!nn->nfsd_serv)
> + /* There cannot be any files to unlock */
> + return -EINVAL;
>
> /* sanity check */
> if (size == 0)
> @@ -241,24 +232,39 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
> }
>
> /*
> - * write_unlock_fs - Release all locks on a local file system
> + * write_unlock_ip - Release all locks used by a client
> *
> * Experimental.
> *
> * Input:
> - * buf: '\n'-terminated C string containing the
> - * absolute pathname of a local file system
> + * buf: '\n'-terminated C string containing a
> + * presentation format IP address
> * size: length of C string in @buf
> * Output:
> * On success: returns zero if all specified locks were released;
> * returns one if one or more locks were not released
> * On error: return code is negative errno value
> */
> -static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
> +static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
> +{
> + ssize_t rv;
> +
> + mutex_lock(&nfsd_mutex);
> + rv = __write_unlock_ip(file, buf, size);
> + mutex_unlock(&nfsd_mutex);
> + return rv;
> +}
> +
> +static ssize_t __write_unlock_fs(struct file *file, char *buf, size_t size)
> {
> struct path path;
> char *fo_path;
> int error;
> + struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
> +
> + if (!nn->nfsd_serv)
> + /* There cannot be any files to unlock */
> + return -EINVAL;
>
> /* sanity check */
> if (size == 0)
> @@ -291,6 +297,30 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
> return error;
> }
>
> +/*
> + * write_unlock_fs - Release all locks on a local file system
> + *
> + * Experimental.
> + *
> + * Input:
> + * buf: '\n'-terminated C string containing the
> + * absolute pathname of a local file system
> + * size: length of C string in @buf
> + * Output:
> + * On success: returns zero if all specified locks were released;
> + * returns one if one or more locks were not released
> + * On error: return code is negative errno value
> + */
> +static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
> +{
> + ssize_t rv;
> +
> + mutex_lock(&nfsd_mutex);
> + rv = __write_unlock_fs(file, buf, size);
> + mutex_unlock(&nfsd_mutex);
> + return rv;
> +}
> +
> /*
> * write_filehandle - Get a variable-length NFS file handle by path
> *
> @@ -1053,6 +1083,29 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
> }
> #endif
>
> +static ssize_t __write_v4_end_grace(struct file *file, char *buf, size_t size)
> +{
> + struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
> +
> + if (size > 0) {
> + switch(buf[0]) {
> + case 'Y':
> + case 'y':
> + case '1':
> + if (!nn->nfsd_serv)
> + return -EBUSY;
> + trace_nfsd_end_grace(netns(file));
> + nfsd4_end_grace(nn);
> + break;
> + default:
> + return -EINVAL;
> + }
> + }
> +
> + return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n",
> + nn->grace_ended ? 'Y' : 'N');
> +}
> +
> /*
> * write_v4_end_grace - release grace period for nfsd's v4.x lock manager
> *
> @@ -1075,27 +1128,13 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
> */
> static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
> {
> - struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
> -
> - if (size > 0) {
> - switch(buf[0]) {
> - case 'Y':
> - case 'y':
> - case '1':
> - if (!nn->nfsd_serv)
> - return -EBUSY;
> - trace_nfsd_end_grace(netns(file));
> - nfsd4_end_grace(nn);
> - break;
> - default:
> - return -EINVAL;
> - }
> - }
> + ssize_t rv;
>
> - return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n",
> - nn->grace_ended ? 'Y' : 'N');
> + mutex_lock(&nfsd_mutex);
> + rv = __write_v4_end_grace(file, buf, size);
> + mutex_unlock(&nfsd_mutex);
> + return rv;
> }
> -
> #endif
>
> /*----------------------------------------------------------------------------*/
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] nfsd: use kref and new mutex for global config management
2025-06-18 21:31 ` [PATCH 2/3] nfsd: use kref and new mutex for global config management NeilBrown
2025-06-19 14:06 ` Chuck Lever
@ 2025-06-20 13:01 ` Jeff Layton
1 sibling, 0 replies; 11+ messages in thread
From: Jeff Layton @ 2025-06-20 13:01 UTC (permalink / raw)
To: NeilBrown, Chuck Lever
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
On Thu, 2025-06-19 at 07:31 +1000, NeilBrown wrote:
> nfsd_mutex is used for two quite different things:
> 1/ it prevents races when start/stoping global resources:
> the filecache and the v4 state table
> 2/ it prevents races for per-netns config, typically
> ensure config changes are synchronised w.r.t. server
> startup/shutdown.
>
> This patch splits out the first used. A subsequent patch improves the
> second.
>
> "nfsd_users" is changed to a kref which is can be taken to delay
Changelog nits:
s/which is/which/
> the shutdown of global services. nfsd_startup_get(), it is succeeds,
nit: "if it succeeds"
> ensure the global services will remain until nfsd_startup_put().
"ensures"
>
> The new mutex, nfsd_startup_mutex, is only take for startup and
"only taken"
> shutdown. It is not needed to protect the kref.
>
> The locking needed by nfsd_file_cache_purge() is now provided internally
> by that function so calls don't need to be concerned.
>
> This replaces NFSD_FILE_CACHE_UP which is effective just a flag which
"effectively"
> says nfsd_users is non-zero.
>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> fs/nfsd/export.c | 6 ------
> fs/nfsd/filecache.c | 31 +++++++++++--------------------
> fs/nfsd/nfsd.h | 3 +++
> fs/nfsd/nfssvc.c | 41 +++++++++++++++++++++++++++--------------
> 4 files changed, 41 insertions(+), 40 deletions(-)
>
> diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> index cadfc2bae60e..1ea3d72ef5c9 100644
> --- a/fs/nfsd/export.c
> +++ b/fs/nfsd/export.c
> @@ -243,13 +243,7 @@ static struct cache_head *expkey_alloc(void)
>
> static void expkey_flush(void)
> {
> - /*
> - * Take the nfsd_mutex here to ensure that the file cache is not
> - * destroyed while we're in the middle of flushing.
> - */
> - mutex_lock(&nfsd_mutex);
> nfsd_file_cache_purge(current->nsproxy->net_ns);
> - mutex_unlock(&nfsd_mutex);
> }
>
> static const struct cache_detail svc_expkey_cache_template = {
> diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
> index e108b6c705b4..0a9116b7530c 100644
> --- a/fs/nfsd/filecache.c
> +++ b/fs/nfsd/filecache.c
> @@ -50,8 +50,6 @@
>
> #define NFSD_LAUNDRETTE_DELAY (2 * HZ)
>
> -#define NFSD_FILE_CACHE_UP (0)
> -
> /* We only care about NFSD_MAY_READ/WRITE for this cache */
> #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
>
> @@ -70,7 +68,6 @@ struct nfsd_fcache_disposal {
> static struct kmem_cache *nfsd_file_slab;
> static struct kmem_cache *nfsd_file_mark_slab;
> static struct list_lru nfsd_file_lru;
> -static unsigned long nfsd_file_flags;
> static struct fsnotify_group *nfsd_file_fsnotify_group;
> static struct delayed_work nfsd_filecache_laundrette;
> static struct rhltable nfsd_file_rhltable
> @@ -112,9 +109,12 @@ static const struct rhashtable_params nfsd_file_rhash_params = {
> static void
> nfsd_file_schedule_laundrette(void)
> {
> - if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
> - queue_delayed_work(system_unbound_wq, &nfsd_filecache_laundrette,
> + if (nfsd_startup_get()) {
> + queue_delayed_work(system_unbound_wq,
> + &nfsd_filecache_laundrette,
> NFSD_LAUNDRETTE_DELAY);
> + nfsd_startup_put();
> + }
> }
>
> static void
> @@ -795,10 +795,6 @@ nfsd_file_cache_init(void)
> {
> int ret;
>
> - lockdep_assert_held(&nfsd_mutex);
> - if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
> - return 0;
> -
> ret = rhltable_init(&nfsd_file_rhltable, &nfsd_file_rhash_params);
> if (ret)
> goto out;
> @@ -853,8 +849,6 @@ nfsd_file_cache_init(void)
>
> INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
> out:
> - if (ret)
> - clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
> return ret;
> out_notifier:
> lease_unregister_notifier(&nfsd_file_lease_notifier);
> @@ -958,9 +952,10 @@ nfsd_file_cache_start_net(struct net *net)
> void
> nfsd_file_cache_purge(struct net *net)
> {
> - lockdep_assert_held(&nfsd_mutex);
> - if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
> + if (nfsd_startup_get()) {
> __nfsd_file_cache_purge(net);
> + nfsd_startup_put();
> + }
> }
>
> void
> @@ -975,10 +970,6 @@ nfsd_file_cache_shutdown(void)
> {
> int i;
>
> - lockdep_assert_held(&nfsd_mutex);
> - if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
> - return;
> -
> lease_unregister_notifier(&nfsd_file_lease_notifier);
> shrinker_free(nfsd_file_shrinker);
> /*
> @@ -1347,8 +1338,7 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
> unsigned long lru = 0, total_age = 0;
>
> /* Serialize with server shutdown */
> - mutex_lock(&nfsd_mutex);
> - if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
> + if (nfsd_startup_get()) {
> struct bucket_table *tbl;
> struct rhashtable *ht;
>
> @@ -1360,8 +1350,9 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
> tbl = rht_dereference_rcu(ht->tbl, ht);
> buckets = tbl->size;
> rcu_read_unlock();
> +
> + nfsd_startup_put();
> }
> - mutex_unlock(&nfsd_mutex);
>
> for_each_possible_cpu(i) {
> hits += per_cpu(nfsd_file_cache_hits, i);
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index 1bfd0b4e9af7..8ad9fcc23789 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -80,6 +80,9 @@ extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
> extern struct mutex nfsd_mutex;
> extern atomic_t nfsd_th_cnt; /* number of available threads */
>
> +bool nfsd_startup_get(void);
> +void nfsd_startup_put(void);
> +
> extern const struct seq_operations nfs_exports_op;
>
> /*
> diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
> index 82b0111ac469..b2080e5a71e6 100644
> --- a/fs/nfsd/nfssvc.c
> +++ b/fs/nfsd/nfssvc.c
> @@ -270,38 +270,51 @@ static int nfsd_init_socks(struct net *net, const struct cred *cred)
> return 0;
> }
>
> -static int nfsd_users = 0;
> +static struct kref nfsd_users = KREF_INIT(0);
> +static DEFINE_MUTEX(nfsd_startup_mutex);
>
> static int nfsd_startup_generic(void)
> {
> - int ret;
> + int ret = 0;
>
> - if (nfsd_users++)
> + if (kref_get_unless_zero(&nfsd_users))
> return 0;
> + mutex_lock(&nfsd_startup_mutex);
> + if (kref_get_unless_zero(&nfsd_users))
> + goto out_unlock;
>
> ret = nfsd_file_cache_init();
> if (ret)
> - goto dec_users;
> + goto out_unlock;
>
> ret = nfs4_state_start();
> if (ret)
> goto out_file_cache;
> - return 0;
> + kref_init(&nfsd_users);
> +out_unlock:
> + mutex_unlock(&nfsd_startup_mutex);
> + return ret;
>
> out_file_cache:
> nfsd_file_cache_shutdown();
> -dec_users:
> - nfsd_users--;
> - return ret;
> + goto out_unlock;
> }
>
> -static void nfsd_shutdown_generic(void)
> +static void nfsd_shutdown_cb(struct kref *kref)
> {
> - if (--nfsd_users)
> - return;
> -
> nfs4_state_shutdown();
> nfsd_file_cache_shutdown();
> + mutex_unlock(&nfsd_startup_mutex);
> +}
> +
> +bool nfsd_startup_get(void)
> +{
> + return kref_get_unless_zero(&nfsd_users);
> +}
> +
> +void nfsd_startup_put(void)
> +{
> + kref_put_mutex(&nfsd_users, nfsd_shutdown_cb, &nfsd_startup_mutex);
> }
>
> static bool nfsd_needs_lockd(struct nfsd_net *nn)
> @@ -416,7 +429,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred)
> nn->lockd_up = false;
> }
> out_socks:
> - nfsd_shutdown_generic();
> + nfsd_startup_put();
> return ret;
> }
>
> @@ -443,7 +456,7 @@ static void nfsd_shutdown_net(struct net *net)
> percpu_ref_exit(&nn->nfsd_net_ref);
>
> nn->nfsd_net_up = false;
> - nfsd_shutdown_generic();
> + nfsd_startup_put();
> }
>
> static DEFINE_SPINLOCK(nfsd_notifier_lock);
I like this approach though. Taking a reference seems much less brittle
than dealing with a global mutex. Other than the changelog nits:
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
2025-06-18 21:31 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
2025-06-19 12:33 ` kernel test robot
@ 2025-06-20 13:13 ` Jeff Layton
1 sibling, 0 replies; 11+ messages in thread
From: Jeff Layton @ 2025-06-20 13:13 UTC (permalink / raw)
To: NeilBrown, Chuck Lever
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
On Thu, 2025-06-19 at 07:31 +1000, NeilBrown wrote:
> The remaining uses for nfsd_mutex are all to protect per-netns
> resources.
>
> This patch replaces the global mutex with one per netns. The "svc_info"
> struct now contains that mutex rather than a pointer to the mutex.
>
> Macros are provided to make it easy to take the mutex given a file or net.
>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> .../admin-guide/nfs/nfsd-admin-interfaces.rst | 2 +-
> fs/nfsd/nfsctl.c | 113 +++++++++---------
> fs/nfsd/nfsd.h | 1 -
> fs/nfsd/nfssvc.c | 33 ++---
> include/linux/sunrpc/svc.h | 2 +-
> net/sunrpc/svc_xprt.c | 4 +-
> 6 files changed, 72 insertions(+), 83 deletions(-)
>
> diff --git a/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst b/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
> index c05926f79054..9548e4ab35b6 100644
> --- a/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
> +++ b/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
> @@ -37,4 +37,4 @@ Implementation notes
>
> Note that the rpc server requires the caller to serialize addition and
> removal of listening sockets, and startup and shutdown of the server.
> -For nfsd this is done using nfsd_mutex.
> +For nfsd this is done using nfsd_info.mutex in struct nfsd_net.
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index 3710a1992d17..70eddf2640f0 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -95,6 +95,13 @@ static ssize_t (*const write_op[])(struct file *, char *, size_t) = {
> #endif
> };
>
> +#define with_nfsd_net_locked(__net) \
> + for (struct nfsd_net *__nn = net_generic(__net, nfsd_net_id); \
> + __nn ? ({mutex_lock(&__nn->nfsd_info.mutex); 1; }) : 0; \
> + ({mutex_unlock(&__nn->nfsd_info.mutex); __nn = NULL;}))
> +#define with_nfsd_file_locked(__file) \
> + with_nfsd_net_locked(netns(__file))
> +
This is certainly clever, but I think I'd rather have simple
nfsd_net_mutex_lock/_unlock() functions than have to maintain this sort
of macro.
> static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
> {
> ino_t ino = file_inode(file)->i_ino;
> @@ -249,9 +256,8 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
> {
> ssize_t rv;
>
> - mutex_lock(&nfsd_mutex);
> - rv = __write_unlock_ip(file, buf, size);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __write_unlock_ip(file, buf, size);
> return rv;
> }
>
> @@ -315,9 +321,8 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
> {
> ssize_t rv;
>
> - mutex_lock(&nfsd_mutex);
> - rv = __write_unlock_fs(file, buf, size);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __write_unlock_fs(file, buf, size);
> return rv;
> }
>
> @@ -440,9 +445,8 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
> if (newthreads < 0)
> return -EINVAL;
> trace_nfsd_ctl_threads(net, newthreads);
> - mutex_lock(&nfsd_mutex);
> - rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_net_locked(net)
> + rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
> if (rv < 0)
> return rv;
> } else
> @@ -473,7 +477,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
> * return code is the size in bytes of the string
> * On error: return code is zero or a negative errno value
> */
> -static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
> +static ssize_t __write_pool_threads(struct file *file, char *buf, size_t size)
> {
> /* if size > 0, look for an array of number of threads per node
> * and apply them then write out number of threads per node as reply
> @@ -486,7 +490,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
> int *nthreads;
> struct net *net = netns(file);
>
> - mutex_lock(&nfsd_mutex);
> npools = nfsd_nrpools(net);
> if (npools == 0) {
> /*
> @@ -494,7 +497,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
> * writing to the threads file but NOT the pool_threads
> * file, sorry. Report zero threads.
> */
> - mutex_unlock(&nfsd_mutex);
> strcpy(buf, "0\n");
> return strlen(buf);
> }
> @@ -544,10 +546,18 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
> rv = mesg - buf;
> out_free:
> kfree(nthreads);
> - mutex_unlock(&nfsd_mutex);
> return rv;
> }
>
> +static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
> +{
> + ssize_t ret;
> +
> + with_nfsd_file_locked(file)
> + ret = __write_pool_threads(file, buf, size);
> + return ret;
> +}
> +
> static ssize_t
> nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining,
> const char *sep, unsigned vers, int minor)
> @@ -709,9 +719,9 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
> {
> ssize_t rv;
>
> - mutex_lock(&nfsd_mutex);
> - rv = __write_versions(file, buf, size);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __write_versions(file, buf, size);
> +
> return rv;
> }
>
> @@ -868,9 +878,8 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
> {
> ssize_t rv;
>
> - mutex_lock(&nfsd_mutex);
> - rv = __write_ports(file, buf, size, netns(file));
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __write_ports(file, buf, size, netns(file));
> return rv;
> }
>
> @@ -916,13 +925,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
> bsize = max_t(int, bsize, 1024);
> bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE);
> bsize &= ~(1024-1);
> - mutex_lock(&nfsd_mutex);
> + mutex_lock(&nn->nfsd_info.mutex);
> if (nn->nfsd_serv) {
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> return -EBUSY;
> }
> nfsd_max_blksize = bsize;
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> }
>
> return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
> @@ -971,9 +980,8 @@ static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
> {
> ssize_t rv;
>
> - mutex_lock(&nfsd_mutex);
> - rv = __nfsd4_write_time(file, buf, size, time, nn);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __nfsd4_write_time(file, buf, size, time, nn);
> return rv;
> }
>
> @@ -1076,9 +1084,8 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
> ssize_t rv;
> struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
>
> - mutex_lock(&nfsd_mutex);
> - rv = __write_recoverydir(file, buf, size, nn);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __write_recoverydir(file, buf, size, nn);
> return rv;
> }
> #endif
> @@ -1130,9 +1137,8 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
> {
> ssize_t rv;
>
> - mutex_lock(&nfsd_mutex);
> - rv = __write_v4_end_grace(file, buf, size);
> - mutex_unlock(&nfsd_mutex);
> + with_nfsd_file_locked(file)
> + rv = __write_v4_end_grace(file, buf, size);
> return rv;
> }
> #endif
> @@ -1552,9 +1558,8 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
> int i, ret, rqstp_index = 0;
> struct nfsd_net *nn;
>
> - mutex_lock(&nfsd_mutex);
> -
> nn = net_generic(sock_net(skb->sk), nfsd_net_id);
> + mutex_lock(&nn->nfsd_info.mutex);
> if (!nn->nfsd_serv) {
> ret = -ENODEV;
> goto out_unlock;
> @@ -1636,7 +1641,7 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
> out:
> rcu_read_unlock();
> out_unlock:
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
>
> return ret;
> }
> @@ -1665,7 +1670,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> count++;
> }
>
> - mutex_lock(&nfsd_mutex);
> + mutex_lock(&nn->nfsd_info.mutex);
>
> nrpools = max(count, nfsd_nrpools(net));
> nthreads = kcalloc(nrpools, sizeof(int), GFP_KERNEL);
> @@ -1720,7 +1725,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> if (ret > 0)
> ret = 0;
> out_unlock:
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> kfree(nthreads);
> return ret;
> }
> @@ -1749,7 +1754,7 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> goto err_free_msg;
> }
>
> - mutex_lock(&nfsd_mutex);
> + mutex_lock(&nn->nfsd_info.mutex);
>
> err = nla_put_u32(skb, NFSD_A_SERVER_GRACETIME,
> nn->nfsd4_grace) ||
> @@ -1777,14 +1782,14 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> goto err_unlock;
> }
>
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
>
> genlmsg_end(skb, hdr);
>
> return genlmsg_reply(skb, info);
>
> err_unlock:
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> err_free_msg:
> nlmsg_free(skb);
>
> @@ -1807,11 +1812,10 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
> if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION))
> return -EINVAL;
>
> - mutex_lock(&nfsd_mutex);
> -
> nn = net_generic(genl_info_net(info), nfsd_net_id);
> + mutex_lock(&nn->nfsd_info.mutex);
> if (nn->nfsd_serv) {
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> return -EBUSY;
> }
>
> @@ -1856,7 +1860,7 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
> }
> }
>
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
>
> return 0;
> }
> @@ -1884,7 +1888,7 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
> goto err_free_msg;
> }
>
> - mutex_lock(&nfsd_mutex);
> + mutex_lock(&nn->nfsd_info.mutex);
> nn = net_generic(genl_info_net(info), nfsd_net_id);
>
> for (i = 2; i <= 4; i++) {
> @@ -1928,13 +1932,13 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
> }
> }
>
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> genlmsg_end(skb, hdr);
>
> return genlmsg_reply(skb, info);
>
> err_nfsd_unlock:
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> err_free_msg:
> nlmsg_free(skb);
>
> @@ -1959,15 +1963,16 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
> bool delete = false;
> int err, rem;
>
> - mutex_lock(&nfsd_mutex);
> + nn = net_generic(net, nfsd_net_id);
> +
> + mutex_lock(&nn->nfsd_info.mutex);
>
> err = nfsd_create_serv(net);
> if (err) {
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> return err;
> }
>
> - nn = net_generic(net, nfsd_net_id);
> serv = nn->nfsd_serv;
>
> spin_lock_bh(&serv->sv_lock);
> @@ -2083,7 +2088,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
> nfsd_destroy_serv(net);
>
> out_unlock_mtx:
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
>
> return err;
> }
> @@ -2113,8 +2118,8 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
> goto err_free_msg;
> }
>
> - mutex_lock(&nfsd_mutex);
> nn = net_generic(genl_info_net(info), nfsd_net_id);
> + mutex_lock(&nn->nfsd_info.mutex);
>
> /* no nfs server? Just send empty socket list */
> if (!nn->nfsd_serv)
> @@ -2144,14 +2149,14 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
> }
> spin_unlock_bh(&serv->sv_lock);
> out_unlock_mtx:
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> genlmsg_end(skb, hdr);
>
> return genlmsg_reply(skb, info);
>
> err_serv_unlock:
> spin_unlock_bh(&serv->sv_lock);
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> err_free_msg:
> nlmsg_free(skb);
>
> @@ -2253,7 +2258,7 @@ static __net_init int nfsd_net_init(struct net *net)
> nn->nfsd_versions[i] = nfsd_support_version(i);
> for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++)
> nn->nfsd4_minorversions[i] = nfsd_support_version(4);
> - nn->nfsd_info.mutex = &nfsd_mutex;
> + mutex_init(&nn->nfsd_info.mutex);
> nn->nfsd_serv = NULL;
> nfsd4_init_leases_net(nn);
> get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index 8ad9fcc23789..3cbca4d34f48 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -77,7 +77,6 @@ struct nfsd_genl_rqstp {
>
> extern struct svc_program nfsd_programs[];
> extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
> -extern struct mutex nfsd_mutex;
> extern atomic_t nfsd_th_cnt; /* number of available threads */
>
> bool nfsd_startup_get(void);
> diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
> index b2080e5a71e6..9f70b1fbc55e 100644
> --- a/fs/nfsd/nfssvc.c
> +++ b/fs/nfsd/nfssvc.c
> @@ -56,20 +56,6 @@ static __be32 nfsd_init_request(struct svc_rqst *,
> const struct svc_program *,
> struct svc_process_info *);
>
> -/*
> - * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
> - * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
> - *
> - * Finally, the nfsd_mutex also protects some of the global variables that are
> - * accessed when nfsd starts and that are settable via the write_* routines in
> - * nfsctl.c. In particular:
> - *
> - * user_recovery_dirname
> - * user_lease_time
> - * nfsd_versions
> - */
> -DEFINE_MUTEX(nfsd_mutex);
> -
> #if IS_ENABLED(CONFIG_NFS_LOCALIO)
> static const struct svc_version *localio_versions[] = {
> [1] = &localio_version1,
> @@ -242,10 +228,10 @@ int nfsd_nrthreads(struct net *net)
> int rv = 0;
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
>
> - mutex_lock(&nfsd_mutex);
> + mutex_lock(&nn->nfsd_info.mutex);
> if (nn->nfsd_serv)
> rv = nn->nfsd_serv->sv_nrthreads;
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> return rv;
> }
>
> @@ -522,7 +508,6 @@ static struct notifier_block nfsd_inet6addr_notifier = {
> };
> #endif
>
> -/* Only used under nfsd_mutex, so this atomic may be overkill: */
> static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
>
> /**
> @@ -534,7 +519,7 @@ void nfsd_destroy_serv(struct net *net)
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> struct svc_serv *serv = nn->nfsd_serv;
>
> - lockdep_assert_held(&nfsd_mutex);
> + lockdep_assert_held(&nn->nfsd_info.mutex);
>
> spin_lock(&nfsd_notifier_lock);
> nn->nfsd_serv = NULL;
> @@ -606,17 +591,17 @@ void nfsd_shutdown_threads(struct net *net)
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> struct svc_serv *serv;
>
> - mutex_lock(&nfsd_mutex);
> + mutex_lock(&nn->nfsd_info.mutex);
> serv = nn->nfsd_serv;
> if (serv == NULL) {
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> return;
> }
>
> /* Kill outstanding nfsd threads */
> svc_set_num_threads(serv, NULL, 0);
> nfsd_destroy_serv(net);
> - mutex_unlock(&nfsd_mutex);
> + mutex_unlock(&nn->nfsd_info.mutex);
> }
>
> struct svc_rqst *nfsd_current_rqst(void)
> @@ -632,7 +617,7 @@ int nfsd_create_serv(struct net *net)
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> struct svc_serv *serv;
>
> - WARN_ON(!mutex_is_locked(&nfsd_mutex));
> + WARN_ON(!mutex_is_locked(&nn->nfsd_info.mutex));
> if (nn->nfsd_serv)
> return 0;
>
> @@ -714,7 +699,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
> int err = 0;
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
>
> - lockdep_assert_held(&nfsd_mutex);
> + lockdep_assert_held(&nn->nfsd_info.mutex);
>
> if (nn->nfsd_serv == NULL || n <= 0)
> return 0;
> @@ -787,7 +772,7 @@ nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const c
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> struct svc_serv *serv;
>
> - lockdep_assert_held(&nfsd_mutex);
> + lockdep_assert_held(&nn->nfsd_info.mutex);
>
> dprintk("nfsd: creating service\n");
>
> diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
> index 48666b83fe68..a12fe99156ec 100644
> --- a/include/linux/sunrpc/svc.h
> +++ b/include/linux/sunrpc/svc.h
> @@ -98,7 +98,7 @@ struct svc_serv {
> /* This is used by pool_stats to find and lock an svc */
> struct svc_info {
> struct svc_serv *serv;
> - struct mutex *mutex;
> + struct mutex mutex;
I know we haven't been good about it with this struct so far, but I
like prefixes on names like this. Maybe we can call this "si_mutex" ?
> };
>
> void svc_destroy(struct svc_serv **svcp);
> diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
> index 8b1837228799..b8352b7d6860 100644
> --- a/net/sunrpc/svc_xprt.c
> +++ b/net/sunrpc/svc_xprt.c
> @@ -1399,7 +1399,7 @@ static void *svc_pool_stats_start(struct seq_file *m, loff_t *pos)
>
> dprintk("svc_pool_stats_start, *pidx=%u\n", pidx);
>
> - mutex_lock(si->mutex);
> + mutex_lock(&si->mutex);
>
> if (!pidx)
> return SEQ_START_TOKEN;
> @@ -1436,7 +1436,7 @@ static void svc_pool_stats_stop(struct seq_file *m, void *p)
> {
> struct svc_info *si = m->private;
>
> - mutex_unlock(si->mutex);
> + mutex_unlock(&si->mutex);
> }
>
> static int svc_pool_stats_show(struct seq_file *m, void *p)
All that said, I definitely support making this mutex per-net.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
2025-06-20 23:33 [PATCH 0/3 RFC] improve some nfsd_mutex locking NeilBrown
@ 2025-06-20 23:33 ` NeilBrown
2025-06-21 13:02 ` kernel test robot
0 siblings, 1 reply; 11+ messages in thread
From: NeilBrown @ 2025-06-20 23:33 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: linux-nfs, Olga Kornievskaia, Dai Ngo, Tom Talpey, Li Lingfeng
The remaining uses for nfsd_mutex are all to protect per-netns
resources.
This patch replaces the global mutex with one per netns. The "svc_info"
struct now contains that mutex rather than a pointer to the mutex.
The use of guard(mutex)(...) is extended to all users of this lock.
Signed-off-by: NeilBrown <neil@brown.name>
---
.../admin-guide/nfs/nfsd-admin-interfaces.rst | 4 +-
fs/nfsd/netns.h | 1 +
fs/nfsd/nfsctl.c | 158 +++++++-----------
fs/nfsd/nfsd.h | 1 -
fs/nfsd/nfssvc.c | 40 ++---
include/linux/sunrpc/svc.h | 2 +-
net/sunrpc/svc_xprt.c | 4 +-
7 files changed, 73 insertions(+), 137 deletions(-)
diff --git a/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst b/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
index c05926f79054..0d9c3392e1ed 100644
--- a/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
+++ b/Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst
@@ -37,4 +37,6 @@ Implementation notes
Note that the rpc server requires the caller to serialize addition and
removal of listening sockets, and startup and shutdown of the server.
-For nfsd this is done using nfsd_mutex.
+For nfsd this is done using nfsd_info.mutex in struct nfsd_net, which is
+called config_mutex.
+
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 3e2d0fde80a7..d05e3f405d2e 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -134,6 +134,7 @@ struct nfsd_net {
struct svc_info nfsd_info;
#define nfsd_serv nfsd_info.serv
+#define config_mutex nfsd_info.mutex
struct percpu_ref nfsd_net_ref;
struct completion nfsd_net_confirm_done;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 0e7e89dc730b..66c87f63a258 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -223,7 +223,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
struct net *net = netns(file);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- guard(mutex)(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
if (!nn->nfsd_serv)
/* There cannot be any files to unlock */
return -EINVAL;
@@ -267,7 +267,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
int error;
struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
- guard(mutex)(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
if (!nn->nfsd_serv)
/* There cannot be any files to unlock */
return -EINVAL;
@@ -413,6 +413,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
char *mesg = buf;
int rv;
struct net *net = netns(file);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
if (size > 0) {
int newthreads;
@@ -422,9 +423,8 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
if (newthreads < 0)
return -EINVAL;
trace_nfsd_ctl_threads(net, newthreads);
- mutex_lock(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
- mutex_unlock(&nfsd_mutex);
if (rv < 0)
return rv;
} else
@@ -467,8 +467,10 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
int npools;
int *nthreads;
struct net *net = netns(file);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+ guard(mutex)(&nn->config_mutex);
- mutex_lock(&nfsd_mutex);
npools = nfsd_nrpools(net);
if (npools == 0) {
/*
@@ -476,7 +478,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
* writing to the threads file but NOT the pool_threads
* file, sorry. Report zero threads.
*/
- mutex_unlock(&nfsd_mutex);
strcpy(buf, "0\n");
return strlen(buf);
}
@@ -526,7 +527,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
rv = mesg - buf;
out_free:
kfree(nthreads);
- mutex_unlock(&nfsd_mutex);
return rv;
}
@@ -689,12 +689,10 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
*/
static ssize_t write_versions(struct file *file, char *buf, size_t size)
{
- ssize_t rv;
+ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
- mutex_lock(&nfsd_mutex);
- rv = __write_versions(file, buf, size);
- mutex_unlock(&nfsd_mutex);
- return rv;
+ guard(mutex)(&nn->config_mutex);
+ return __write_versions(file, buf, size);
}
/*
@@ -848,15 +846,12 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size,
*/
static ssize_t write_ports(struct file *file, char *buf, size_t size)
{
- ssize_t rv;
+ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
- mutex_lock(&nfsd_mutex);
- rv = __write_ports(file, buf, size, netns(file));
- mutex_unlock(&nfsd_mutex);
- return rv;
+ guard(mutex)(&nn->config_mutex);
+ return __write_ports(file, buf, size, netns(file));
}
-
int nfsd_max_blksize;
/*
@@ -898,13 +893,11 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
bsize = max_t(int, bsize, 1024);
bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE);
bsize &= ~(1024-1);
- mutex_lock(&nfsd_mutex);
- if (nn->nfsd_serv) {
- mutex_unlock(&nfsd_mutex);
+
+ guard(mutex)(&nn->config_mutex);
+ if (nn->nfsd_serv)
return -EBUSY;
- }
nfsd_max_blksize = bsize;
- mutex_unlock(&nfsd_mutex);
}
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
@@ -951,12 +944,8 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size,
static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
time64_t *time, struct nfsd_net *nn)
{
- ssize_t rv;
-
- mutex_lock(&nfsd_mutex);
- rv = __nfsd4_write_time(file, buf, size, time, nn);
- mutex_unlock(&nfsd_mutex);
- return rv;
+ guard(mutex)(&nn->config_mutex);
+ return __nfsd4_write_time(file, buf, size, time, nn);
}
/*
@@ -1055,13 +1044,10 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size,
*/
static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
{
- ssize_t rv;
struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
- mutex_lock(&nfsd_mutex);
- rv = __write_recoverydir(file, buf, size, nn);
- mutex_unlock(&nfsd_mutex);
- return rv;
+ guard(mutex)(&nn->config_mutex);
+ return __write_recoverydir(file, buf, size, nn);
}
#endif
@@ -1090,7 +1076,7 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
{
struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
- guard(mutex)(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
if (size > 0) {
switch(buf[0]) {
case 'Y':
@@ -1525,17 +1511,13 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
int i, ret, rqstp_index = 0;
- struct nfsd_net *nn;
-
- mutex_lock(&nfsd_mutex);
+ struct nfsd_net *nn = net_generic(sock_net(skb->sk), nfsd_net_id);
- nn = net_generic(sock_net(skb->sk), nfsd_net_id);
- if (!nn->nfsd_serv) {
- ret = -ENODEV;
- goto out_unlock;
- }
+ guard(mutex)(&nn->config_mutex);
+ if (!nn->nfsd_serv)
+ return -ENODEV;
- rcu_read_lock();
+ guard(rcu)();
for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) {
struct svc_rqst *rqstp;
@@ -1601,17 +1583,13 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
ret = nfsd_genl_rpc_status_compose_msg(skb, cb,
&genl_rqstp);
if (ret)
- goto out;
+ return ret;
}
}
cb->args[0] = i;
cb->args[1] = rqstp_index;
ret = skb->len;
-out:
- rcu_read_unlock();
-out_unlock:
- mutex_unlock(&nfsd_mutex);
return ret;
}
@@ -1640,14 +1618,12 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
count++;
}
- mutex_lock(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
nrpools = max(count, nfsd_nrpools(net));
nthreads = kcalloc(nrpools, sizeof(int), GFP_KERNEL);
- if (!nthreads) {
- ret = -ENOMEM;
- goto out_unlock;
- }
+ if (!nthreads)
+ return -ENOMEM;
i = 0;
nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
@@ -1663,7 +1639,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
info->attrs[NFSD_A_SERVER_SCOPE]) {
ret = -EBUSY;
if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
- goto out_unlock;
+ goto out_free;
ret = -EINVAL;
attr = info->attrs[NFSD_A_SERVER_GRACETIME];
@@ -1671,7 +1647,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
u32 gracetime = nla_get_u32(attr);
if (gracetime < 10 || gracetime > 3600)
- goto out_unlock;
+ goto out_free;
nn->nfsd4_grace = gracetime;
}
@@ -1681,7 +1657,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
u32 leasetime = nla_get_u32(attr);
if (leasetime < 10 || leasetime > 3600)
- goto out_unlock;
+ goto out_free;
nn->nfsd4_lease = leasetime;
}
@@ -1694,8 +1670,7 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope);
if (ret > 0)
ret = 0;
-out_unlock:
- mutex_unlock(&nfsd_mutex);
+out_free:
kfree(nthreads);
return ret;
}
@@ -1724,7 +1699,7 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_free_msg;
}
- mutex_lock(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
err = nla_put_u32(skb, NFSD_A_SERVER_GRACETIME,
nn->nfsd4_grace) ||
@@ -1733,7 +1708,7 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
nla_put_string(skb, NFSD_A_SERVER_SCOPE,
nn->nfsd_name);
if (err)
- goto err_unlock;
+ goto err_free_msg;
if (nn->nfsd_serv) {
int i;
@@ -1744,22 +1719,18 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
err = nla_put_u32(skb, NFSD_A_SERVER_THREADS,
sp->sp_nrthreads);
if (err)
- goto err_unlock;
+ goto err_free_msg;
}
} else {
err = nla_put_u32(skb, NFSD_A_SERVER_THREADS, 0);
if (err)
- goto err_unlock;
+ goto err_free_msg;
}
- mutex_unlock(&nfsd_mutex);
-
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
-err_unlock:
- mutex_unlock(&nfsd_mutex);
err_free_msg:
nlmsg_free(skb);
@@ -1782,13 +1753,10 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION))
return -EINVAL;
- mutex_lock(&nfsd_mutex);
-
nn = net_generic(genl_info_net(info), nfsd_net_id);
- if (nn->nfsd_serv) {
- mutex_unlock(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
+ if (nn->nfsd_serv)
return -EBUSY;
- }
/* clear current supported versions. */
nfsd_vers(nn, 2, NFSD_CLEAR);
@@ -1831,8 +1799,6 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
}
}
- mutex_unlock(&nfsd_mutex);
-
return 0;
}
@@ -1859,8 +1825,8 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_free_msg;
}
- mutex_lock(&nfsd_mutex);
nn = net_generic(genl_info_net(info), nfsd_net_id);
+ guard(mutex)(&nn->config_mutex);
for (i = 2; i <= 4; i++) {
int j;
@@ -1882,13 +1848,13 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
NFSD_A_SERVER_PROTO_VERSION);
if (!attr) {
err = -EINVAL;
- goto err_nfsd_unlock;
+ goto err_free_msg;
}
if (nla_put_u32(skb, NFSD_A_VERSION_MAJOR, i) ||
nla_put_u32(skb, NFSD_A_VERSION_MINOR, j)) {
err = -EINVAL;
- goto err_nfsd_unlock;
+ goto err_free_msg;
}
/* Set the enabled flag if the version is enabled */
@@ -1896,20 +1862,17 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
(i < 4 || nfsd_minorversion(nn, j, NFSD_TEST)) &&
nla_put_flag(skb, NFSD_A_VERSION_ENABLED)) {
err = -EINVAL;
- goto err_nfsd_unlock;
+ goto err_free_msg;
}
nla_nest_end(skb, attr);
}
}
- mutex_unlock(&nfsd_mutex);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
-err_nfsd_unlock:
- mutex_unlock(&nfsd_mutex);
err_free_msg:
nlmsg_free(skb);
@@ -1934,15 +1897,13 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
bool delete = false;
int err, rem;
- mutex_lock(&nfsd_mutex);
+ nn = net_generic(net, nfsd_net_id);
+ guard(mutex)(&nn->config_mutex);
err = nfsd_create_serv(net);
- if (err) {
- mutex_unlock(&nfsd_mutex);
+ if (err)
return err;
- }
- nn = net_generic(net, nfsd_net_id);
serv = nn->nfsd_serv;
spin_lock_bh(&serv->sv_lock);
@@ -2003,10 +1964,8 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
spin_unlock_bh(&serv->sv_lock);
/* Do not remove listeners while there are active threads. */
- if (serv->sv_nrthreads) {
- err = -EBUSY;
- goto out_unlock_mtx;
- }
+ if (serv->sv_nrthreads)
+ return -EBUSY;
/*
* Since we can't delete an arbitrary llist entry, destroy the
@@ -2057,9 +2016,6 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
nfsd_destroy_serv(net);
-out_unlock_mtx:
- mutex_unlock(&nfsd_mutex);
-
return err;
}
@@ -2088,12 +2044,12 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
goto err_free_msg;
}
- mutex_lock(&nfsd_mutex);
nn = net_generic(genl_info_net(info), nfsd_net_id);
+ guard(mutex)(&nn->config_mutex);
/* no nfs server? Just send empty socket list */
if (!nn->nfsd_serv)
- goto out_unlock_mtx;
+ goto out;
serv = nn->nfsd_serv;
spin_lock_bh(&serv->sv_lock);
@@ -2103,7 +2059,7 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
attr = nla_nest_start(skb, NFSD_A_SERVER_SOCK_ADDR);
if (!attr) {
err = -EINVAL;
- goto err_serv_unlock;
+ goto err;
}
if (nla_put_string(skb, NFSD_A_SOCK_TRANSPORT_NAME,
@@ -2112,21 +2068,19 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
sizeof(struct sockaddr_storage),
&xprt->xpt_local)) {
err = -EINVAL;
- goto err_serv_unlock;
+ goto err;
}
nla_nest_end(skb, attr);
}
spin_unlock_bh(&serv->sv_lock);
-out_unlock_mtx:
- mutex_unlock(&nfsd_mutex);
+out:
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
-err_serv_unlock:
+err:
spin_unlock_bh(&serv->sv_lock);
- mutex_unlock(&nfsd_mutex);
err_free_msg:
nlmsg_free(skb);
@@ -2228,7 +2182,7 @@ static __net_init int nfsd_net_init(struct net *net)
nn->nfsd_versions[i] = nfsd_support_version(i);
for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++)
nn->nfsd4_minorversions[i] = nfsd_support_version(4);
- nn->nfsd_info.mutex = &nfsd_mutex;
+ mutex_init(&nn->config_mutex);
nn->nfsd_serv = NULL;
nfsd4_init_leases_net(nn);
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 8ad9fcc23789..3cbca4d34f48 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -77,7 +77,6 @@ struct nfsd_genl_rqstp {
extern struct svc_program nfsd_programs[];
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
-extern struct mutex nfsd_mutex;
extern atomic_t nfsd_th_cnt; /* number of available threads */
bool nfsd_startup_get(void);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index b2080e5a71e6..f7fdb05203f2 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -56,20 +56,6 @@ static __be32 nfsd_init_request(struct svc_rqst *,
const struct svc_program *,
struct svc_process_info *);
-/*
- * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
- * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
- *
- * Finally, the nfsd_mutex also protects some of the global variables that are
- * accessed when nfsd starts and that are settable via the write_* routines in
- * nfsctl.c. In particular:
- *
- * user_recovery_dirname
- * user_lease_time
- * nfsd_versions
- */
-DEFINE_MUTEX(nfsd_mutex);
-
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
static const struct svc_version *localio_versions[] = {
[1] = &localio_version1,
@@ -239,14 +225,12 @@ static void nfsd_net_free(struct percpu_ref *ref)
int nfsd_nrthreads(struct net *net)
{
- int rv = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- mutex_lock(&nfsd_mutex);
- if (nn->nfsd_serv)
- rv = nn->nfsd_serv->sv_nrthreads;
- mutex_unlock(&nfsd_mutex);
- return rv;
+ guard(mutex)(&nn->config_mutex);
+ if (!nn->nfsd_serv)
+ return 0;
+ return nn->nfsd_serv->sv_nrthreads;
}
static int nfsd_init_socks(struct net *net, const struct cred *cred)
@@ -522,7 +506,6 @@ static struct notifier_block nfsd_inet6addr_notifier = {
};
#endif
-/* Only used under nfsd_mutex, so this atomic may be overkill: */
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
/**
@@ -534,7 +517,7 @@ void nfsd_destroy_serv(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv = nn->nfsd_serv;
- lockdep_assert_held(&nfsd_mutex);
+ lockdep_assert_held(&nn->config_mutex);
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = NULL;
@@ -606,17 +589,14 @@ void nfsd_shutdown_threads(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
- mutex_lock(&nfsd_mutex);
+ guard(mutex)(&nn->config_mutex);
serv = nn->nfsd_serv;
- if (serv == NULL) {
- mutex_unlock(&nfsd_mutex);
+ if (serv == NULL)
return;
- }
/* Kill outstanding nfsd threads */
svc_set_num_threads(serv, NULL, 0);
nfsd_destroy_serv(net);
- mutex_unlock(&nfsd_mutex);
}
struct svc_rqst *nfsd_current_rqst(void)
@@ -632,7 +612,7 @@ int nfsd_create_serv(struct net *net)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
- WARN_ON(!mutex_is_locked(&nfsd_mutex));
+ WARN_ON(!mutex_is_locked(&nn->config_mutex));
if (nn->nfsd_serv)
return 0;
@@ -714,7 +694,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
int err = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- lockdep_assert_held(&nfsd_mutex);
+ lockdep_assert_held(&nn->config_mutex);
if (nn->nfsd_serv == NULL || n <= 0)
return 0;
@@ -787,7 +767,7 @@ nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const c
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_serv *serv;
- lockdep_assert_held(&nfsd_mutex);
+ lockdep_assert_held(&nn->config_mutex);
dprintk("nfsd: creating service\n");
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 48666b83fe68..a12fe99156ec 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -98,7 +98,7 @@ struct svc_serv {
/* This is used by pool_stats to find and lock an svc */
struct svc_info {
struct svc_serv *serv;
- struct mutex *mutex;
+ struct mutex mutex;
};
void svc_destroy(struct svc_serv **svcp);
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 8b1837228799..b8352b7d6860 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -1399,7 +1399,7 @@ static void *svc_pool_stats_start(struct seq_file *m, loff_t *pos)
dprintk("svc_pool_stats_start, *pidx=%u\n", pidx);
- mutex_lock(si->mutex);
+ mutex_lock(&si->mutex);
if (!pidx)
return SEQ_START_TOKEN;
@@ -1436,7 +1436,7 @@ static void svc_pool_stats_stop(struct seq_file *m, void *p)
{
struct svc_info *si = m->private;
- mutex_unlock(si->mutex);
+ mutex_unlock(&si->mutex);
}
static int svc_pool_stats_show(struct seq_file *m, void *p)
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
2025-06-20 23:33 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
@ 2025-06-21 13:02 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-06-21 13:02 UTC (permalink / raw)
To: NeilBrown, Chuck Lever, Jeff Layton
Cc: llvm, oe-kbuild-all, linux-nfs, Olga Kornievskaia, Dai Ngo,
Tom Talpey, Li Lingfeng
Hi NeilBrown,
kernel test robot noticed the following build errors:
[auto build test ERROR on brauner-vfs/vfs.all]
[also build test ERROR on trondmy-nfs/linux-next linus/master v6.16-rc2 next-20250620]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/NeilBrown/nfsd-provide-proper-locking-for-all-write_-function/20250621-073955
base: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git vfs.all
patch link: https://lore.kernel.org/r/20250620233802.1453016-4-neil%40brown.name
patch subject: [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace.
config: i386-buildonly-randconfig-003-20250621 (https://download.01.org/0day-ci/archive/20250621/202506212005.LpUsPRa3-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250621/202506212005.LpUsPRa3-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506212005.LpUsPRa3-lkp@intel.com/
All errors (new ones prefixed by >>):
>> fs/nfsd/nfsctl.c:1699:3: error: cannot jump from this goto statement to its label
1699 | goto err_free_msg;
| ^
fs/nfsd/nfsctl.c:1702:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
1702 | guard(mutex)(&nn->config_mutex);
| ^
include/linux/cleanup.h:338:15: note: expanded from macro 'guard'
338 | CLASS(_name, __UNIQUE_ID(guard))
| ^
include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^
include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
84 | #define __PASTE(a,b) ___PASTE(a,b)
| ^
include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
83 | #define ___PASTE(a,b) a##b
| ^
<scratch space>:45:1: note: expanded from here
45 | __UNIQUE_ID_guard1097
| ^
fs/nfsd/nfsctl.c:1825:3: error: cannot jump from this goto statement to its label
1825 | goto err_free_msg;
| ^
fs/nfsd/nfsctl.c:1829:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
1829 | guard(mutex)(&nn->config_mutex);
| ^
include/linux/cleanup.h:338:15: note: expanded from macro 'guard'
338 | CLASS(_name, __UNIQUE_ID(guard))
| ^
include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^
include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
84 | #define __PASTE(a,b) ___PASTE(a,b)
| ^
include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
83 | #define ___PASTE(a,b) a##b
| ^
<scratch space>:68:1: note: expanded from here
68 | __UNIQUE_ID_guard1099
| ^
fs/nfsd/nfsctl.c:2044:3: error: cannot jump from this goto statement to its label
2044 | goto err_free_msg;
| ^
fs/nfsd/nfsctl.c:2048:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
2048 | guard(mutex)(&nn->config_mutex);
| ^
include/linux/cleanup.h:338:15: note: expanded from macro 'guard'
338 | CLASS(_name, __UNIQUE_ID(guard))
| ^
include/linux/compiler.h:166:29: note: expanded from macro '__UNIQUE_ID'
166 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
| ^
include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
84 | #define __PASTE(a,b) ___PASTE(a,b)
| ^
include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
83 | #define ___PASTE(a,b) a##b
| ^
<scratch space>:118:1: note: expanded from here
118 | __UNIQUE_ID_guard1101
| ^
3 errors generated.
vim +1699 fs/nfsd/nfsctl.c
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1677
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1678 /**
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1679 * nfsd_nl_threads_get_doit - get the number of running threads
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1680 * @skb: reply buffer
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1681 * @info: netlink metadata and command arguments
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1682 *
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1683 * Return 0 on success or a negative errno.
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1684 */
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1685 int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1686 {
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1687 struct net *net = genl_info_net(info);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1688 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1689 void *hdr;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1690 int err;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1691
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1692 skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1693 if (!skb)
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1694 return -ENOMEM;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1695
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1696 hdr = genlmsg_iput(skb, info);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1697 if (!hdr) {
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1698 err = -EMSGSIZE;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 @1699 goto err_free_msg;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1700 }
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1701
40677c06f2c6a4 NeilBrown 2025-06-21 1702 guard(mutex)(&nn->config_mutex);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1703
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1704 err = nla_put_u32(skb, NFSD_A_SERVER_GRACETIME,
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1705 nn->nfsd4_grace) ||
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1706 nla_put_u32(skb, NFSD_A_SERVER_LEASETIME,
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1707 nn->nfsd4_lease) ||
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1708 nla_put_string(skb, NFSD_A_SERVER_SCOPE,
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1709 nn->nfsd_name);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1710 if (err)
40677c06f2c6a4 NeilBrown 2025-06-21 1711 goto err_free_msg;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1712
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1713 if (nn->nfsd_serv) {
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1714 int i;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1715
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1716 for (i = 0; i < nfsd_nrpools(net); ++i) {
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1717 struct svc_pool *sp = &nn->nfsd_serv->sv_pools[i];
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1718
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1719 err = nla_put_u32(skb, NFSD_A_SERVER_THREADS,
60749cbe3d8ae5 NeilBrown 2024-07-15 1720 sp->sp_nrthreads);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1721 if (err)
40677c06f2c6a4 NeilBrown 2025-06-21 1722 goto err_free_msg;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1723 }
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1724 } else {
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1725 err = nla_put_u32(skb, NFSD_A_SERVER_THREADS, 0);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1726 if (err)
40677c06f2c6a4 NeilBrown 2025-06-21 1727 goto err_free_msg;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1728 }
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1729
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1730 genlmsg_end(skb, hdr);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1731
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1732 return genlmsg_reply(skb, info);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1733
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1734 err_free_msg:
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1735 nlmsg_free(skb);
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1736
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1737 return err;
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1738 }
924f4fb003ba11 Lorenzo Bianconi 2024-04-23 1739
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-06-21 13:02 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-18 21:31 [PATCH 0/3 RFC] improve some nfsd_mutex locking NeilBrown
2025-06-18 21:31 ` [PATCH 1/3] nfsd: provide proper locking for all write_ function NeilBrown
2025-06-20 12:59 ` Jeff Layton
2025-06-18 21:31 ` [PATCH 2/3] nfsd: use kref and new mutex for global config management NeilBrown
2025-06-19 14:06 ` Chuck Lever
2025-06-20 13:01 ` Jeff Layton
2025-06-18 21:31 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
2025-06-19 12:33 ` kernel test robot
2025-06-20 13:13 ` Jeff Layton
-- strict thread matches above, loose matches on Subject: below --
2025-06-20 23:33 [PATCH 0/3 RFC] improve some nfsd_mutex locking NeilBrown
2025-06-20 23:33 ` [PATCH 3/3] nfsd: split nfsd_mutex into one mutex per net-namespace NeilBrown
2025-06-21 13:02 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox