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 /net | |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/xprt.c | 20 | ||||
-rw-r--r-- | net/sunrpc/xprtsock.c | 5 |
2 files changed, 21 insertions, 4 deletions
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index fb92f51405c5..a3af02168af1 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 6fa52f44de0f..abb40c140738 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 | } |