Hi Neil-
The clean-up of NFSD thread management has been merged into v5.17-rc.
I'm wondering if you have thought more about how to implement dynamic
management.
One thing that occurred to me was that there was a changeset that
attempted to convert NFSD threads into work queues. We merged some
of that but stopped when it was found that work queue scheduling
seems to have some worst case behavior that sometimes impacts the
performance of the Linux NFS server.
The part of that work that was merged is now vestigial, and might
need to be reverted before proceeding with dynamic NFSD thread
management. For example:
476 void svc_xprt_enqueue(struct svc_xprt *xprt)
477 {
478 if (test_bit(XPT_BUSY, &xprt->xpt_flags))
479 return;
480 xprt->xpt_server->sv_ops->svo_enqueue_xprt(xprt);
481 }
482 EXPORT_SYMBOL_GPL(svc_xprt_enqueue);
The svo_enqueue_xprt method was to be different for the different
scheduling mechanisms. But to this day there is only one method
defined: svc_xprt_do_enqueue()
Do you have plans to use this or discard it? If not, I'd like to
see that it is removed eventually, as virtual functions are
more costly than they used to be thanks to Spectre/Meltdown, and
this one is invoked multiple times per RPC.
--
Chuck Lever
On Tue, 25 Jan 2022, Chuck Lever III wrote:
> Hi Neil-
>
> The clean-up of NFSD thread management has been merged into v5.17-rc.
> I'm wondering if you have thought more about how to implement dynamic
> management.
I have some scraps of code but nothing much.
My plan is to have threads exit after they have been around for "a
while" and have new threads be created when there is "apparent need".
I think there always needs to be at least one thread (as thread creation
can fail, or block for a long time), but if there are > 1 and a thread
finds that it has nothing immediately to do, and it has been over (e.g.)
5 minutes since it was created, then it exits. So there will always be
gentle downward pressure on the number of threads.
When data arrives on a transport and there is no thread available to
process it, then we need to consider starting a new thread. This is by
far the most interesting part. I think we want a rate limit as a
function of the number of threads already existing.
e.g.:
if there are fewer than "nr_threads" threads, then start a thread.
if there are between "nr_threads" and "8*nr_threads", then schedule a thread to
start in 10ms unless that has already been scheduled
if there are more than "8*nr_threads", then don't start new threads
Obviously there are some magic numbers in there that need to be
justified, and I don't have any justification as yet.
The "10ms" should ideally be related to the typical request-processing
time (mean? max? mid-range?)
The "8*nr_threads" should probably be separately configurable, with
something like that value as a default.
So with the default of 4 (or 8?) threads, the count will quickly grow to
that number if there is any load, then slowly grow beyond that as load
persists.
It would be nice to find a way to measure if the nfsd threads are
overloading the system in some way, but I haven't yet thought about what
that might mean.
>
> One thing that occurred to me was that there was a changeset that
> attempted to convert NFSD threads into work queues. We merged some
> of that but stopped when it was found that work queue scheduling
> seems to have some worst case behavior that sometimes impacts the
> performance of the Linux NFS server.
>
> The part of that work that was merged is now vestigial, and might
> need to be reverted before proceeding with dynamic NFSD thread
> management. For example:
>
> 476 void svc_xprt_enqueue(struct svc_xprt *xprt)
> 477 {
> 478 if (test_bit(XPT_BUSY, &xprt->xpt_flags))
> 479 return;
> 480 xprt->xpt_server->sv_ops->svo_enqueue_xprt(xprt);
> 481 }
> 482 EXPORT_SYMBOL_GPL(svc_xprt_enqueue);
>
> The svo_enqueue_xprt method was to be different for the different
> scheduling mechanisms. But to this day there is only one method
> defined: svc_xprt_do_enqueue()
>
> Do you have plans to use this or discard it? If not, I'd like to
> see that it is removed eventually, as virtual functions are
> more costly than they used to be thanks to Spectre/Meltdown, and
> this one is invoked multiple times per RPC.
I think this can go - I see no use for it.
I suspect svo_module can go as well - I don't think the thread is ever
the thing that primarily keeps a module active.
svo_shutdown can go too. any code that calls svc_shutdown_net() knows
what the shutdown function should be, and so can call it directly.
svo_function is useful, but as it is the only thing in sv_ops that is
needed, we can drop the sv_ops structure and just have a pointer to the
svo_function directly in the svc_serv.
I'm happy to provide patches for all this if you like.
NeilBrown