From nobody Sun Dec 14 06:34:18 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 54F342D839B; Fri, 12 Dec 2025 22:39:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579182; cv=none; b=O2PmXKNzn3fnn+94LUgSzAXqtHzOOcYaS7sZmsTKQ0ojFhn17D2/MvmGOpxmDwzEfSObm0bnLi+m9Si/hvn9lkBTDX6ZyIrxJbmLU65mNTn3282DeVGIpouF+3YkPPePH9b53hX13NqiXIiHUqod3AJ2yA2UkwPMYqPU44+bc+g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579182; c=relaxed/simple; bh=dIE975TUgkAuUNw4EjZWFUdwmYqV3JHWMaIbUC7wnxw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=flFLpYLdp6powqBMeWRsz7gBP3y/Zu9j9NFNDO/mCWvjYRpKO7NzZPl8j9swYSXl6ORRUzSL2svG1ZVUk67BTYu6cWh+39yXmqLb+SRGSkeB1qignbbjOXfK0Zoo9KN4ajmv2C2YaUouqo/PQSLhD1cwZvH6rG4qzQ6Hd3UHyxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SJsfWRvy; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="SJsfWRvy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C2C5BC4CEF5; Fri, 12 Dec 2025 22:39:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765579181; bh=dIE975TUgkAuUNw4EjZWFUdwmYqV3JHWMaIbUC7wnxw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=SJsfWRvyEjyFcSBbcy7d7I8tJP/VBXJWI7tR8u5WRmeDUStmYmLwrn2e8nn7ZuGnN wCbxp9soyBaPWxBJ+XFDH2r6dl68NXxoXXJW5mn7Mg1Mkh6LJ+WDLVfwFIjuX18G1s ehBnSCaov7XW0Lca7yJb7m1YSFIBCfrqvHPZNcZe2yVG9hHejFWGJnhCAqB+eEzpqV +d15HUt7lE1Xi/w55lVtFNqkOzJ2KWZFNK11urS2SijD2qC4n38pBs53+8u911r0IG qY230Avi+psyXSIVHd2lih2qApHsiAbPvw9IV5lAugIB1EDw6wuZRherPL28xE64R9 bH803UIbVLzaw== From: Jeff Layton Date: Sat, 13 Dec 2025 07:39:13 +0900 Subject: [PATCH RFC 1/6] sunrpc: split svc_set_num_threads() into two functions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251213-nfsd-dynathread-v1-1-de755e59cbc4@kernel.org> References: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> In-Reply-To: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8388; i=jlayton@kernel.org; h=from:subject:message-id; bh=dIE975TUgkAuUNw4EjZWFUdwmYqV3JHWMaIbUC7wnxw=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpPJmn8cWhIfZNCyBWM8NYjCh5ziIOmcH9PoNF2 OWFq8hnBlaJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaTyZpwAKCRAADmhBGVaC FYoWD/9R9Djb7EaVW5MEtVc3jAinkSGiG4ujhO6oNXDMjMAXsQ7z6i08GYp75xksEBqhr8MUQFa oAp+siOiQxmtBsLZT0SO8uSOOTEvKsXXGufRKSFnuv9KNHQ2HwIwaI18VZHrJFtQgQ4coSF5pzo ZlQGjrlFtobqe+Tt1DYkusKmJsEn95jst89Fu9eZ35A+hznOtg06cIyDqazpVGAMGVuhR9Vmy+o FR22oBeJNxmBAc94k/4j33/6qH7RQtW/SdLA2Ev7kIItjYcBl9kyq3ZogsYDcQT0q8qY2Av8Yxm 7QXO6ffK4vR7T3BW5WClwVODP9LVxCyG1Sm1L6h5ARhZ/VNcY0F+7wlxll6Aur8ckLtdQbdJn2C B+pmxj9eEa3DVQcoKx5kxe1uliYOiPvj4lGOvdrDj9evuymh23E00GNrL9ZvAXgJ+RZ6BbixgrC be/V8+e1eoC6MZQf+evDuD+PFxuFd4wn6TKcpG21a4PvBWDcnCbup0eqqZ/0lxxHe64WiqemYyQ AtJ5iQ76UKB4Kf8E2h8pw255ezmLZ9u9B+bsF6fO0kGThiBkWRkj12pAOjNs92zELB28YaBI2YZ 3D99hgZZLaIpfaQ9piDCAtdYS69/9XXuQNBM36Uy4jOGQz5fjf/oN7O79NaCPI2SbZCIavtyKtg OUBN1HCh8let/Fg== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 svc_set_num_threads() will set the number of running threads for a given pool. If the pool argument is set to NULL however, it will distribute the threads among all of the pools evenly. These divergent codepaths complicate the move to dynamic threading. Simplify the API by splitting these two cases into different helpers: Add a new svc_set_pool_threads() function that sets the number of threads in a single, given pool. Modify svc_set_num_threads() to distribute the threads evenly between all of the pools and then call svc_set_pool_threads() for each. Signed-off-by: Jeff Layton --- fs/lockd/svc.c | 4 ++-- fs/nfs/callback.c | 8 +++---- fs/nfsd/nfssvc.c | 21 ++++++++---------- include/linux/sunrpc/svc.h | 3 ++- net/sunrpc/svc.c | 54 +++++++++++++++++++++++++++++++++++++-----= ---- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index d68afa196535a8785bab2931c2b14f03a1174ef9..fbf132b4e08d11a91784c21ee02= 09fd7c149fd9d 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -340,7 +340,7 @@ static int lockd_get(void) return -ENOMEM; } =20 - error =3D svc_set_num_threads(serv, NULL, 1); + error =3D svc_set_num_threads(serv, 1); if (error < 0) { svc_destroy(&serv); return error; @@ -368,7 +368,7 @@ static void lockd_put(void) unregister_inet6addr_notifier(&lockd_inet6addr_notifier); #endif =20 - svc_set_num_threads(nlmsvc_serv, NULL, 0); + svc_set_num_threads(nlmsvc_serv, 0); timer_delete_sync(&nlmsvc_retry); svc_destroy(&nlmsvc_serv); dprintk("lockd_down: service destroyed\n"); diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index c8b837006bb27277ab34fe516f1b63992fee9b7f..44b35b7f8dc022f1d8c069eaf2f= 7d334c93f77fc 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -119,9 +119,9 @@ static int nfs_callback_start_svc(int minorversion, str= uct rpc_xprt *xprt, if (serv->sv_nrthreads =3D=3D nrservs) return 0; =20 - ret =3D svc_set_num_threads(serv, NULL, nrservs); + ret =3D svc_set_num_threads(serv, nrservs); if (ret) { - svc_set_num_threads(serv, NULL, 0); + svc_set_num_threads(serv, 0); return ret; } dprintk("nfs_callback_up: service started\n"); @@ -242,7 +242,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *= xprt) cb_info->users++; err_net: if (!cb_info->users) { - svc_set_num_threads(cb_info->serv, NULL, 0); + svc_set_num_threads(cb_info->serv, 0); svc_destroy(&cb_info->serv); } err_create: @@ -268,7 +268,7 @@ void nfs_callback_down(int minorversion, struct net *ne= t) nfs_callback_down_net(minorversion, serv, net); cb_info->users--; if (cb_info->users =3D=3D 0) { - svc_set_num_threads(serv, NULL, 0); + svc_set_num_threads(serv, 0); dprintk("nfs_callback_down: service destroyed\n"); svc_destroy(&cb_info->serv); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 93f7435cafd2362d9ddb28815277c824067cb370..aafec8ff588b85b0e26d40b76ef= 00953dc6472b4 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -594,7 +594,7 @@ void nfsd_shutdown_threads(struct net *net) } =20 /* Kill outstanding nfsd threads */ - svc_set_num_threads(serv, NULL, 0); + svc_set_num_threads(serv, 0); nfsd_destroy_serv(net); mutex_unlock(&nfsd_mutex); } @@ -702,12 +702,9 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct ne= t *net) if (nn->nfsd_serv =3D=3D NULL || n <=3D 0) return 0; =20 - /* - * Special case: When n =3D=3D 1, pass in NULL for the pool, so that the - * change is distributed equally among them. - */ + /* Special case: When n =3D=3D 1, distribute threads equally among pools.= */ if (n =3D=3D 1) - return svc_set_num_threads(nn->nfsd_serv, NULL, nthreads[0]); + return svc_set_num_threads(nn->nfsd_serv, nthreads[0]); =20 if (n > nn->nfsd_serv->sv_nrpools) n =3D nn->nfsd_serv->sv_nrpools; @@ -733,18 +730,18 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct n= et *net) =20 /* apply the new numbers */ for (i =3D 0; i < n; i++) { - err =3D svc_set_num_threads(nn->nfsd_serv, - &nn->nfsd_serv->sv_pools[i], - nthreads[i]); + err =3D svc_set_pool_threads(nn->nfsd_serv, + &nn->nfsd_serv->sv_pools[i], + nthreads[i]); if (err) goto out; } =20 /* Anything undefined in array is considered to be 0 */ for (i =3D n; i < nn->nfsd_serv->sv_nrpools; ++i) { - err =3D svc_set_num_threads(nn->nfsd_serv, - &nn->nfsd_serv->sv_pools[i], - 0); + err =3D svc_set_pool_threads(nn->nfsd_serv, + &nn->nfsd_serv->sv_pools[i], + 0); if (err) goto out; } diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 5506d20857c318774cd223272d4b0022cc19ffb8..dd5fbbf8b3d39df6c17a7624edf= 344557fffd32c 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -446,7 +446,8 @@ struct svc_serv * svc_create_pooled(struct svc_program= *prog, struct svc_stat *stats, unsigned int bufsize, int (*threadfn)(void *data)); -int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); +int svc_set_pool_threads(struct svc_serv *, struct svc_pool *, int); +int svc_set_num_threads(struct svc_serv *, int); 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 4704dce7284eccc9e2bc64cf22947666facfa86a..3fe5a7f8e57e3fa3837265ec068= 84b357d5373ff 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -856,15 +856,12 @@ svc_stop_kthreads(struct svc_serv *serv, struct svc_p= ool *pool, int nrservs) } =20 /** - * svc_set_num_threads - adjust number of threads per RPC service + * svc_set_pool_threads - adjust number of threads per pool * @serv: RPC service to adjust - * @pool: Specific pool from which to choose threads, or NULL + * @pool: Specific pool from which to choose threads * @nrservs: New number of threads for @serv (0 or less means kill all thr= eads) * - * Create or destroy threads to make the number of threads for @serv the - * given number. If @pool is non-NULL, change only threads in that pool; - * otherwise, round-robin between all pools for @serv. @serv's - * sv_nrthreads is adjusted for each thread created or destroyed. + * Create or destroy threads in @pool to bring it to @nrservs. * * Caller must ensure mutual exclusion between this and server startup or * shutdown. @@ -873,12 +870,12 @@ svc_stop_kthreads(struct svc_serv *serv, struct svc_p= ool *pool, int nrservs) * starting a thread. */ int -svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrse= rvs) +svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool, int nrs= ervs) { if (!pool) - nrservs -=3D serv->sv_nrthreads; - else - nrservs -=3D pool->sp_nrthreads; + return -EINVAL; + + nrservs -=3D pool->sp_nrthreads; =20 if (nrservs > 0) return svc_start_kthreads(serv, pool, nrservs); @@ -886,6 +883,43 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_= pool *pool, int nrservs) return svc_stop_kthreads(serv, pool, nrservs); return 0; } +EXPORT_SYMBOL_GPL(svc_set_pool_threads); + +/** + * svc_set_num_threads - adjust number of threads in serv + * @serv: RPC service to adjust + * @nrservs: New number of threads for @serv (0 or less means kill all thr= eads) + * + * Create or destroy threads in @serv to bring it to @nrservs. If there + * are multiple pools then the new threads or victims will be distributed + * evenly among them. + * + * Caller must ensure mutual exclusion between this and server startup or + * shutdown. + * + * Returns zero on success or a negative errno if an error occurred while + * starting a thread. + */ +int +svc_set_num_threads(struct svc_serv *serv, int nrservs) +{ + int base =3D nrservs / serv->sv_nrpools; + int remain =3D nrservs % serv->sv_nrpools; + int i, err; + + for (i =3D 0; i < serv->sv_nrpools; ++i) { + int threads =3D base; + + if (remain) { + ++threads; + --remain; + } + err =3D svc_set_pool_threads(serv, &serv->sv_pools[i], threads); + if (err) + break; + } + return err; +} EXPORT_SYMBOL_GPL(svc_set_num_threads); =20 /** --=20 2.52.0 From nobody Sun Dec 14 06:34:18 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 061B82D543E; Fri, 12 Dec 2025 22:39:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579185; cv=none; b=HpYch7LZInXHHnOHNYuUI2I5oWdj+XYj6373FDQe9V1sxIEZYlJkuPsLAv/d8o7OjTEO2+CCNjPLxRCZPPxyz8lDnIKjMEv3+YHFLjhCHevO77cH2kULLvwx3WPPN5tgmiVkP0bOOovYGEWiI2TI9doq6wfuQT4Ph/MKcRI+RQg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579185; c=relaxed/simple; bh=En+G5End2AK7R9Jp9jH88aqH0V7NIQgAbC6srDNX1fM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VNjxkcFG3RxdR8PvBOBtaoh4M2AT5QYfJZ9AhngvqM7vfiYEr76Wf1LPP5WkfH8oGWate/J9rYpKdI9IptLrt2sbwV+uF9X/Vw6xizlk4ziL8RfrGp8YEUAsFAihvrDaTUzvMlYzZAQqOATfJoTPuSbSoEzfCCG0WR887cWHRoU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NGe0pLN7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NGe0pLN7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 575C1C19422; Fri, 12 Dec 2025 22:39:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765579184; bh=En+G5End2AK7R9Jp9jH88aqH0V7NIQgAbC6srDNX1fM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=NGe0pLN7ypbCZzN9Rtkjv/nE5/IdxxzMMg/0EdKBGw+ZXvj7udEj4MUDRMNeKWPDn ZE6vELmQbTADxggSpmzXMGY/jgwiuE7IT6FlY8uG7oyCqiXxW4sRVQv+baxECx0vpo gZN/RxzHH+NIZUr7bCRZQueGHTZ26126D6rtU81gy4Cjl77i4auE64Xaw4XiPJGIic derMC/A86Vs9GBT8uNUNoPrxYuuiMy5tmvukGIYzwQ9AoHIYeMVgS6h+/BmG6BUmWV 5Aub2TBJbg8Upt0KAEMYfZXMNal6Pzoe1Z3Q4P8BXmF7EvY5/V0x7ie2ii4bX/IY/R CcnjcvvBGxZEA== From: Jeff Layton Date: Sat, 13 Dec 2025 07:39:14 +0900 Subject: [PATCH RFC 2/6] sunrpc: remove special handling of NULL pool from svc_start/stop_kthreads() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251213-nfsd-dynathread-v1-2-de755e59cbc4@kernel.org> References: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> In-Reply-To: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=3170; i=jlayton@kernel.org; h=from:subject:message-id; bh=En+G5End2AK7R9Jp9jH88aqH0V7NIQgAbC6srDNX1fM=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpPJmoAa2yTOUtJ6P9idQv872HMKEYxxzFgfI04 OPhnOXdIXKJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaTyZqAAKCRAADmhBGVaC FVxnD/9nn+oTWCUNdxFkMZY7aCdTLByi1MSUP0/2eoTVNdqOcXtpKk9kkxXK1u01YJs3vuPDTS9 TU34miZWs3ICC9MzmUdv/ZSPR/7O1oyBMoRqdZ5OvhoGkcGtvjfteVCdx3u6HXmUbYWkOml/wxk cytr8EOZ0isvKX1fNI8LejYaEyN0yKH0wIyxh48dve57H9U4feO7aqQErqnVpE+7432DGQCDg46 f9GufuwY0TPvW0Ry6w7duTH979aNQrzdTAro2n8NlbMZaXRbYtxQ2ucHf+J15jXP7DHIOSEGeUO fcn0Qr+HY7dzfvIBflVWD9WgYkmOfvBqi87ZvAT8n1XmtEco9lAW/QLtmWPIzNuX3xO5WrhyBa0 GQSLogZu+Nu2gBs5zIC4J1/YEQp/POiBZOzSIfE/ZbTaN/zUmripHl2kfFqzkRHhU+MPsjrB6Ry xZuI5EJhMBn77T1qpIDuK7QvpepDDP2wrW0I7gcLs1aOIATY6PejLMy5mhoIHquTIsz56vUKi0d Pmqb28lkHcKx4vAdMcWqBFYLQZWVriRpCZk0/xFkp9vNvXejblqC/L/4/4cDDnLiPz2GkJkBtr0 Jk28tEOvCIiIqu0H5Ecgh+vIoby9NN5jVjNWgeUz7KihTPZAXTKxWOh9oYjNvJiYdCVjGbwhiwq q3JgvvlcPBqJSgA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Now that svc_set_num_threads() handles distributing the threads among the available pools, remove the special handling of a NULL pool pointer from svc_start_kthreads() and svc_stop_kthreads(). Signed-off-by: Jeff Layton --- net/sunrpc/svc.c | 53 +++++++---------------------------------------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 3fe5a7f8e57e3fa3837265ec06884b357d5373ff..3484c587a108e6f34e5c23edaf8= f3a3c169c9e4a 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -763,53 +763,19 @@ void svc_pool_wake_idle_thread(struct svc_pool *pool) } EXPORT_SYMBOL_GPL(svc_pool_wake_idle_thread); =20 -static struct svc_pool * -svc_pool_next(struct svc_serv *serv, struct svc_pool *pool, unsigned int *= state) -{ - return pool ? pool : &serv->sv_pools[(*state)++ % serv->sv_nrpools]; -} - -static struct svc_pool * -svc_pool_victim(struct svc_serv *serv, struct svc_pool *target_pool, - unsigned int *state) -{ - struct svc_pool *pool; - unsigned int i; - - pool =3D target_pool; - - if (!pool) { - for (i =3D 0; i < serv->sv_nrpools; i++) { - pool =3D &serv->sv_pools[--(*state) % serv->sv_nrpools]; - if (pool->sp_nrthreads) - break; - } - } - - if (pool && pool->sp_nrthreads) { - set_bit(SP_VICTIM_REMAINS, &pool->sp_flags); - set_bit(SP_NEED_VICTIM, &pool->sp_flags); - return pool; - } - return NULL; -} - static int svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrser= vs) { struct svc_rqst *rqstp; struct task_struct *task; - struct svc_pool *chosen_pool; - unsigned int state =3D serv->sv_nrthreads-1; int node; int err; =20 do { nrservs--; - chosen_pool =3D svc_pool_next(serv, pool, &state); - node =3D svc_pool_map_get_node(chosen_pool->sp_id); + node =3D svc_pool_map_get_node(pool->sp_id); =20 - rqstp =3D svc_prepare_thread(serv, chosen_pool, node); + rqstp =3D svc_prepare_thread(serv, pool, node); if (!rqstp) return -ENOMEM; task =3D kthread_create_on_node(serv->sv_threadfn, rqstp, @@ -821,7 +787,7 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_po= ol *pool, int nrservs) =20 rqstp->rq_task =3D task; if (serv->sv_nrpools > 1) - svc_pool_map_set_cpumask(task, chosen_pool->sp_id); + svc_pool_map_set_cpumask(task, pool->sp_id); =20 svc_sock_update_bufs(serv); wake_up_process(task); @@ -840,16 +806,11 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_= pool *pool, int nrservs) static int svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrserv= s) { - unsigned int state =3D serv->sv_nrthreads-1; - struct svc_pool *victim; - do { - victim =3D svc_pool_victim(serv, pool, &state); - if (!victim) - break; - svc_pool_wake_idle_thread(victim); - wait_on_bit(&victim->sp_flags, SP_VICTIM_REMAINS, - TASK_IDLE); + set_bit(SP_VICTIM_REMAINS, &pool->sp_flags); + set_bit(SP_NEED_VICTIM, &pool->sp_flags); + svc_pool_wake_idle_thread(pool); + wait_on_bit(&pool->sp_flags, SP_VICTIM_REMAINS, TASK_IDLE); nrservs++; } while (nrservs < 0); return 0; --=20 2.52.0 From nobody Sun Dec 14 06:34:18 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A418D2C08DF; Fri, 12 Dec 2025 22:39:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579188; cv=none; b=WWTWWyScKQVb046BL8QSu2HXfyc29KdzLuBViODPbXFJRaI0JZvhexHv+nSu6OF/zGGK24ybkbRpRuFExdy0MCfLZC4d1ljuzi69mN7chPHZyu2aFAHYnENrefDLGNINGy3HrMTOmzS8o2Q3cT7zHfkWzfIoMCrKXxWiIRHgfTQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579188; c=relaxed/simple; bh=PBKOld62qb3aIXy84QE4/Vk+CdaXTEqGox5SaOfThHc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YXqAxZsWDRXi3hQTunYLj7ifxC2vjW2yXiuRbt5FYn0F6bnWjMJY6iWEQJuvzCNarXjtTWlvk31pOZiqXr58Z1SmATCVqSVAD1Eo0gkvSq5vUI/S5ILG8iM2KtMhDO0SvufNLzHwC5cuUqvg5fYYn0yBaBXMO5BtMhP7Rj9VXTs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mfsBUhwD; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mfsBUhwD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 05152C4CEF5; Fri, 12 Dec 2025 22:39:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765579187; bh=PBKOld62qb3aIXy84QE4/Vk+CdaXTEqGox5SaOfThHc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=mfsBUhwDIDqsp85o5x8bVDDKZO+7PN92uUjLWiNMZOYqOsq8x3qawibnc4cGhz/Qk 25jN+bc1qYuIT1RcVqdTQIAsYVZWYWS7yMZnBCfu4gRGKbcX44ZwOP2HK0kZIaXPtU tSVpsOK2XcvXA1iJFphbUiZ2bMSx/YGy0hzuENhvAYlJHAQqJoZi/qtjl45x1KcOb8 q9H7GLaB02IRGYGefTceI/JIyjRpvYwJZgXGdjUYXigc3rXTQP3rDWpuLg/CdWX9mN z1d1+8qt5Z1DA8Z4+JCa1Yq5FGx2VS4GT47xy1F7J59bdo/r6kUlKLLxJ05Ivo6Bze tcD6tsNy0zNkQ== From: Jeff Layton Date: Sat, 13 Dec 2025 07:39:15 +0900 Subject: [PATCH RFC 3/6] sunrpc: track the max number of requested threads in a pool Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251213-nfsd-dynathread-v1-3-de755e59cbc4@kernel.org> References: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> In-Reply-To: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1768; i=jlayton@kernel.org; h=from:subject:message-id; bh=PBKOld62qb3aIXy84QE4/Vk+CdaXTEqGox5SaOfThHc=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpPJmoQYoeeQc4T9DkAvMsTkadnS0nwtEAyMVPF HTG+B5OFRCJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaTyZqAAKCRAADmhBGVaC FSbmD/4lAqmiBjKc83UVvYOunR+6KzIz/U4Q5BBUxJ5jneRxcZdldc9qPNF82pmOM1ITjU0PfT6 mY8ysicZDTWvABjbHBMGMsuhmzBaVHFTb423x5ig4Upau20HeXitgFe758zo1sXSNtfWW0NvKm9 ERNBxV4mCr45CBgewHz3tqG0dzHOrE2RoAmTVPaWuupQcAIpZvZX9OVVdZiGFJgHDrtnM7E2MEF LkSIO/sXVIY0gOBohxsQr4ufTDs9AVOGjpq/FsUAQMPaNTVHHeLKnZyEphr1QLHYuIth2B5Wn4n CbzHTzDlUc6iB5O/KuPZafAiDpJbRjxX/pzZjG/DLdjJlFIRWYsc5agu6IXfxUYi/ePVFf8YFc6 J36kUmBwNOw9/MaE1bo5BTwUm78W5xjyLdJmj4qbtqq3TaHRrUoBpQ5Qb1yXxvjR/78kniQhIxb XrOS1714N9F+VigcQLp4wie9MWX7hdU83d1HN6ltH3wLsSjbrVzqWIsNfmYuJL0nBbgjVfyBnP1 lwTGTnt3VMKpKwhVDWrwA0kgmJH9tzMdiMHRpwQq+pR6D/HlyRaoEHF5kNu5FbCwp3v68/2+djo pa8JPCySYeFOBJ4y9cwmQfjFeNjtrNz2EKziTXBDROB9Uy0JG+Q2s9TcMfUeYQga4tNajJDfYz/ DJ3Rh4IIjOqABVQ== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 The kernel currently tracks the number of threads running in a pool in the "sp_nrthreads" field. In the future, where threads are dynamically spun up and down, it'll be necessary to keep track of the maximum number of requested threads separately from the actual number running. Add a pool->sp_nrthrmax parameter to track this. When userland changes the number of threads in a pool, update that value accordingly. Signed-off-by: Jeff Layton --- include/linux/sunrpc/svc.h | 3 ++- net/sunrpc/svc.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index dd5fbbf8b3d39df6c17a7624edf344557fffd32c..ee9260ca908c907f4373f4cfa47= 1b272bc7bcc8c 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -35,8 +35,9 @@ */ struct svc_pool { unsigned int sp_id; /* pool id; also node id on NUMA */ + unsigned int sp_nrthreads; /* # of threads currently running in pool */ + unsigned int sp_nrthrmax; /* Max requested number of threads in pool */ struct lwq sp_xprts; /* pending transports */ - unsigned int sp_nrthreads; /* # of threads in pool */ struct list_head sp_all_threads; /* all server threads */ struct llist_head sp_idle_threads; /* idle server threads */ =20 diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 3484c587a108e6f34e5c23edaf8f3a3c169c9e4a..8cd45f62ef74af6e0826b8f13cc= 903b0962af5e0 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -836,6 +836,7 @@ svc_set_pool_threads(struct svc_serv *serv, struct svc_= pool *pool, int nrservs) if (!pool) return -EINVAL; =20 + pool->sp_nrthrmax =3D nrservs; nrservs -=3D pool->sp_nrthreads; =20 if (nrservs > 0) --=20 2.52.0 From nobody Sun Dec 14 06:34:18 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D8562877E9; Fri, 12 Dec 2025 22:39:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579190; cv=none; b=sAw4k//koNCOu1L+OADlxmirEeNqYcIMfLXspPD0Khji5C1n8Ot/NZGOyPwhMzjNBvuwOBHvNb2buXRW5Fi0lf1zkJTIlRaa7xdZhXFPesneZS2mxNXerbtUMErRJVcqcQzn7N8MLuu6+ZHa8UNRYTEV/X2Dcn+Z9/EQwCms7lM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579190; c=relaxed/simple; bh=2V8LC7bIXuva455YKP3tb4V3HVWTHw3F2x+OVnwt/a4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cOKPf0h1eK3MiZs6E7Q82TU1Fq/sQr0q02znJxXSl1Uz6kd7UVuFYLLNGZUYvFmaHI7UbFcwRb0siEqwxCTahNuMjKVlgk4Rf5TA8lYOCWXG+FrEVjNqbALZWK21INercujdA9NY+iaf+xt7ZSpyfsQhiWrFpX170UPhrzgQB1Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=i/vw+eN6; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="i/vw+eN6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A6B40C113D0; Fri, 12 Dec 2025 22:39:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765579189; bh=2V8LC7bIXuva455YKP3tb4V3HVWTHw3F2x+OVnwt/a4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=i/vw+eN678TITbNczRiI6wutJkGdfTRUm1A8CC2SrYSbaOf/p+QH91HesB0aD6MD9 F4j3JT3BNojjMzZXZ6bA7JXZGy8JrOWh1n5n/hEBghP98IFITH8F5CNoHu+M+7rTXr cejZT+nL5Q91g9BvfphI4633ivYineB8W8Z7Cfu/aZSrgkr4YDpbIAa0hiNHXp1buh 9FlOWcnqiGSyc/sJ/+KXeIGnwOKIXga9Zyp0kvoqH9+IDJLiF5qe9NThZ2n5FXmhyB +NNjwx3ALU9f3/I4lxNmfb98xsZK8mjnJOjL3Dk/DZDVPVGRCcO0Ny28kJAjux2Yqd mrpXKrz371tzg== From: Jeff Layton Date: Sat, 13 Dec 2025 07:39:16 +0900 Subject: [PATCH RFC 4/6] sunrpc: introduce the concept of a minimum number of threads per pool Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251213-nfsd-dynathread-v1-4-de755e59cbc4@kernel.org> References: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> In-Reply-To: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8431; i=jlayton@kernel.org; h=from:subject:message-id; bh=2V8LC7bIXuva455YKP3tb4V3HVWTHw3F2x+OVnwt/a4=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpPJmoSHyPBXfyJ2GnV/1O0lGu3G5ROHDXcR46X VYrF3Z5pKOJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaTyZqAAKCRAADmhBGVaC FX9UD/4mkgzKjPbMZ0GQFDrYmkzIUe/1G9zRfOjpd//ilSJ+qoyBK3x0fliLOUBbFfxynpDNxQM kLfl4+mXzbbhtcju+afFSxd3UEjixjNtZkYP9pOlaZMNWB6OJq07M854agfzwUtpI9nD/8pSPQv qHPUjgRSd9ukaJ2O7esxnZSasC4GL9EcoqZM7AhrtgfVPdbce6wqHQea4ZrlvXOQFKp9wqK/O5Z LQhpm8L0EsRbzTeDJf9xTFcfIpd0Um2YuTRwVeM1iNc8v0bncMK5hdLamb0Ctt9J0ZPH67dvBkW +Tevqwd7ohPHIiPze6NfKKdY/GnkBCfhMONB+1JgHS0HkvFjuFoqhVZiqCfHCB2f7SrMmFgOn8m aye9zFBp8H/HVW0lw8gCgjIU+VvDBPJ3r2RZIojHW+k+W8D8eNb4hngMCt4TnTcA4jXeAIKuyEq hLMD3hfttLfYUL/pddi5dfrDv+twoWdLLkeoDLX56A8dlmdQ9dnc4e+G3tr5j+WusCHyxHlIYrZ TCKD05W0OBQTSZ3DszDVGeTVYBSli+PbYvCxBeu/kg/mRC68xMfZixGLsmVUfPVHyXNyFwwkm4M BochS95WO4yRDGzu0xecimklTMdWnbziTMRhnDssOekfpc7VOvZNuLLTQZ1VHoeGPIeJOkIhohX RF2CRUVlpBdPcdg== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Add a new pool->sp_nrthrmin field to track the minimum number of threads in a pool. Add min_threads parameters to both svc_set_num_threads() and svc_set_pool_threads(). If min_threads is non-zero, then have svc_set_num_threads() ensure that the number of running threads is between the min and the max. For now, the min_threads is always 0, but a later patch will pass the proper value through from nfsd. Signed-off-by: Jeff Layton --- fs/lockd/svc.c | 4 ++-- fs/nfs/callback.c | 8 ++++---- fs/nfsd/nfssvc.c | 8 ++++---- include/linux/sunrpc/svc.h | 7 ++++--- net/sunrpc/svc.c | 21 ++++++++++++++++++--- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index fbf132b4e08d11a91784c21ee0209fd7c149fd9d..7899205314391415dfb698ab58f= e97efc426d928 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -340,7 +340,7 @@ static int lockd_get(void) return -ENOMEM; } =20 - error =3D svc_set_num_threads(serv, 1); + error =3D svc_set_num_threads(serv, 1, 0); if (error < 0) { svc_destroy(&serv); return error; @@ -368,7 +368,7 @@ static void lockd_put(void) unregister_inet6addr_notifier(&lockd_inet6addr_notifier); #endif =20 - svc_set_num_threads(nlmsvc_serv, 0); + svc_set_num_threads(nlmsvc_serv, 0, 0); timer_delete_sync(&nlmsvc_retry); svc_destroy(&nlmsvc_serv); dprintk("lockd_down: service destroyed\n"); diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 44b35b7f8dc022f1d8c069eaf2f7d334c93f77fc..32bbc0e688ff3988e4ba50eeb36= b4808cec07c87 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -119,9 +119,9 @@ static int nfs_callback_start_svc(int minorversion, str= uct rpc_xprt *xprt, if (serv->sv_nrthreads =3D=3D nrservs) return 0; =20 - ret =3D svc_set_num_threads(serv, nrservs); + ret =3D svc_set_num_threads(serv, nrservs, 0); if (ret) { - svc_set_num_threads(serv, 0); + svc_set_num_threads(serv, 0, 0); return ret; } dprintk("nfs_callback_up: service started\n"); @@ -242,7 +242,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *= xprt) cb_info->users++; err_net: if (!cb_info->users) { - svc_set_num_threads(cb_info->serv, 0); + svc_set_num_threads(cb_info->serv, 0, 0); svc_destroy(&cb_info->serv); } err_create: @@ -268,7 +268,7 @@ void nfs_callback_down(int minorversion, struct net *ne= t) nfs_callback_down_net(minorversion, serv, net); cb_info->users--; if (cb_info->users =3D=3D 0) { - svc_set_num_threads(serv, 0); + svc_set_num_threads(serv, 0, 0); dprintk("nfs_callback_down: service destroyed\n"); svc_destroy(&cb_info->serv); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index aafec8ff588b85b0e26d40b76ef00953dc6472b4..993ed338764b0ccd7bdfb76bd6f= bb5dc6ab4022d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -594,7 +594,7 @@ void nfsd_shutdown_threads(struct net *net) } =20 /* Kill outstanding nfsd threads */ - svc_set_num_threads(serv, 0); + svc_set_num_threads(serv, 0, 0); nfsd_destroy_serv(net); mutex_unlock(&nfsd_mutex); } @@ -704,7 +704,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net= *net) =20 /* Special case: When n =3D=3D 1, distribute threads equally among pools.= */ if (n =3D=3D 1) - return svc_set_num_threads(nn->nfsd_serv, nthreads[0]); + return svc_set_num_threads(nn->nfsd_serv, nthreads[0], 0); =20 if (n > nn->nfsd_serv->sv_nrpools) n =3D nn->nfsd_serv->sv_nrpools; @@ -732,7 +732,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net= *net) for (i =3D 0; i < n; i++) { err =3D svc_set_pool_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i], - nthreads[i]); + nthreads[i], 0); if (err) goto out; } @@ -741,7 +741,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net= *net) for (i =3D n; i < nn->nfsd_serv->sv_nrpools; ++i) { err =3D svc_set_pool_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i], - 0); + 0, 0); if (err) goto out; } diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index ee9260ca908c907f4373f4cfa471b272bc7bcc8c..35bd3247764ae8dc5dcdfffeea3= 6f7cfefd13372 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -36,6 +36,7 @@ struct svc_pool { unsigned int sp_id; /* pool id; also node id on NUMA */ unsigned int sp_nrthreads; /* # of threads currently running in pool */ + unsigned int sp_nrthrmin; /* Min number of threads to run per pool */ unsigned int sp_nrthrmax; /* Max requested number of threads in pool */ struct lwq sp_xprts; /* pending transports */ struct list_head sp_all_threads; /* all server threads */ @@ -72,7 +73,7 @@ struct svc_serv { struct svc_stat * sv_stats; /* RPC statistics */ spinlock_t sv_lock; unsigned int sv_nprogs; /* Number of sv_programs */ - unsigned int sv_nrthreads; /* # of server threads */ + unsigned int sv_nrthreads; /* # of running server threads */ unsigned int sv_max_payload; /* datagram payload size */ unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */ unsigned int sv_xdrsize; /* XDR buffer size */ @@ -447,8 +448,8 @@ struct svc_serv * svc_create_pooled(struct svc_program= *prog, struct svc_stat *stats, unsigned int bufsize, int (*threadfn)(void *data)); -int svc_set_pool_threads(struct svc_serv *, struct svc_pool *, int); -int svc_set_num_threads(struct svc_serv *, int); +int svc_set_pool_threads(struct svc_serv *, struct svc_pool *, int, un= signed int); +int svc_set_num_threads(struct svc_serv *, int, unsigned int); 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 8cd45f62ef74af6e0826b8f13cc903b0962af5e0..dc818158f8529b62dcf96c91bd9= a9d4ab21df91f 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -821,6 +821,7 @@ svc_stop_kthreads(struct svc_serv *serv, struct svc_poo= l *pool, int nrservs) * @serv: RPC service to adjust * @pool: Specific pool from which to choose threads * @nrservs: New number of threads for @serv (0 or less means kill all thr= eads) + * @min_threads: minimum number of threads per pool (0 means set to same a= s nrservs) * * Create or destroy threads in @pool to bring it to @nrservs. * @@ -831,12 +832,22 @@ svc_stop_kthreads(struct svc_serv *serv, struct svc_p= ool *pool, int nrservs) * starting a thread. */ int -svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool, int nrs= ervs) +svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool, int nrs= ervs, + unsigned int min_threads) { if (!pool) return -EINVAL; =20 pool->sp_nrthrmax =3D nrservs; + if (min_threads) { + if (pool->sp_nrthreads > nrservs) { + // fallthrough to update nrservs + } else if (pool->sp_nrthreads < min_threads) { + nrservs =3D min_threads; + } else { + return 0; + } + } nrservs -=3D pool->sp_nrthreads; =20 if (nrservs > 0) @@ -851,6 +862,7 @@ EXPORT_SYMBOL_GPL(svc_set_pool_threads); * svc_set_num_threads - adjust number of threads in serv * @serv: RPC service to adjust * @nrservs: New number of threads for @serv (0 or less means kill all thr= eads) + * @min_threads: minimum number of threads per pool (0 means set to same a= s nrservs) * * Create or destroy threads in @serv to bring it to @nrservs. If there * are multiple pools then the new threads or victims will be distributed @@ -863,20 +875,23 @@ EXPORT_SYMBOL_GPL(svc_set_pool_threads); * starting a thread. */ int -svc_set_num_threads(struct svc_serv *serv, int nrservs) +svc_set_num_threads(struct svc_serv *serv, int nrservs, unsigned int min_t= hreads) { int base =3D nrservs / serv->sv_nrpools; int remain =3D nrservs % serv->sv_nrpools; int i, err; =20 for (i =3D 0; i < serv->sv_nrpools; ++i) { + struct svc_pool *pool =3D &serv->sv_pools[i]; int threads =3D base; =20 if (remain) { ++threads; --remain; } - err =3D svc_set_pool_threads(serv, &serv->sv_pools[i], threads); + + pool->sp_nrthrmin =3D min_threads; + err =3D svc_set_pool_threads(serv, pool, threads, min_threads); if (err) break; } --=20 2.52.0 From nobody Sun Dec 14 06:34:18 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BD05B2D9496; Fri, 12 Dec 2025 22:39:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579192; cv=none; b=X21JTm8IIXJIQcq7hLea7mwo33B2O8hUS61eG6HRJHe7gcigUhfFVHRgK74nfvRyUer0dZaHiDlD1TsDrxTpuqBRxni5coExltwYEEFuIdIpoz0hj/FPPJoTU/POmVN/9EbbUKwj3nDMow4hSYAoI3xjT5nkatYqOVFVPpVIy+U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579192; c=relaxed/simple; bh=X1FzereqmkvvyyWDhOqhN108ue/fvWDPvdFvAdVnD88=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OSjdzg/JmOH9jShuo2b+PPxTJ+tJmpHRBN+49XXTyqM+zB1Yy1ZaWiSFdwXxyojuyPLKlOpBFLyg4ruzKRmAjfO6zx7viL9y525k0wsyr97V/epf5AvUvMrKDAQYQ+YOUNa4WDVb0b//uA1h0micGVQp0z+7TdvPjzu0ztcXhA8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=vC5cu3Xq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="vC5cu3Xq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 09617C4CEF1; Fri, 12 Dec 2025 22:39:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765579192; bh=X1FzereqmkvvyyWDhOqhN108ue/fvWDPvdFvAdVnD88=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=vC5cu3XqX2Hs6WtMCl72r7to39Yl99kQUbwLA5+tJdu5ymE9srf4YLOcmK/e7lUKf Gi5x2HiFjLPt6mBS6o4lej+/A/l7rdG9s4LH88fpk331sv/nl5CvCsXvEmGFPQ6Tar ap1KfwJ5soQsKaFjwHt2lZ+yhzMA0fPpbf1GwsETwip5K4yBVo0U90Hzl2Cz+iflvh u8jdphj7yOygFVHxaBk29OOKQMyFwiklizOGxOPUg/fBjm6ocP4EyK5qLiJoGqKDWN EwD4UtScXkmaqA+pMiP5uo4zqHBPGnderXwbPlWG0ldPLwoa+KLq5r658Q7dPgA+a3 svJWNq2Nq8n1Q== From: Jeff Layton Date: Sat, 13 Dec 2025 07:39:17 +0900 Subject: [PATCH RFC 5/6] nfsd: adjust number of running nfsd threads based on activity Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251213-nfsd-dynathread-v1-5-de755e59cbc4@kernel.org> References: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> In-Reply-To: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=13016; i=jlayton@kernel.org; h=from:subject:message-id; bh=X1FzereqmkvvyyWDhOqhN108ue/fvWDPvdFvAdVnD88=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpPJmoayEQidBV0q+/kqx27IdjWfbzX+0u8wwkv /yMVlrXxUmJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaTyZqAAKCRAADmhBGVaC FcrbEACLIHbFz/A8b3rQtJ7Ftw9qDnz7hrRkT/wS+UIFwDP9u6oeOesz3aHqYCD/4AB0cR/aRy6 Lc/1z5FBdGHEwkmeVT0J05zOCe4sPtXwRO/k7/XZfwH2LwG0l0JTNLGkkMLw4z+ohQZVRuaaJDp /7Sw4EJ9TouvaHMOKQnM9iLB9D8hzRVavHahu6LuMMs0GoUwvvaaUqUgS/JBPQzc3MoIByZF1ki xKsIJAgMxzTC+JLxgj0w+aCf2Iadqg/R4lHhgbX19RFwFPs7t5RMbK6uF9PzG97h7LuKchx9hX9 pUYbWNhSpLTMufPxqTDFDZN5JWyCHJS2NhpqPmEg8bmpYHWlT0dJUki6FOa604A/Zs7vvCFBnzo elzZIGKIril/L3P5B5ljqGlaaQHIhyY6LaStmQ+YCiJc/L9zFH0sqdhckFl1deCEEyCLXGko3un vTUmh1T8lLAduWB8SyTbCXHh4Jd1Mes0r/aJ1a7hwElgloSC+XVnbsgYXZQwdJA8mYAKQg04DsX 3eRBIqp/BmHrw3ymdkxrV51fweRqP9mmYvyl/8HmjJgOM/Gu23rzFgzlu5bEQW4vE0ub5wxHEsH RpWYiCAOYn4cJYfpvVO4G6snAmJlaEDquVA4qLxk+evHMv24mRznRPxiaEmFm0qzg3rlIZyNljE PWRHf55XU5aOOXA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 This patch is based on a draft patch by Neil: svc_recv() is changed to return a status. This can be: -ETIMEDOUT - waited for 5 seconds and found nothing to do. This is boring. Also there are more actual threads than really needed. -EBUSY - I did something, but there is more stuff to do and no one idle who I can wake up to do it. BTW I successful set a flag: SP_TASK_STARTING. You better clear it. 0 - just minding my own business, nothing to see here. nfsd() is changed to pay attention to this status. In the case of -ETIMEDOUT, if the service mutex can be taken (trylock), the thread becomes and RQ_VICTIM so that it will exit. In the case of -EBUSY, if the actual number of threads is below the calculated maximum, a new thread is started. SP_TASK_STARTING is cleared. To support the above, some code is split out of svc_start_kthreads() into svc_new_thread(). I think we want memory pressure to be able to push a thread into returning -ETIMEDOUT. That can come later. Signed-off-by: NeilBrown Signed-off-by: Jeff Layton --- fs/nfsd/nfssvc.c | 35 ++++++++++++++++++++- fs/nfsd/trace.h | 35 +++++++++++++++++++++ include/linux/sunrpc/svc.h | 2 ++ include/linux/sunrpc/svcsock.h | 2 +- net/sunrpc/svc.c | 69 ++++++++++++++++++++++++--------------= ---- net/sunrpc/svc_xprt.c | 45 ++++++++++++++++++++++----- 6 files changed, 148 insertions(+), 40 deletions(-) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 993ed338764b0ccd7bdfb76bd6fbb5dc6ab4022d..26c3a6cb1f400f1b757d26f6ba7= 7e27deb7e8ee2 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -896,9 +896,11 @@ static int nfsd(void *vrqstp) { struct svc_rqst *rqstp =3D (struct svc_rqst *) vrqstp; + struct svc_pool *pool =3D rqstp->rq_pool; struct svc_xprt *perm_sock =3D list_entry(rqstp->rq_server->sv_permsocks.= next, typeof(struct svc_xprt), xpt_list); struct net *net =3D perm_sock->xpt_net; struct nfsd_net *nn =3D net_generic(net, nfsd_net_id); + bool have_mutex =3D false; =20 /* At this point, the thread shares current->fs * with the init process. We need to create files with the @@ -916,7 +918,36 @@ nfsd(void *vrqstp) * The main request loop */ while (!svc_thread_should_stop(rqstp)) { - svc_recv(rqstp); + switch (svc_recv(rqstp)) { + case -ETIMEDOUT: /* Nothing to do */ + if (mutex_trylock(&nfsd_mutex)) { + if (pool->sp_nrthreads > pool->sp_nrthrmin) { + trace_nfsd_dynthread_kill(net, pool); + set_bit(RQ_VICTIM, &rqstp->rq_flags); + have_mutex =3D true; + } else + mutex_unlock(&nfsd_mutex); + } else { + trace_nfsd_dynthread_trylock_fail(net, pool); + } + break; + case -EBUSY: /* Too much to do */ + if (pool->sp_nrthreads < pool->sp_nrthrmax && + mutex_trylock(&nfsd_mutex)) { + // check no idle threads? + if (pool->sp_nrthreads < pool->sp_nrthrmax) { + trace_nfsd_dynthread_start(net, pool); + svc_new_thread(rqstp->rq_server, pool); + } + mutex_unlock(&nfsd_mutex); + } else { + trace_nfsd_dynthread_trylock_fail(net, pool); + } + clear_bit(SP_TASK_STARTING, &pool->sp_flags); + break; + default: + break; + } nfsd_file_net_dispose(nn); } =20 @@ -924,6 +955,8 @@ nfsd(void *vrqstp) =20 /* Release the thread */ svc_exit_thread(rqstp); + if (have_mutex) + mutex_unlock(&nfsd_mutex); return 0; } =20 diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 5ae2a611e57f4b4e51a4d9eb6e0fccb66ad8d288..8885fd9bead98ebf55379d68ab9= c3701981a5150 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -91,6 +91,41 @@ DEFINE_EVENT(nfsd_xdr_err_class, nfsd_##name##_err, \ DEFINE_NFSD_XDR_ERR_EVENT(garbage_args); DEFINE_NFSD_XDR_ERR_EVENT(cant_encode); =20 +DECLARE_EVENT_CLASS(nfsd_dynthread_class, + TP_PROTO( + const struct net *net, + const struct svc_pool *pool + ), + TP_ARGS(net, pool), + TP_STRUCT__entry( + __field(unsigned int, netns_ino) + __field(unsigned int, pool_id) + __field(unsigned int, nrthreads) + __field(unsigned int, nrthrmin) + __field(unsigned int, nrthrmax) + ), + TP_fast_assign( + __entry->netns_ino =3D net->ns.inum; + __entry->pool_id =3D pool->sp_id; + __entry->nrthreads =3D pool->sp_nrthreads; + __entry->nrthrmin =3D pool->sp_nrthrmin; + __entry->nrthrmax =3D pool->sp_nrthrmax; + ), + TP_printk("pool=3D%u nrthreads=3D%u nrthrmin=3D%u nrthrmax=3D%u", + __entry->pool_id, __entry->nrthreads, + __entry->nrthrmin, __entry->nrthrmax + ) +); + +#define DEFINE_NFSD_DYNTHREAD_EVENT(name) \ +DEFINE_EVENT(nfsd_dynthread_class, nfsd_dynthread_##name, \ + TP_PROTO(const struct net *net, const struct svc_pool *pool), \ + TP_ARGS(net, pool)) + +DEFINE_NFSD_DYNTHREAD_EVENT(start); +DEFINE_NFSD_DYNTHREAD_EVENT(kill); +DEFINE_NFSD_DYNTHREAD_EVENT(trylock_fail); + #define show_nfsd_may_flags(x) \ __print_flags(x, "|", \ { NFSD_MAY_EXEC, "EXEC" }, \ diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 35bd3247764ae8dc5dcdfffeea36f7cfefd13372..f47e19c9bd9466986438766e9ab= 7b4c71cda1ba6 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -55,6 +55,7 @@ enum { SP_TASK_PENDING, /* still work to do even if no xprt is queued */ SP_NEED_VICTIM, /* One thread needs to agree to exit */ SP_VICTIM_REMAINS, /* One thread needs to actually exit */ + SP_TASK_STARTING, /* Task has started but not added to idle yet */ }; =20 =20 @@ -442,6 +443,7 @@ struct svc_serv *svc_create(struct svc_program *, unsig= ned int, bool svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page); void svc_rqst_release_pages(struct svc_rqst *rqstp); +int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int nprog, diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index de37069aba90899be19b1090e6e90e509a3cf530..5c87d3fedd33e7edf5ade32e605= 23cae7e9ebaba 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -61,7 +61,7 @@ static inline u32 svc_sock_final_rec(struct svc_sock *svs= k) /* * Function prototypes. */ -void svc_recv(struct svc_rqst *rqstp); +int svc_recv(struct svc_rqst *rqstp); void svc_send(struct svc_rqst *rqstp); int svc_addsock(struct svc_serv *serv, struct net *net, const int fd, char *name_return, const size_t len, diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index dc818158f8529b62dcf96c91bd9a9d4ab21df91f..9fca2dd340037f82baa4936766e= be0e38c3f0d85 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -714,9 +714,6 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_po= ol *pool, int node) =20 rqstp->rq_err =3D -EAGAIN; /* No error yet */ =20 - serv->sv_nrthreads +=3D 1; - pool->sp_nrthreads +=3D 1; - /* Protected by whatever lock the service uses when calling * svc_set_num_threads() */ @@ -763,45 +760,57 @@ void svc_pool_wake_idle_thread(struct svc_pool *pool) } EXPORT_SYMBOL_GPL(svc_pool_wake_idle_thread); =20 -static int -svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrser= vs) +int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool) { struct svc_rqst *rqstp; struct task_struct *task; int node; int err; =20 - do { - nrservs--; - node =3D svc_pool_map_get_node(pool->sp_id); - - rqstp =3D svc_prepare_thread(serv, pool, node); - if (!rqstp) - return -ENOMEM; - task =3D kthread_create_on_node(serv->sv_threadfn, rqstp, - node, "%s", serv->sv_name); - if (IS_ERR(task)) { - svc_exit_thread(rqstp); - return PTR_ERR(task); - } + node =3D svc_pool_map_get_node(pool->sp_id); =20 - rqstp->rq_task =3D task; - if (serv->sv_nrpools > 1) - svc_pool_map_set_cpumask(task, pool->sp_id); + rqstp =3D svc_prepare_thread(serv, pool, node); + if (!rqstp) + return -ENOMEM; + set_bit(SP_TASK_STARTING, &pool->sp_flags); + task =3D kthread_create_on_node(serv->sv_threadfn, rqstp, + node, "%s", serv->sv_name); + if (IS_ERR(task)) { + clear_bit(SP_TASK_STARTING, &pool->sp_flags); + svc_exit_thread(rqstp); + return PTR_ERR(task); + } =20 - svc_sock_update_bufs(serv); - wake_up_process(task); + serv->sv_nrthreads +=3D 1; + pool->sp_nrthreads +=3D 1; =20 - wait_var_event(&rqstp->rq_err, rqstp->rq_err !=3D -EAGAIN); - err =3D rqstp->rq_err; - if (err) { - svc_exit_thread(rqstp); - return err; - } - } while (nrservs > 0); + rqstp->rq_task =3D task; + if (serv->sv_nrpools > 1) + svc_pool_map_set_cpumask(task, pool->sp_id); =20 + svc_sock_update_bufs(serv); + wake_up_process(task); + + wait_var_event(&rqstp->rq_err, rqstp->rq_err !=3D -EAGAIN); + err =3D rqstp->rq_err; + if (err) { + svc_exit_thread(rqstp); + return err; + } return 0; } +EXPORT_SYMBOL_GPL(svc_new_thread); + +static int +svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrser= vs) +{ + int err =3D 0; + + while (!err && nrservs--) + err =3D svc_new_thread(serv, pool); + + return err; +} =20 static int svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrserv= s) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 6973184ff6675211b4338fac80105894e9c8d4df..9612334300c8dae38720a0f5c61= c0f505432ec2f 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -714,15 +714,22 @@ svc_thread_should_sleep(struct svc_rqst *rqstp) return true; } =20 -static void svc_thread_wait_for_work(struct svc_rqst *rqstp) +static bool nfsd_schedule_timeout(long timeout) +{ + return schedule_timeout(timeout) =3D=3D 0; +} + +static bool svc_thread_wait_for_work(struct svc_rqst *rqstp) { struct svc_pool *pool =3D rqstp->rq_pool; + bool did_timeout =3D false; =20 if (svc_thread_should_sleep(rqstp)) { set_current_state(TASK_IDLE | TASK_FREEZABLE); llist_add(&rqstp->rq_idle, &pool->sp_idle_threads); + clear_bit(SP_TASK_STARTING, &pool->sp_flags); if (likely(svc_thread_should_sleep(rqstp))) - schedule(); + did_timeout =3D nfsd_schedule_timeout(5 * HZ); =20 while (!llist_del_first_this(&pool->sp_idle_threads, &rqstp->rq_idle)) { @@ -734,7 +741,10 @@ static void svc_thread_wait_for_work(struct svc_rqst *= rqstp) * for this new work. This thread can safely sleep * until woken again. */ - schedule(); + if (did_timeout) + did_timeout =3D nfsd_schedule_timeout(HZ); + else + did_timeout =3D nfsd_schedule_timeout(5 * HZ); set_current_state(TASK_IDLE | TASK_FREEZABLE); } __set_current_state(TASK_RUNNING); @@ -742,6 +752,7 @@ static void svc_thread_wait_for_work(struct svc_rqst *r= qstp) cond_resched(); } try_to_freeze(); + return did_timeout; } =20 static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *= newxpt) @@ -825,6 +836,8 @@ static void svc_handle_xprt(struct svc_rqst *rqstp, str= uct svc_xprt *xprt) =20 static void svc_thread_wake_next(struct svc_rqst *rqstp) { + clear_bit(SP_TASK_STARTING, &rqstp->rq_pool->sp_flags); + if (!svc_thread_should_sleep(rqstp)) /* More work pending after I dequeued some, * wake another worker @@ -839,21 +852,31 @@ static void svc_thread_wake_next(struct svc_rqst *rqs= tp) * This code is carefully organised not to touch any cachelines in * the shared svc_serv structure, only cachelines in the local * svc_pool. + * + * Returns -ETIMEDOUT if idle for an extended period + * -EBUSY is there is more work to do than available threads + * 0 otherwise. */ -void svc_recv(struct svc_rqst *rqstp) +int svc_recv(struct svc_rqst *rqstp) { struct svc_pool *pool =3D rqstp->rq_pool; + bool did_wait; + int ret =3D 0; =20 if (!svc_alloc_arg(rqstp)) - return; + return ret; + + did_wait =3D svc_thread_wait_for_work(rqstp); =20 - svc_thread_wait_for_work(rqstp); + if (did_wait && svc_thread_should_sleep(rqstp) && + pool->sp_nrthrmin && (pool->sp_nrthreads > pool->sp_nrthrmin)) + ret =3D -ETIMEDOUT; =20 clear_bit(SP_TASK_PENDING, &pool->sp_flags); =20 if (svc_thread_should_stop(rqstp)) { svc_thread_wake_next(rqstp); - return; + return ret; } =20 rqstp->rq_xprt =3D svc_xprt_dequeue(pool); @@ -867,8 +890,13 @@ void svc_recv(struct svc_rqst *rqstp) */ if (pool->sp_idle_threads.first) rqstp->rq_chandle.thread_wait =3D 5 * HZ; - else + else { rqstp->rq_chandle.thread_wait =3D 1 * HZ; + if (!did_wait && + !test_and_set_bit(SP_TASK_STARTING, + &pool->sp_flags)) + ret =3D -EBUSY; + } =20 trace_svc_xprt_dequeue(rqstp); svc_handle_xprt(rqstp, xprt); @@ -887,6 +915,7 @@ void svc_recv(struct svc_rqst *rqstp) } } #endif + return ret; } EXPORT_SYMBOL_GPL(svc_recv); =20 --=20 2.52.0 From nobody Sun Dec 14 06:34:18 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 287DA2D543E; Fri, 12 Dec 2025 22:39:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579197; cv=none; b=GEFNgFln8hBGdf5dEaRDAfimD9HQmN6MEgOPb3FBHOiheZGXRZOwLFh+Z93h0velK8a/27P4aE+xoQQSFOqyboYSx6ITovVHx2pR1dGF5FTLtAHSBARQjQCvzP50DiUXKCfou22Lx9VdlVAcbIEvW4/0AVLJ7v8B6T9IzIKF0P0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765579197; c=relaxed/simple; bh=oycIXeGLB32L9C5BN6z7KGcYKHTK6L0OWD9Nr5s8M+I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aaoHTA+PcHglvPMgt506O9CibFb8AIohNrW/tOVlEBpPjyakWTFd9TmtueKKmR20Iq3ekXTRS7gmMSznVAJ2Vvujxh3GaT1ggEvXLvZsU7c3KDgtiA/hHNa27rxW31SyR7c3z7eBLdwRUfxHnNaJNOVdlY4wToYGck56HWbPGr0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WQhEd/KR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WQhEd/KR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AAF1EC113D0; Fri, 12 Dec 2025 22:39:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1765579194; bh=oycIXeGLB32L9C5BN6z7KGcYKHTK6L0OWD9Nr5s8M+I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=WQhEd/KRiB3HcvWu+P9akOVOTEf7mHrN9aBQJSq9TGNNxZ0rXeEtmHCcz/ijBxWeX X87711O5Y94oDGs73clXp9Brg+u5l1Zi0NRbPl2ZUWZwRAAJrdT9Jw/YJOR9NluzjH C5IMsImQmX2mr3tUYX42IKyaLxFaqfg5Spcv78a6Vek/u5aXR3pufnvmQ+wpwyjopQ lvqKiVAl3jj6iVa57iVdR9+qUuG0ilQ3Jz7NGZsoxd8Tagr+lbtYFR36JK5Thj2udh afXCUV2D1N2dJynyHJibvqc41SbSnW1AJTlx470oHRMzuwjmL6pYKd+jgBm3/DJ+ZN G6jx9bBJe80/g== From: Jeff Layton Date: Sat, 13 Dec 2025 07:39:18 +0900 Subject: [PATCH RFC 6/6] nfsd: add controls to set the minimum number of threads per pool Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251213-nfsd-dynathread-v1-6-de755e59cbc4@kernel.org> References: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> In-Reply-To: <20251213-nfsd-dynathread-v1-0-de755e59cbc4@kernel.org> To: Chuck Lever , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org, Jeff Layton X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=9944; i=jlayton@kernel.org; h=from:subject:message-id; bh=oycIXeGLB32L9C5BN6z7KGcYKHTK6L0OWD9Nr5s8M+I=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpPJmp96SfWUQhJCh4EIqXJJl4kyDY46gijsOMv QcXjkPojGKJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaTyZqQAKCRAADmhBGVaC FbUZD/0RfAjt6AIuH+aayGeCodZ53bxXLWRQzIxRJtsocO4UFp5JYx6PL2qwUXsKjKKFh1Cz7q9 RDyZLpq7mY4wQPdbwpurmRIeziee1QPcZnj8C419OnV6ng6HLG9nsDHCLgrWIwDCty/lZ+u95yV tYORbFVifVVB7SDWLWnzFWtrEkKvEo1Uu3lPPtG5O760KfuolMSIDSNtnuiUP91HvJ+9b8hjK0v Y2YvNmX1Sc/w0KK8MNeSCtWZhnLzCqRnMXLjr0VzZzbA+v5A+MK7j57f9Hk6aFNSSxzYKZOHdHw YiWiMDoPHc2BajLvVwGTtu4hZbiNi1vbNCQGp9oQP2T2qt0sRIfVgM9EnYi97aznKFq9zeQJgrF m6FgJP+LPXOWAfup9GXUAIaJNvCblwf/ST4x1+xbcs87BVIMKHMia2F+BQtMEi2mMUBy71HTkbh /2FX7vuuw8q1Pm03tNO53Ngcz/riQNuqzo6VICnbx+nQ3PGFzr49gqCjl53SqT4cnPKbK6SaGcz xPZqLG4AEQrPvnyHxhlq+2iBhJ6vRUpIhT5CGz4H4Jqq2A/fYs/bwkeFhMv/pgIhhfla0EaO54U 46FedCSOuxuRIDjQjpPJ+78CRwwgDyRDe2ItfA2ht2AKW6F//FaqUn0Bvvo9UYEXbkuW6LTX8np Xm0T7ak8iqjxOxw== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Add a new "min_threads" variable to the nfsd_net, along with the corresponding nfsdfs and netlink interfaces to set that value from userland. Pass that value to svc_set_pool_threads() and svc_set_num_threads(). Signed-off-by: Jeff Layton --- Documentation/netlink/specs/nfsd.yaml | 5 ++++ fs/nfsd/netlink.c | 5 ++-- fs/nfsd/netns.h | 6 +++++ fs/nfsd/nfsctl.c | 50 +++++++++++++++++++++++++++++++= ++++ fs/nfsd/nfssvc.c | 8 +++--- fs/nfsd/trace.h | 19 +++++++++++++ include/uapi/linux/nfsd_netlink.h | 1 + 7 files changed, 88 insertions(+), 6 deletions(-) diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/= specs/nfsd.yaml index 100363029e82aed87295e34a008ab771a95d508c..badb2fe57c9859c6932c621a589= da694782b0272 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -78,6 +78,9 @@ attribute-sets: - name: scope type: string + - + name: min-threads + type: u32 - name: version attributes: @@ -159,6 +162,7 @@ operations: - gracetime - leasetime - scope + - min-threads - name: threads-get doc: get the number of running threads @@ -170,6 +174,7 @@ operations: - gracetime - leasetime - scope + - min-threads - name: version-set doc: set nfs enabled versions diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index ac51a44e1065ec3f1d88165f70a831a828b58394..887525964451e640304371e33aa= 4f415b4ff2848 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -24,11 +24,12 @@ const struct nla_policy nfsd_version_nl_policy[NFSD_A_V= ERSION_ENABLED + 1] =3D { }; =20 /* NFSD_CMD_THREADS_SET - do */ -static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SC= OPE + 1] =3D { +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MI= N_THREADS + 1] =3D { [NFSD_A_SERVER_THREADS] =3D { .type =3D NLA_U32, }, [NFSD_A_SERVER_GRACETIME] =3D { .type =3D NLA_U32, }, [NFSD_A_SERVER_LEASETIME] =3D { .type =3D NLA_U32, }, [NFSD_A_SERVER_SCOPE] =3D { .type =3D NLA_NUL_STRING, }, + [NFSD_A_SERVER_MIN_THREADS] =3D { .type =3D NLA_U32, }, }; =20 /* NFSD_CMD_VERSION_SET - do */ @@ -57,7 +58,7 @@ static const struct genl_split_ops nfsd_nl_ops[] =3D { .cmd =3D NFSD_CMD_THREADS_SET, .doit =3D nfsd_nl_threads_set_doit, .policy =3D nfsd_threads_set_nl_policy, - .maxattr =3D NFSD_A_SERVER_SCOPE, + .maxattr =3D NFSD_A_SERVER_MIN_THREADS, .flags =3D GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 3e2d0fde80a7ce434ef2cce9f1666c2bd16ab2eb..1c3449810eaefea8167ddd284af= 7bd66cac7e211 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -128,6 +128,12 @@ struct nfsd_net { seqlock_t writeverf_lock; unsigned char writeverf[8]; =20 + /* + * Minimum number of threads to run per pool. If 0 then the + * min =3D=3D max requested number of threads. + */ + unsigned int min_threads; + u32 clientid_base; u32 clientid_counter; u32 clverifier_counter; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 206534fccf36a992026669fee6533adff1062c36..a5401015e62499d07150cde8822= f1e7dd0515dfe 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -48,6 +48,7 @@ enum { NFSD_Versions, NFSD_Ports, NFSD_MaxBlkSize, + NFSD_MinThreads, NFSD_Filecache, NFSD_Leasetime, NFSD_Gracetime, @@ -67,6 +68,7 @@ static ssize_t write_pool_threads(struct file *file, char= *buf, size_t size); static ssize_t write_versions(struct file *file, char *buf, size_t size); static ssize_t write_ports(struct file *file, char *buf, size_t size); static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); +static ssize_t write_minthreads(struct file *file, char *buf, size_t size); #ifdef CONFIG_NFSD_V4 static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t write_gracetime(struct file *file, char *buf, size_t size); @@ -85,6 +87,7 @@ static ssize_t (*const write_op[])(struct file *, char *,= size_t) =3D { [NFSD_Versions] =3D write_versions, [NFSD_Ports] =3D write_ports, [NFSD_MaxBlkSize] =3D write_maxblksize, + [NFSD_MinThreads] =3D write_minthreads, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] =3D write_leasetime, [NFSD_Gracetime] =3D write_gracetime, @@ -899,6 +902,46 @@ static ssize_t write_maxblksize(struct file *file, cha= r *buf, size_t size) nfsd_max_blksize); } =20 +/* + * write_minthreads - Set or report the current min number of threads + * + * Input: + * buf: ignored + * size: zero + * OR + * + * Input: + * buf: C string containing an unsigned + * integer value representing the new + * max number of threads + * size: non-zero length of C string in @buf + * Output: + * On success: passed-in buffer filled with '\n'-terminated C string + * containing numeric value of min_threads setting + * for this net namespace; + * 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_minthreads(struct file *file, char *buf, size_t size) +{ + char *mesg =3D buf; + struct nfsd_net *nn =3D net_generic(netns(file), nfsd_net_id); + unsigned int minthreads =3D nn->min_threads; + + if (size > 0) { + int rv =3D get_uint(&mesg, &minthreads); + + if (rv) + return rv; + trace_nfsd_ctl_minthreads(netns(file), minthreads); + mutex_lock(&nfsd_mutex); + nn->min_threads =3D minthreads; + mutex_unlock(&nfsd_mutex); + } + + return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%u\n", minthreads); +} + #ifdef CONFIG_NFSD_V4 static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t siz= e, time64_t *time, struct nfsd_net *nn) @@ -1292,6 +1335,7 @@ static int nfsd_fill_super(struct super_block *sb, st= ruct fs_context *fc) [NFSD_Versions] =3D {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Ports] =3D {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_MaxBlkSize] =3D {"max_block_size", &transaction_ops, S_IWUSR|S_IRU= GO}, + [NFSD_MinThreads] =3D {"min_threads", &transaction_ops, S_IWUSR|S_IRUGO}, [NFSD_Filecache] =3D {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO}, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] =3D {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUS= R}, @@ -1636,6 +1680,10 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, st= ruct genl_info *info) scope =3D nla_data(attr); } =20 + attr =3D info->attrs[NFSD_A_SERVER_MIN_THREADS]; + if (attr) + nn->min_threads =3D nla_get_u32(attr); + ret =3D nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope); if (ret > 0) ret =3D 0; @@ -1675,6 +1723,8 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, str= uct genl_info *info) nn->nfsd4_grace) || nla_put_u32(skb, NFSD_A_SERVER_LEASETIME, nn->nfsd4_lease) || + nla_put_u32(skb, NFSD_A_SERVER_MIN_THREADS, + nn->min_threads) || nla_put_string(skb, NFSD_A_SERVER_SCOPE, nn->nfsd_name); if (err) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 26c3a6cb1f400f1b757d26f6ba77e27deb7e8ee2..d6120dd843ac1b6a42f0ef33170= 0f4d6d70d8c38 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -594,7 +594,7 @@ void nfsd_shutdown_threads(struct net *net) } =20 /* Kill outstanding nfsd threads */ - svc_set_num_threads(serv, 0, 0); + svc_set_num_threads(serv, 0, nn->min_threads); nfsd_destroy_serv(net); mutex_unlock(&nfsd_mutex); } @@ -704,7 +704,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net= *net) =20 /* Special case: When n =3D=3D 1, distribute threads equally among pools.= */ if (n =3D=3D 1) - return svc_set_num_threads(nn->nfsd_serv, nthreads[0], 0); + return svc_set_num_threads(nn->nfsd_serv, nthreads[0], nn->min_threads); =20 if (n > nn->nfsd_serv->sv_nrpools) n =3D nn->nfsd_serv->sv_nrpools; @@ -732,7 +732,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net= *net) for (i =3D 0; i < n; i++) { err =3D svc_set_pool_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i], - nthreads[i], 0); + nthreads[i], nn->min_threads); if (err) goto out; } @@ -741,7 +741,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net= *net) for (i =3D n; i < nn->nfsd_serv->sv_nrpools; ++i) { err =3D svc_set_pool_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i], - 0, 0); + 0, nn->min_threads); if (err) goto out; } diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 8885fd9bead98ebf55379d68ab9c3701981a5150..d1d0b0dd054588a8c20e3386356= dfa4e9632b8e0 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -2164,6 +2164,25 @@ TRACE_EVENT(nfsd_ctl_maxblksize, ) ); =20 +TRACE_EVENT(nfsd_ctl_minthreads, + TP_PROTO( + const struct net *net, + int minthreads + ), + TP_ARGS(net, minthreads), + TP_STRUCT__entry( + __field(unsigned int, netns_ino) + __field(int, minthreads) + ), + TP_fast_assign( + __entry->netns_ino =3D net->ns.inum; + __entry->minthreads =3D minthreads + ), + TP_printk("minthreads=3D%d", + __entry->minthreads + ) +); + TRACE_EVENT(nfsd_ctl_time, TP_PROTO( const struct net *net, diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_ne= tlink.h index e157e2009ea8c1ef805301261d536c82677821ef..e9efbc9e63d83ed25fcd790b7a8= 77c0023638f15 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -35,6 +35,7 @@ enum { NFSD_A_SERVER_GRACETIME, NFSD_A_SERVER_LEASETIME, NFSD_A_SERVER_SCOPE, + NFSD_A_SERVER_MIN_THREADS, =20 __NFSD_A_SERVER_MAX, NFSD_A_SERVER_MAX =3D (__NFSD_A_SERVER_MAX - 1) --=20 2.52.0