summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuck Lever <cel@kernel.org>2026-06-10 21:58:53 -0400
committerChuck Lever <cel@kernel.org>2026-07-12 22:22:39 -0400
commitc9be9f8e5fa249f6cd5de40ab41b2e64ea4a05fa (patch)
tree29c3f211c6d542527b95e9b063062012b8d3d810
parent458610970cad36c5e6b0dee351b9cf97afb33328 (diff)
downloadlinux-next-c9be9f8e5fa249f6cd5de40ab41b2e64ea4a05fa.tar.gz
linux-next-c9be9f8e5fa249f6cd5de40ab41b2e64ea4a05fa.zip
SUNRPC: Add svc_serv_maxthreads() to report the thread ceiling
A pooled RPC service sizes its threads dynamically, growing and shrinking each pool between its minimum and maximum bounds as load varies. The count of running threads therefore reflects recent demand, not the service's capacity. A consumer that sizes a data structure against the concurrency the service can sustain -- NFSD's NFSv4 session slot tables, for one -- needs that stable ceiling, and computing it means summing sp_nrthrmax across every pool. Add svc_serv_maxthreads() so the summation, and its dependence on the layout of struct svc_serv and struct svc_pool, stays within sunrpc. The read is lock-free: pool maxima change only when a service is reconfigured, a path callers already serialize against startup and shutdown, so a racing reader observes at worst a transient value. This is acceptable for the sizing heuristics that will consume it. nfsd_nrthreads() already sums sp_nrthrmax across pools by hand; convert it to svc_serv_maxthreads(), giving the new export an in-tree consumer and removing a copy of the dependence on svc_serv internals. Reviewed-by: NeilBrown <neil@brown.name> Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Benjamin Coddington <bcodding@hammerspace.com> Link: https://patch.msgid.link/20260610-nfsd-slot-growth-clamp-v1-1-7b966700df0b@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
-rw-r--r--fs/nfsd/nfssvc.c12
-rw-r--r--include/linux/sunrpc/svc.h1
-rw-r--r--net/sunrpc/svc.c23
3 files changed, 33 insertions, 3 deletions
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index e45d46089959..0d3838dd59c1 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -237,15 +237,21 @@ static void nfsd_net_free(struct percpu_ref *ref)
*/
#define NFSD_MAXSERVS 8192
+/**
+ * nfsd_nrthreads - report a namespace's configured nfsd thread count
+ * @net: network namespace to query
+ *
+ * Return: the configured thread ceiling, or 0 when no service runs.
+ */
int nfsd_nrthreads(struct net *net)
{
- int i, rv = 0;
+ int rv = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ /* nfsd_mutex keeps nn->nfsd_serv valid across the read. */
mutex_lock(&nfsd_mutex);
if (nn->nfsd_serv)
- for (i = 0; i < nn->nfsd_serv->sv_nrpools; ++i)
- rv += nn->nfsd_serv->sv_pools[i].sp_nrthrmax;
+ rv = svc_serv_maxthreads(nn->nfsd_serv);
mutex_unlock(&nfsd_mutex);
return rv;
}
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 4be6204f6630..3a0152d926fb 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -469,6 +469,7 @@ int svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool,
unsigned int min_threads, unsigned int max_threads);
int svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
unsigned int nrservs);
+unsigned int svc_serv_maxthreads(const struct svc_serv *serv);
int svc_pool_stats_open(struct svc_info *si, struct file *file);
void svc_process(struct svc_rqst *rqstp);
void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 009373737ea9..86d39610cf0a 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -988,6 +988,29 @@ svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
EXPORT_SYMBOL_GPL(svc_set_num_threads);
/**
+ * svc_serv_maxthreads - report a service's configured thread ceiling
+ * @serv: RPC service to query
+ *
+ * A pooled service sizes its threads dynamically, so the number of
+ * threads running at any moment tracks recent load rather than the
+ * service's capacity. The per-pool maximum is the stable figure a
+ * consumer should size against.
+ *
+ * The caller must keep @serv valid for the duration of the call.
+ *
+ * Return: the sum of every pool's maximum thread count.
+ */
+unsigned int svc_serv_maxthreads(const struct svc_serv *serv)
+{
+ unsigned int i, max = 0;
+
+ for (i = 0; i < serv->sv_nrpools; i++)
+ max += data_race(serv->sv_pools[i].sp_nrthrmax);
+ return max;
+}
+EXPORT_SYMBOL_GPL(svc_serv_maxthreads);
+
+/**
* svc_rqst_replace_page - Replace one page in rq_respages[]
* @rqstp: svc_rqst with pages to replace
* @page: replacement page