Re: [PATCH v1 1/2] SUNRPC: Remove the .svo_enqueue_xprt method

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux