Commit d4bea820 authored by Trond Myklebust's avatar Trond Myklebust

Don't use an RPC child process when reconnecting to a TCP server.

This is more efficient, and also fixes an existing deadlock
situation in which the child could be waiting for an xprt_write_lock
that was being held by the parent.
parent a92694eb
...@@ -57,8 +57,7 @@ static void call_refresh(struct rpc_task *task); ...@@ -57,8 +57,7 @@ static void call_refresh(struct rpc_task *task);
static void call_refreshresult(struct rpc_task *task); static void call_refreshresult(struct rpc_task *task);
static void call_timeout(struct rpc_task *task); static void call_timeout(struct rpc_task *task);
static void call_connect(struct rpc_task *task); static void call_connect(struct rpc_task *task);
static void child_connect(struct rpc_task *task); static void call_connect_status(struct rpc_task *task);
static void child_connect_status(struct rpc_task *task);
static u32 * call_header(struct rpc_task *task); static u32 * call_header(struct rpc_task *task);
static u32 * call_verify(struct rpc_task *task); static u32 * call_verify(struct rpc_task *task);
...@@ -602,40 +601,48 @@ static void ...@@ -602,40 +601,48 @@ static void
call_connect(struct rpc_task *task) call_connect(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
struct rpc_task *child;
dprintk("RPC: %4d call_connect status %d\n", dprintk("RPC: %4d call_connect status %d\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
if (!clnt->cl_xprt->stream) {
task->tk_action = call_transmit; task->tk_action = call_transmit;
if (task->tk_status < 0 || !clnt->cl_xprt->stream)
return; return;
/* Run as a child to ensure it runs as an rpciod task. Rpciod
* guarantees we have the correct capabilities for socket bind
* to succeed. */
child = rpc_new_child(clnt, task);
if (child) {
child->tk_action = child_connect;
rpc_run_child(task, child, NULL);
} }
task->tk_action = call_connect_status;
if (task->tk_status < 0)
return;
xprt_connect(task);
} }
/*
* 4b. Sort out connect result
*/
static void static void
child_connect(struct rpc_task *task) call_connect_status(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client;
int status = task->tk_status;
task->tk_status = 0; task->tk_status = 0;
task->tk_action = child_connect_status; if (status >= 0) {
xprt_connect(task); clnt->cl_stats->netreconn++;
} task->tk_action = call_transmit;
return;
}
static void /* Something failed: we may have to rebind */
child_connect_status(struct rpc_task *task) if (clnt->cl_autobind)
{ clnt->cl_port = 0;
if (task->tk_status == -EAGAIN) switch (status) {
task->tk_action = child_connect; case -ENOTCONN:
else case -ETIMEDOUT:
task->tk_action = NULL; case -EAGAIN:
task->tk_action = (clnt->cl_port == 0) ? call_bind : call_connect;
break;
default:
rpc_exit(task, -EIO);
}
} }
/* /*
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); static void xprt_request_init(struct rpc_task *, struct rpc_xprt *);
static inline void do_xprt_reserve(struct rpc_task *); static inline void do_xprt_reserve(struct rpc_task *);
static void xprt_disconnect(struct rpc_xprt *); static void xprt_disconnect(struct rpc_xprt *);
static void xprt_conn_status(struct rpc_task *task); static void xprt_connect_status(struct rpc_task *task);
static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap, static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap,
struct rpc_timeout *to); struct rpc_timeout *to);
static struct socket *xprt_create_socket(int, struct rpc_timeout *, int); static struct socket *xprt_create_socket(int, struct rpc_timeout *, int);
...@@ -406,7 +406,6 @@ xprt_disconnect(struct rpc_xprt *xprt) ...@@ -406,7 +406,6 @@ xprt_disconnect(struct rpc_xprt *xprt)
/* /*
* Attempt to connect a TCP socket. * Attempt to connect a TCP socket.
* *
* NB: This never collides with TCP reads, as both run from rpciod
*/ */
void void
xprt_connect(struct rpc_task *task) xprt_connect(struct rpc_task *task)
...@@ -454,6 +453,9 @@ xprt_connect(struct rpc_task *task) ...@@ -454,6 +453,9 @@ xprt_connect(struct rpc_task *task)
dprintk("RPC: %4d connect status %d connected %d sock state %d\n", dprintk("RPC: %4d connect status %d connected %d sock state %d\n",
task->tk_pid, -status, xprt_connected(xprt), inet->state); task->tk_pid, -status, xprt_connected(xprt), inet->state);
if (status >= 0)
return;
switch (status) { switch (status) {
case -EINPROGRESS: case -EINPROGRESS:
case -EALREADY: case -EALREADY:
...@@ -466,53 +468,37 @@ xprt_connect(struct rpc_task *task) ...@@ -466,53 +468,37 @@ xprt_connect(struct rpc_task *task)
/* if the socket is already closing, delay briefly */ /* if the socket is already closing, delay briefly */
if ((1 << inet->state) & ~(TCPF_SYN_SENT|TCPF_SYN_RECV)) if ((1 << inet->state) & ~(TCPF_SYN_SENT|TCPF_SYN_RECV))
task->tk_timeout = RPC_REESTABLISH_TIMEOUT; task->tk_timeout = RPC_REESTABLISH_TIMEOUT;
rpc_sleep_on(&xprt->pending, task, xprt_conn_status, rpc_sleep_on(&xprt->pending, task, xprt_connect_status,
NULL); NULL);
release_sock(inet);
/* task status set when task wakes up again */
return;
} }
release_sock(inet); release_sock(inet);
task->tk_status = 0;
break;
case 0:
case -EISCONN: /* not likely, but just in case */
/* Half closed state. No race -- this socket is dead. */
if (inet->state != TCP_ESTABLISHED) {
xprt_close(xprt);
task->tk_status = -EAGAIN;
goto out_write;
}
/* Otherwise, the connection is already established. */
task->tk_status = 0;
break; break;
case -ECONNREFUSED:
case -EPIPE: case -ENOTCONN:
xprt_close(xprt); case -ENETUNREACH:
if (!task->tk_client->cl_softrtry) {
rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
task->tk_status = -ENOTCONN; task->tk_status = -ENOTCONN;
goto out_write; break;
}
default: default:
/* Report myriad other possible returns. If this file /* Report myriad other possible returns. If this file
* system is soft mounted, just error out, like Solaris. */ * system is soft mounted, just error out, like Solaris. */
xprt_close(xprt);
if (task->tk_client->cl_softrtry) { if (task->tk_client->cl_softrtry) {
printk(KERN_WARNING printk(KERN_WARNING
"RPC: error %d connecting to server %s, exiting\n", "RPC: error %d connecting to server %s, exiting\n",
-status, task->tk_client->cl_server); -status, task->tk_client->cl_server);
task->tk_status = -EIO; task->tk_status = -EIO;
} else { goto out_write;
printk(KERN_WARNING }
"RPC: error %d connecting to server %s\n", printk(KERN_WARNING "RPC: error %d connecting to server %s\n",
-status, task->tk_client->cl_server); -status, task->tk_client->cl_server);
/* This will prevent anybody else from reconnecting */
rpc_delay(task, RPC_REESTABLISH_TIMEOUT); rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
task->tk_status = status; task->tk_status = status;
}
break; break;
} }
return;
out_write: out_write:
xprt_release_write(xprt, task); xprt_release_write(xprt, task);
} }
...@@ -521,33 +507,32 @@ xprt_connect(struct rpc_task *task) ...@@ -521,33 +507,32 @@ xprt_connect(struct rpc_task *task)
* We arrive here when awoken from waiting on connection establishment. * We arrive here when awoken from waiting on connection establishment.
*/ */
static void static void
xprt_conn_status(struct rpc_task *task) xprt_connect_status(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_xprt;
switch (task->tk_status) { if (task->tk_status >= 0) {
case 0: dprintk("RPC: %4d xprt_connect_status: connection established\n",
dprintk("RPC: %4d xprt_conn_status: connection established\n",
task->tk_pid); task->tk_pid);
goto out; return;
}
/* if soft mounted, just cause this RPC to fail */
if (task->tk_client->cl_softrtry)
task->tk_status = -EIO;
switch (task->tk_status) {
case -ENOTCONN:
rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
return;
case -ETIMEDOUT: case -ETIMEDOUT:
dprintk("RPC: %4d xprt_conn_status: timed out\n", dprintk("RPC: %4d xprt_connect_status: timed out\n",
task->tk_pid); task->tk_pid);
/* prevent TCP from continuing to retry SYNs */
xprt_close(xprt);
break; break;
default: default:
printk(KERN_ERR "RPC: error %d connecting to server %s\n", printk(KERN_ERR "RPC: error %d connecting to server %s\n",
-task->tk_status, task->tk_client->cl_server); -task->tk_status, task->tk_client->cl_server);
xprt_close(xprt);
rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
break;
} }
/* if soft mounted, cause this RPC to fail */
if (task->tk_client->cl_softrtry)
task->tk_status = -EIO;
out:
xprt_release_write(xprt, task); xprt_release_write(xprt, task);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment