diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-10-28 15:21:39 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-10-28 15:21:39 -0400 |
commit | 2a9e1cfa23fb62da37739af81127dab5af095d99 (patch) | |
tree | 3fdb86f69ea598163f7096e1f85fd8f5c86bb896 /net/sunrpc/xprtsock.c | |
parent | e946217e4fdaa67681bbabfa8e6b18641921f750 (diff) |
SUNRPC: Respond promptly to server TCP resets
If the server sends us an RST error while we're in the TCP_ESTABLISHED
state, then that will not result in a state change, and so the RPC client
ends up hanging forever (see
http://bugzilla.kernel.org/show_bug.cgi?id=11154)
We can intercept the reset by setting up an sk->sk_error_report callback,
which will then allow us to initiate a proper shutdown and retry...
We also make sure that if the send request receives an ECONNRESET, then we
shutdown too...
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc/xprtsock.c')
-rw-r--r-- | net/sunrpc/xprtsock.c | 58 |
1 files changed, 48 insertions, 10 deletions
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 9a288d5eea64..0a50361e3d83 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -249,6 +249,7 @@ struct sock_xprt { | |||
249 | void (*old_data_ready)(struct sock *, int); | 249 | void (*old_data_ready)(struct sock *, int); |
250 | void (*old_state_change)(struct sock *); | 250 | void (*old_state_change)(struct sock *); |
251 | void (*old_write_space)(struct sock *); | 251 | void (*old_write_space)(struct sock *); |
252 | void (*old_error_report)(struct sock *); | ||
252 | }; | 253 | }; |
253 | 254 | ||
254 | /* | 255 | /* |
@@ -698,8 +699,9 @@ static int xs_tcp_send_request(struct rpc_task *task) | |||
698 | case -EAGAIN: | 699 | case -EAGAIN: |
699 | xs_nospace(task); | 700 | xs_nospace(task); |
700 | break; | 701 | break; |
701 | case -ECONNREFUSED: | ||
702 | case -ECONNRESET: | 702 | case -ECONNRESET: |
703 | xs_tcp_shutdown(xprt); | ||
704 | case -ECONNREFUSED: | ||
703 | case -ENOTCONN: | 705 | case -ENOTCONN: |
704 | case -EPIPE: | 706 | case -EPIPE: |
705 | status = -ENOTCONN; | 707 | status = -ENOTCONN; |
@@ -742,6 +744,22 @@ out_release: | |||
742 | xprt_release_xprt(xprt, task); | 744 | xprt_release_xprt(xprt, task); |
743 | } | 745 | } |
744 | 746 | ||
747 | static void xs_save_old_callbacks(struct sock_xprt *transport, struct sock *sk) | ||
748 | { | ||
749 | transport->old_data_ready = sk->sk_data_ready; | ||
750 | transport->old_state_change = sk->sk_state_change; | ||
751 | transport->old_write_space = sk->sk_write_space; | ||
752 | transport->old_error_report = sk->sk_error_report; | ||
753 | } | ||
754 | |||
755 | static void xs_restore_old_callbacks(struct sock_xprt *transport, struct sock *sk) | ||
756 | { | ||
757 | sk->sk_data_ready = transport->old_data_ready; | ||
758 | sk->sk_state_change = transport->old_state_change; | ||
759 | sk->sk_write_space = transport->old_write_space; | ||
760 | sk->sk_error_report = transport->old_error_report; | ||
761 | } | ||
762 | |||
745 | /** | 763 | /** |
746 | * xs_close - close a socket | 764 | * xs_close - close a socket |
747 | * @xprt: transport | 765 | * @xprt: transport |
@@ -765,9 +783,8 @@ static void xs_close(struct rpc_xprt *xprt) | |||
765 | transport->sock = NULL; | 783 | transport->sock = NULL; |
766 | 784 | ||
767 | sk->sk_user_data = NULL; | 785 | sk->sk_user_data = NULL; |
768 | sk->sk_data_ready = transport->old_data_ready; | 786 | |
769 | sk->sk_state_change = transport->old_state_change; | 787 | xs_restore_old_callbacks(transport, sk); |
770 | sk->sk_write_space = transport->old_write_space; | ||
771 | write_unlock_bh(&sk->sk_callback_lock); | 788 | write_unlock_bh(&sk->sk_callback_lock); |
772 | 789 | ||
773 | sk->sk_no_check = 0; | 790 | sk->sk_no_check = 0; |
@@ -1180,6 +1197,28 @@ static void xs_tcp_state_change(struct sock *sk) | |||
1180 | } | 1197 | } |
1181 | 1198 | ||
1182 | /** | 1199 | /** |
1200 | * xs_tcp_error_report - callback mainly for catching RST events | ||
1201 | * @sk: socket | ||
1202 | */ | ||
1203 | static void xs_tcp_error_report(struct sock *sk) | ||
1204 | { | ||
1205 | struct rpc_xprt *xprt; | ||
1206 | |||
1207 | read_lock(&sk->sk_callback_lock); | ||
1208 | if (sk->sk_err != ECONNRESET || sk->sk_state != TCP_ESTABLISHED) | ||
1209 | goto out; | ||
1210 | if (!(xprt = xprt_from_sock(sk))) | ||
1211 | goto out; | ||
1212 | dprintk("RPC: %s client %p...\n" | ||
1213 | "RPC: error %d\n", | ||
1214 | __func__, xprt, sk->sk_err); | ||
1215 | |||
1216 | xprt_force_disconnect(xprt); | ||
1217 | out: | ||
1218 | read_unlock(&sk->sk_callback_lock); | ||
1219 | } | ||
1220 | |||
1221 | /** | ||
1183 | * xs_udp_write_space - callback invoked when socket buffer space | 1222 | * xs_udp_write_space - callback invoked when socket buffer space |
1184 | * becomes available | 1223 | * becomes available |
1185 | * @sk: socket whose state has changed | 1224 | * @sk: socket whose state has changed |
@@ -1454,10 +1493,9 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) | |||
1454 | 1493 | ||
1455 | write_lock_bh(&sk->sk_callback_lock); | 1494 | write_lock_bh(&sk->sk_callback_lock); |
1456 | 1495 | ||
1496 | xs_save_old_callbacks(transport, sk); | ||
1497 | |||
1457 | sk->sk_user_data = xprt; | 1498 | sk->sk_user_data = xprt; |
1458 | transport->old_data_ready = sk->sk_data_ready; | ||
1459 | transport->old_state_change = sk->sk_state_change; | ||
1460 | transport->old_write_space = sk->sk_write_space; | ||
1461 | sk->sk_data_ready = xs_udp_data_ready; | 1499 | sk->sk_data_ready = xs_udp_data_ready; |
1462 | sk->sk_write_space = xs_udp_write_space; | 1500 | sk->sk_write_space = xs_udp_write_space; |
1463 | sk->sk_no_check = UDP_CSUM_NORCV; | 1501 | sk->sk_no_check = UDP_CSUM_NORCV; |
@@ -1589,13 +1627,13 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) | |||
1589 | 1627 | ||
1590 | write_lock_bh(&sk->sk_callback_lock); | 1628 | write_lock_bh(&sk->sk_callback_lock); |
1591 | 1629 | ||
1630 | xs_save_old_callbacks(transport, sk); | ||
1631 | |||
1592 | sk->sk_user_data = xprt; | 1632 | sk->sk_user_data = xprt; |
1593 | transport->old_data_ready = sk->sk_data_ready; | ||
1594 | transport->old_state_change = sk->sk_state_change; | ||
1595 | transport->old_write_space = sk->sk_write_space; | ||
1596 | sk->sk_data_ready = xs_tcp_data_ready; | 1633 | sk->sk_data_ready = xs_tcp_data_ready; |
1597 | sk->sk_state_change = xs_tcp_state_change; | 1634 | sk->sk_state_change = xs_tcp_state_change; |
1598 | sk->sk_write_space = xs_tcp_write_space; | 1635 | sk->sk_write_space = xs_tcp_write_space; |
1636 | sk->sk_error_report = xs_tcp_error_report; | ||
1599 | sk->sk_allocation = GFP_ATOMIC; | 1637 | sk->sk_allocation = GFP_ATOMIC; |
1600 | 1638 | ||
1601 | /* socket options */ | 1639 | /* socket options */ |