[pnfs] [PATCH 1/2] nfs41: Add a reference to svc_serv during callback service bring up

Benny Halevy bhalevy at panasas.com
Wed Mar 5 02:53:15 EST 2008


Ricardo Labiaga wrote:
> From: Ricardo Labiaga <Ricardo.Labiaga at netapp.com>
> 
> There is only one NFSv4.1 callback service.  The first caller of
> nfs4_callback_up() creates the service, subsequent callers increment a
> reference count on the service.  The service is destroyed when the last
> caller invokes nfs_callback_down().
> 
> The transport needs to hold a reference to the callback service in order
> to invoke it during callback processing.  Currently this reference is only
> obtained when the service is first created.  This is incorrect, since
> subsequent registrations for other transports will leave the xprt->serv
> pointer uninitialized, leading to an oops when a callback arrives on
> the "unreferenced" transport.
> 
> This patch fixes the problem by ensuring that a reference to the service
> is saved in xprt->serv, either because the service is created by this
> invocation to nfs4_callback_up() or by a prior invocation.
> 
> Signed-off-by: Ricardo Labiaga <Ricardo.Labiaga at netapp.com>
> ---
>  fs/nfs/callback.c |   22 ++++++++++++++++------
>  1 files changed, 16 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
> index e6a55a6..4afd42c 100644
> --- a/fs/nfs/callback.c
> +++ b/fs/nfs/callback.c
> @@ -186,12 +186,6 @@ int nfs4_callback_up(struct svc_serv *serv)
>   */
>  int nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
>  {
> -	/*
> -	 * Save the svc_serv in the transport so that it can
> -	 * be referenced when the session backchannel is initialized
> -	 */
> -	xprt->serv = serv;
> -

The xprt parameter is no longer used and can be removed.

>  	INIT_LIST_HEAD(&serv->sv_cb_list);
>  	spin_lock_init(&serv->sv_cb_lock);
>  	init_waitqueue_head(&serv->sv_cb_waitq);
> @@ -236,6 +230,22 @@ int nfs_callback_up(int minorversion, void *args)
>  	nfs_callback_info.serv = serv;
>  	wait_for_completion(&nfs_callback_info.started);
>  out:
> +	if (ret == 0) {
> +		switch (minorversion) {

The surrounding logic seem to be a bit overly complex for a single statement
that needs to be done for minorversion 1.  How about a simple if () statement
rather than the switch?

> +		case 0:
> +			break;
> +#if defined(CONFIG_NFS_V4_1)
> +		case 1:
> +			/*
> +			 * Save the svc_serv in the transport so that it can
> +			 * be referenced when the session backchannel is
> +			 * initialized and when callbacks arrive.
> +			 */
> +			xprt->serv = nfs_callback_info.serv;
> +			break;
> +#endif /* CONFIG_NFS_V4_1 */
> +		}
> +	}

Also, you can define a new label here, say "out_common:", to be gone to
from the out_err path rather than goto out to save "if (ret) { }"

>  	mutex_unlock(&nfs_callback_mutex);
>  	unlock_kernel();
>  	return ret;

BTW, looking at this function I don't see how it's supposed to work when mounting
over both minor versions.  We call either nfs4{,1}_callback_up first time around
and the other is never called after the callback service is created.
This needs to be re-written so we can bring either both threads up and down
together or independently on-demand maybe by having two copies of nfs_callback_info,
one for each minor version.  In which case I think they can share the service
which is created once in nfs_callback_up and never destroyed.

One more todo item, while I'm at it is to replace the void *args argument
to nfs_callback_up() with struct rpc_xprt *xprt since it's always passed
by nfs4_init_client()

Benny


More information about the pNFS mailing list