Commit b14d09be authored by Trond Myklebust's avatar Trond Myklebust

RPC: Ensure that we only schedule one RPC request at a time.

   In theory the current code could cause two to be scheduled
   if something wakes up xprt->snd_task before keventd has
   had a chance to run xprt_sock_connect()
parent 5d36d77c
...@@ -216,12 +216,15 @@ void xprt_connect(struct rpc_task *); ...@@ -216,12 +216,15 @@ void xprt_connect(struct rpc_task *);
int xprt_clear_backlog(struct rpc_xprt *); int xprt_clear_backlog(struct rpc_xprt *);
void xprt_sock_setbufsize(struct rpc_xprt *); void xprt_sock_setbufsize(struct rpc_xprt *);
#define XPRT_CONNECT 0 #define XPRT_LOCKED 0
#define XPRT_LOCKED 1 #define XPRT_CONNECT 1
#define XPRT_CONNECTING 2
#define xprt_connected(xp) (test_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_connected(xp) (test_bit(XPRT_CONNECT, &(xp)->sockstate))
#define xprt_set_connected(xp) (set_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_set_connected(xp) (set_bit(XPRT_CONNECT, &(xp)->sockstate))
#define xprt_test_and_set_connected(xp) (test_and_set_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_test_and_set_connected(xp) (test_and_set_bit(XPRT_CONNECT, &(xp)->sockstate))
#define xprt_test_and_clear_connected(xp) \
(test_and_clear_bit(XPRT_CONNECT, &(xp)->sockstate))
#define xprt_clear_connected(xp) (clear_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_clear_connected(xp) (clear_bit(XPRT_CONNECT, &(xp)->sockstate))
#endif /* __KERNEL__*/ #endif /* __KERNEL__*/
......
...@@ -448,7 +448,10 @@ xprt_init_autodisconnect(unsigned long data) ...@@ -448,7 +448,10 @@ xprt_init_autodisconnect(unsigned long data)
goto out_abort; goto out_abort;
spin_unlock(&xprt->sock_lock); spin_unlock(&xprt->sock_lock);
/* Let keventd close the socket */ /* Let keventd close the socket */
schedule_work(&xprt->task_cleanup); if (test_bit(XPRT_CONNECTING, &xprt->sockstate) != 0)
xprt_release_write(xprt, NULL);
else
schedule_work(&xprt->task_cleanup);
return; return;
out_abort: out_abort:
spin_unlock(&xprt->sock_lock); spin_unlock(&xprt->sock_lock);
...@@ -460,12 +463,8 @@ static void xprt_socket_connect(void *args) ...@@ -460,12 +463,8 @@ static void xprt_socket_connect(void *args)
struct socket *sock = xprt->sock; struct socket *sock = xprt->sock;
int status = -EIO; int status = -EIO;
if (xprt->shutdown) { if (xprt->shutdown || xprt->addr.sin_port == 0)
rpc_wake_up_status(&xprt->pending, -EIO); goto out;
return;
}
if (!xprt->addr.sin_port)
goto out_err;
/* /*
* Start by resetting any existing state * Start by resetting any existing state
...@@ -475,12 +474,12 @@ static void xprt_socket_connect(void *args) ...@@ -475,12 +474,12 @@ static void xprt_socket_connect(void *args)
if (sock == NULL) { if (sock == NULL) {
/* couldn't create socket or bind to reserved port; /* couldn't create socket or bind to reserved port;
* this is likely a permanent error, so cause an abort */ * this is likely a permanent error, so cause an abort */
goto out_err; goto out;
return;
} }
xprt_bind_socket(xprt, sock); xprt_bind_socket(xprt, sock);
xprt_sock_setbufsize(xprt); xprt_sock_setbufsize(xprt);
status = 0;
if (!xprt->stream) if (!xprt->stream)
goto out; goto out;
...@@ -491,29 +490,22 @@ static void xprt_socket_connect(void *args) ...@@ -491,29 +490,22 @@ static void xprt_socket_connect(void *args)
sizeof(xprt->addr), O_NONBLOCK); sizeof(xprt->addr), O_NONBLOCK);
dprintk("RPC: %p connect status %d connected %d sock state %d\n", dprintk("RPC: %p connect status %d connected %d sock state %d\n",
xprt, -status, xprt_connected(xprt), sock->sk->sk_state); xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
if (status >= 0) if (status < 0) {
goto out; switch (status) {
switch (status) { case -EINPROGRESS:
case -EINPROGRESS: case -EALREADY:
case -EALREADY: goto out_clear;
return; }
default:
goto out_err;
} }
out: out:
spin_lock_bh(&xprt->sock_lock); if (status < 0)
if (xprt->snd_task) rpc_wake_up_status(&xprt->pending, status);
rpc_wake_up_task(xprt->snd_task); else
spin_unlock_bh(&xprt->sock_lock); rpc_wake_up(&xprt->pending);
return; out_clear:
out_err: smp_mb__before_clear_bit();
spin_lock_bh(&xprt->sock_lock); clear_bit(XPRT_CONNECTING, &xprt->sockstate);
if (xprt->snd_task) { smp_mb__after_clear_bit();
xprt->snd_task->tk_status = status;
rpc_wake_up_task(xprt->snd_task);
} else
rpc_wake_up_status(&xprt->pending, -ENOTCONN);
spin_unlock_bh(&xprt->sock_lock);
} }
/* /*
...@@ -545,7 +537,8 @@ void xprt_connect(struct rpc_task *task) ...@@ -545,7 +537,8 @@ void xprt_connect(struct rpc_task *task)
task->tk_timeout = RPC_CONNECT_TIMEOUT; task->tk_timeout = RPC_CONNECT_TIMEOUT;
rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL); rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL);
schedule_work(&xprt->sock_connect); if (!test_and_set_bit(XPRT_CONNECTING, &xprt->sockstate))
schedule_work(&xprt->sock_connect);
return; return;
out_write: out_write:
xprt_release_write(xprt, task); xprt_release_write(xprt, task);
...@@ -1027,9 +1020,6 @@ tcp_state_change(struct sock *sk) ...@@ -1027,9 +1020,6 @@ tcp_state_change(struct sock *sk)
xprt->tcp_reclen = 0; xprt->tcp_reclen = 0;
xprt->tcp_copied = 0; xprt->tcp_copied = 0;
xprt->tcp_flags = XPRT_COPY_RECM | XPRT_COPY_XID; xprt->tcp_flags = XPRT_COPY_RECM | XPRT_COPY_XID;
if (xprt->snd_task)
rpc_wake_up_task(xprt->snd_task);
rpc_wake_up(&xprt->pending); rpc_wake_up(&xprt->pending);
} }
spin_unlock_bh(&xprt->sock_lock); spin_unlock_bh(&xprt->sock_lock);
...@@ -1038,7 +1028,8 @@ tcp_state_change(struct sock *sk) ...@@ -1038,7 +1028,8 @@ tcp_state_change(struct sock *sk)
case TCP_SYN_RECV: case TCP_SYN_RECV:
break; break;
default: default:
xprt_disconnect(xprt); if (xprt_test_and_clear_connected(xprt))
rpc_wake_up_status(&xprt->pending, -ENOTCONN);
break; break;
} }
out: out:
......
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