diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-11-06 10:18:36 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-01-30 02:05:24 -0500 |
commit | 66af1e558538137080615e7ad6d1f2f80862de01 (patch) | |
tree | f5f9680b86846d4df8312cd9bd729d6e10c7b5e8 | |
parent | ef818a28fac9bd214e676986d8301db0582b92a9 (diff) |
SUNRPC: Fix a race in xs_tcp_state_change()
When scheduling the autoclose RPC call, we want to ensure that we don't
race against the test_bit() call in xprt_clear_locked().
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | include/linux/sunrpc/xprt.h | 1 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 20 | ||||
-rw-r--r-- | net/sunrpc/xprtsock.c | 5 |
3 files changed, 22 insertions, 4 deletions
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 30b17b3bc1a..6f524a9e7fd 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h | |||
@@ -246,6 +246,7 @@ struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid); | |||
246 | void xprt_complete_rqst(struct rpc_task *task, int copied); | 246 | void xprt_complete_rqst(struct rpc_task *task, int copied); |
247 | void xprt_release_rqst_cong(struct rpc_task *task); | 247 | void xprt_release_rqst_cong(struct rpc_task *task); |
248 | void xprt_disconnect(struct rpc_xprt *xprt); | 248 | void xprt_disconnect(struct rpc_xprt *xprt); |
249 | void xprt_force_disconnect(struct rpc_xprt *xprt); | ||
249 | 250 | ||
250 | /* | 251 | /* |
251 | * Reserved bit positions in xprt->state | 252 | * Reserved bit positions in xprt->state |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index fb92f51405c..a3af02168af 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -570,6 +570,7 @@ static void xprt_autoclose(struct work_struct *work) | |||
570 | 570 | ||
571 | xprt_disconnect(xprt); | 571 | xprt_disconnect(xprt); |
572 | xprt->ops->close(xprt); | 572 | xprt->ops->close(xprt); |
573 | clear_bit(XPRT_CLOSE_WAIT, &xprt->state); | ||
573 | xprt_release_write(xprt, NULL); | 574 | xprt_release_write(xprt, NULL); |
574 | } | 575 | } |
575 | 576 | ||
@@ -588,6 +589,25 @@ void xprt_disconnect(struct rpc_xprt *xprt) | |||
588 | } | 589 | } |
589 | EXPORT_SYMBOL_GPL(xprt_disconnect); | 590 | EXPORT_SYMBOL_GPL(xprt_disconnect); |
590 | 591 | ||
592 | /** | ||
593 | * xprt_force_disconnect - force a transport to disconnect | ||
594 | * @xprt: transport to disconnect | ||
595 | * | ||
596 | */ | ||
597 | void xprt_force_disconnect(struct rpc_xprt *xprt) | ||
598 | { | ||
599 | /* Don't race with the test_bit() in xprt_clear_locked() */ | ||
600 | spin_lock_bh(&xprt->transport_lock); | ||
601 | set_bit(XPRT_CLOSE_WAIT, &xprt->state); | ||
602 | /* Try to schedule an autoclose RPC call */ | ||
603 | if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) | ||
604 | queue_work(rpciod_workqueue, &xprt->task_cleanup); | ||
605 | else if (xprt->snd_task != NULL) | ||
606 | rpc_wake_up_task(xprt->snd_task); | ||
607 | spin_unlock_bh(&xprt->transport_lock); | ||
608 | } | ||
609 | EXPORT_SYMBOL_GPL(xprt_force_disconnect); | ||
610 | |||
591 | static void | 611 | static void |
592 | xprt_init_autodisconnect(unsigned long data) | 612 | xprt_init_autodisconnect(unsigned long data) |
593 | { | 613 | { |
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 6fa52f44de0..abb40c14073 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -1122,10 +1122,7 @@ static void xs_tcp_state_change(struct sock *sk) | |||
1122 | case TCP_SYN_RECV: | 1122 | case TCP_SYN_RECV: |
1123 | break; | 1123 | break; |
1124 | case TCP_CLOSE_WAIT: | 1124 | case TCP_CLOSE_WAIT: |
1125 | /* Try to schedule an autoclose RPC calls */ | 1125 | xprt_force_disconnect(xprt); |
1126 | set_bit(XPRT_CLOSE_WAIT, &xprt->state); | ||
1127 | if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) | ||
1128 | queue_work(rpciod_workqueue, &xprt->task_cleanup); | ||
1129 | default: | 1126 | default: |
1130 | xprt_disconnect(xprt); | 1127 | xprt_disconnect(xprt); |
1131 | } | 1128 | } |