Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43d78ef2 authored by Chuck Lever's avatar Chuck Lever Committed by Trond Myklebust
Browse files

NFS: disconnect before retrying NFSv4 requests over TCP

RFC3530 section 3.1.1 states an NFSv4 client MUST NOT send a request
twice on the same connection unless it is the NULL procedure.  Section
3.1.1 suggests that the client should disconnect and reconnect if it
wants to retry a request.

Implement this by adding an rpc_clnt flag that an ULP can use to
specify that the underlying transport should be disconnected on a
major timeout.  The NFSv4 client asserts this new flag, and requests
no retries after a minor retransmit timeout.

Note that disconnecting on a retransmit is in general not safe to do
if the RPC client does not reuse the TCP port number when reconnecting.

See http://bugzilla.linux-nfs.org/show_bug.cgi?id=6



Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent a301b777
Loading
Loading
Loading
Loading
+6 −3
Original line number Original line Diff line number Diff line
@@ -394,7 +394,8 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
static int nfs_create_rpc_client(struct nfs_client *clp, int proto,
static int nfs_create_rpc_client(struct nfs_client *clp, int proto,
						unsigned int timeo,
						unsigned int timeo,
						unsigned int retrans,
						unsigned int retrans,
						rpc_authflavor_t flavor)
						rpc_authflavor_t flavor,
						int flags)
{
{
	struct rpc_timeout	timeparms;
	struct rpc_timeout	timeparms;
	struct rpc_clnt		*clnt = NULL;
	struct rpc_clnt		*clnt = NULL;
@@ -407,6 +408,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp, int proto,
		.program	= &nfs_program,
		.program	= &nfs_program,
		.version	= clp->rpc_ops->version,
		.version	= clp->rpc_ops->version,
		.authflavor	= flavor,
		.authflavor	= flavor,
		.flags		= flags,
	};
	};


	if (!IS_ERR(clp->cl_rpcclient))
	if (!IS_ERR(clp->cl_rpcclient))
@@ -548,7 +550,7 @@ static int nfs_init_client(struct nfs_client *clp, const struct nfs_mount_data *
	 * - RFC 2623, sec 2.3.2
	 * - RFC 2623, sec 2.3.2
	 */
	 */
	error = nfs_create_rpc_client(clp, proto, data->timeo, data->retrans,
	error = nfs_create_rpc_client(clp, proto, data->timeo, data->retrans,
			RPC_AUTH_UNIX);
					RPC_AUTH_UNIX, 0);
	if (error < 0)
	if (error < 0)
		goto error;
		goto error;
	nfs_mark_client_ready(clp, NFS_CS_READY);
	nfs_mark_client_ready(clp, NFS_CS_READY);
@@ -868,7 +870,8 @@ static int nfs4_init_client(struct nfs_client *clp,
	/* Check NFS protocol revision and initialize RPC op vector */
	/* Check NFS protocol revision and initialize RPC op vector */
	clp->rpc_ops = &nfs_v4_clientops;
	clp->rpc_ops = &nfs_v4_clientops;


	error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour);
	error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour,
					RPC_CLNT_CREATE_DISCRTRY);
	if (error < 0)
	if (error < 0)
		goto error;
		goto error;
	memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
	memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
+2 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ struct rpc_clnt {


	unsigned int		cl_softrtry : 1,/* soft timeouts */
	unsigned int		cl_softrtry : 1,/* soft timeouts */
				cl_intr     : 1,/* interruptible */
				cl_intr     : 1,/* interruptible */
				cl_discrtry : 1,/* disconnect before retry */
				cl_autobind : 1,/* use getport() */
				cl_autobind : 1,/* use getport() */
				cl_oneshot  : 1,/* dispose after use */
				cl_oneshot  : 1,/* dispose after use */
				cl_dead     : 1;/* abandoned */
				cl_dead     : 1;/* abandoned */
@@ -111,6 +112,7 @@ struct rpc_create_args {
#define RPC_CLNT_CREATE_ONESHOT		(1UL << 3)
#define RPC_CLNT_CREATE_ONESHOT		(1UL << 3)
#define RPC_CLNT_CREATE_NONPRIVPORT	(1UL << 4)
#define RPC_CLNT_CREATE_NONPRIVPORT	(1UL << 4)
#define RPC_CLNT_CREATE_NOPING		(1UL << 5)
#define RPC_CLNT_CREATE_NOPING		(1UL << 5)
#define RPC_CLNT_CREATE_DISCRTRY	(1UL << 6)


struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt	*rpc_bind_new_program(struct rpc_clnt *,
struct rpc_clnt	*rpc_bind_new_program(struct rpc_clnt *,
+2 −0
Original line number Original line Diff line number Diff line
@@ -249,6 +249,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
		clnt->cl_autobind = 1;
		clnt->cl_autobind = 1;
	if (args->flags & RPC_CLNT_CREATE_ONESHOT)
	if (args->flags & RPC_CLNT_CREATE_ONESHOT)
		clnt->cl_oneshot = 1;
		clnt->cl_oneshot = 1;
	if (args->flags & RPC_CLNT_CREATE_DISCRTRY)
		clnt->cl_discrtry = 1;


	return clnt;
	return clnt;
}
}
+10 −0
Original line number Original line Diff line number Diff line
@@ -735,6 +735,16 @@ void xprt_transmit(struct rpc_task *task)
			xprt_reset_majortimeo(req);
			xprt_reset_majortimeo(req);
			/* Turn off autodisconnect */
			/* Turn off autodisconnect */
			del_singleshot_timer_sync(&xprt->timer);
			del_singleshot_timer_sync(&xprt->timer);
		} else {
			/* If all request bytes have been sent,
			 * then we must be retransmitting this one */
			if (!req->rq_bytes_sent) {
				if (task->tk_client->cl_discrtry) {
					xprt_disconnect(xprt);
					task->tk_status = -ENOTCONN;
					return;
				}
			}
		}
		}
	} else if (!req->rq_bytes_sent)
	} else if (!req->rq_bytes_sent)
		return;
		return;