On Wed, 26 Jan 2022, Chuck Lever wrote: > We have never been able to track down and address the underlying > cause of the performance issues with workqueue-based service > support. svo_enqueue_xprt is called multiple times per RPC, so > it adds instruction path length, but always ends up at the same > function: svc_xprt_do_enqueue(). We do not anticipate needing > this flexibility for dynamic nfsd thread management support. > > As a micro-optimization, remove .svo_enqueue_xprt because > Spectre/Meltdown makes virtual function calls more costly. > > This change essentially reverts commit b9e13cdfac70 ("nfsd/sunrpc: > turn enqueueing a svc_xprt into a svc_serv operation"). > > Signed-off-by: Chuck Lever <chuck.lever@xxxxxxxxxx> > --- > fs/lockd/svc.c | 1 - > fs/nfs/callback.c | 2 -- > fs/nfsd/nfssvc.c | 1 - > include/linux/sunrpc/svc.h | 3 --- > include/linux/sunrpc/svc_xprt.h | 1 - > net/sunrpc/svc_xprt.c | 10 +++++----- > 6 files changed, 5 insertions(+), 13 deletions(-) > > diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c > index 0475c5a5d061..3a05af873625 100644 > --- a/fs/lockd/svc.c > +++ b/fs/lockd/svc.c > @@ -353,7 +353,6 @@ static struct notifier_block lockd_inet6addr_notifier = { > static const struct svc_serv_ops lockd_sv_ops = { > .svo_shutdown = svc_rpcb_cleanup, > .svo_function = lockd, > - .svo_enqueue_xprt = svc_xprt_do_enqueue, > .svo_module = THIS_MODULE, > }; > > diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c > index 054cc1255fac..7a810f885063 100644 > --- a/fs/nfs/callback.c > +++ b/fs/nfs/callback.c > @@ -234,13 +234,11 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, > > static const struct svc_serv_ops nfs40_cb_sv_ops = { > .svo_function = nfs4_callback_svc, > - .svo_enqueue_xprt = svc_xprt_do_enqueue, > .svo_module = THIS_MODULE, > }; > #if defined(CONFIG_NFS_V4_1) > static const struct svc_serv_ops nfs41_cb_sv_ops = { > .svo_function = nfs41_callback_svc, > - .svo_enqueue_xprt = svc_xprt_do_enqueue, > .svo_module = THIS_MODULE, > }; > > diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c > index b8c682b62d29..aeeac6de1f0a 100644 > --- a/fs/nfsd/nfssvc.c > +++ b/fs/nfsd/nfssvc.c > @@ -615,7 +615,6 @@ static int nfsd_get_default_max_blksize(void) > static const struct svc_serv_ops nfsd_thread_sv_ops = { > .svo_shutdown = nfsd_last_thread, > .svo_function = nfsd, > - .svo_enqueue_xprt = svc_xprt_do_enqueue, > .svo_module = THIS_MODULE, > }; > > diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h > index f35c22b3355f..6ef9c1cafd0b 100644 > --- a/include/linux/sunrpc/svc.h > +++ b/include/linux/sunrpc/svc.h > @@ -61,9 +61,6 @@ struct svc_serv_ops { > /* function for service threads to run */ > int (*svo_function)(void *); > > - /* queue up a transport for servicing */ > - void (*svo_enqueue_xprt)(struct svc_xprt *); > - > /* optional module to count when adding threads. > * Thread function must call module_put_and_kthread_exit() to exit. > */ > diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h > index 571f605bc91e..a3ba027fb4ba 100644 > --- a/include/linux/sunrpc/svc_xprt.h > +++ b/include/linux/sunrpc/svc_xprt.h > @@ -131,7 +131,6 @@ int svc_create_xprt(struct svc_serv *, const char *, struct net *, > const int, const unsigned short, int, > const struct cred *); > void svc_xprt_received(struct svc_xprt *xprt); > -void svc_xprt_do_enqueue(struct svc_xprt *xprt); > void svc_xprt_enqueue(struct svc_xprt *xprt); > void svc_xprt_put(struct svc_xprt *xprt); > void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt); > diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c > index b21ad7994147..9fce4f7774bb 100644 > --- a/net/sunrpc/svc_xprt.c > +++ b/net/sunrpc/svc_xprt.c > @@ -32,6 +32,7 @@ static int svc_deferred_recv(struct svc_rqst *rqstp); > static struct cache_deferred_req *svc_defer(struct cache_req *req); > static void svc_age_temp_xprts(struct timer_list *t); > static void svc_delete_xprt(struct svc_xprt *xprt); > +static void svc_xprt_do_enqueue(struct svc_xprt *xprt); > > /* apparently the "standard" is that clients close > * idle connections after 5 minutes, servers after > @@ -266,12 +267,12 @@ void svc_xprt_received(struct svc_xprt *xprt) > } > > /* As soon as we clear busy, the xprt could be closed and > - * 'put', so we need a reference to call svc_enqueue_xprt with: > + * 'put', so we need a reference to call svc_xprt_do_enqueue with: > */ > svc_xprt_get(xprt); > smp_mb__before_atomic(); > clear_bit(XPT_BUSY, &xprt->xpt_flags); > - xprt->xpt_server->sv_ops->svo_enqueue_xprt(xprt); > + svc_xprt_do_enqueue(xprt); > svc_xprt_put(xprt); > } > EXPORT_SYMBOL_GPL(svc_xprt_received); > @@ -423,7 +424,7 @@ static bool svc_xprt_ready(struct svc_xprt *xprt) > return false; > } > > -void svc_xprt_do_enqueue(struct svc_xprt *xprt) > +static void svc_xprt_do_enqueue(struct svc_xprt *xprt) > { > struct svc_pool *pool; > struct svc_rqst *rqstp = NULL; > @@ -467,7 +468,6 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt) > put_cpu(); > trace_svc_xprt_enqueue(xprt, rqstp); > } > -EXPORT_SYMBOL_GPL(svc_xprt_do_enqueue); > > /* > * Queue up a transport with data pending. If there are idle nfsd > @@ -478,7 +478,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt) > { > if (test_bit(XPT_BUSY, &xprt->xpt_flags)) > return; > - xprt->xpt_server->sv_ops->svo_enqueue_xprt(xprt); > + svc_xprt_do_enqueue(xprt); > } > EXPORT_SYMBOL_GPL(svc_xprt_enqueue); > I obviously like this, but I think we can do better - merge svc_xprt_enqueue and svc_xprt_do_enqueue, and drop the extra test on XPT_BUSY. They were separated in Commit 0971374e2818 ("SUNRPC: Reduce contention in svc_xprt_enqueue()") so that the XPT_BUSY check happened before taking any spinlocks. We have since moved or removed the spinlocks so the extra test is fairly pointless. Possibly we could add if (xpt_flags & BIT(XPR_BUSY)) return false; at the start of svc_xprt_ready(). It would make sense there and be close to free. Thanks, NeilBrown