diff options
author | Sowmini Varadhan <sowmini.varadhan@oracle.com> | 2017-07-16 19:43:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-07-16 22:07:35 -0400 |
commit | aed20a53a7d91e45c6a8cb8920c77aaaa88f76ee (patch) | |
tree | 78bdb7167413d66c08f14ec1056cffd85fa0bc7d | |
parent | ce3dbe29749e3a3ef25540b394a68215e3113462 (diff) |
rds: cancel send/recv work before queuing connection shutdown
We could end up executing rds_conn_shutdown before the rds_recv_worker
thread, then rds_conn_shutdown -> rds_tcp_conn_shutdown can do a
sock_release and set sock->sk to null, which may interleave in bad
ways with rds_recv_worker, e.g., it could result in:
"BUG: unable to handle kernel NULL pointer dereference at 0000000000000078"
[ffff881769f6fd70] release_sock at ffffffff815f337b
[ffff881769f6fd90] rds_tcp_recv at ffffffffa043c888 [rds_tcp]
[ffff881769f6fdb0] rds_recv_worker at ffffffffa04a4810 [rds]
[ffff881769f6fde0] process_one_work at ffffffff810a14c1
[ffff881769f6fe40] worker_thread at ffffffff810a1940
[ffff881769f6fec0] kthread at ffffffff810a6b1e
Also, do not enqueue any new shutdown workq items when the connection is
shutting down (this may happen for rds-tcp in softirq mode, if a FIN
or CLOSE is received while the modules is in the middle of an unload)
Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/rds/connection.c | 16 | ||||
-rw-r--r-- | net/rds/rds.h | 2 | ||||
-rw-r--r-- | net/rds/tcp.c | 2 | ||||
-rw-r--r-- | net/rds/tcp_connect.c | 4 | ||||
-rw-r--r-- | net/rds/tcp_send.c | 2 | ||||
-rw-r--r-- | net/rds/threads.c | 2 |
6 files changed, 16 insertions, 12 deletions
diff --git a/net/rds/connection.c b/net/rds/connection.c index 50a3789ac23e..005bca68aa94 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c | |||
@@ -374,13 +374,13 @@ static void rds_conn_path_destroy(struct rds_conn_path *cp) | |||
374 | if (!cp->cp_transport_data) | 374 | if (!cp->cp_transport_data) |
375 | return; | 375 | return; |
376 | 376 | ||
377 | rds_conn_path_drop(cp); | ||
378 | flush_work(&cp->cp_down_w); | ||
379 | |||
380 | /* make sure lingering queued work won't try to ref the conn */ | 377 | /* make sure lingering queued work won't try to ref the conn */ |
381 | cancel_delayed_work_sync(&cp->cp_send_w); | 378 | cancel_delayed_work_sync(&cp->cp_send_w); |
382 | cancel_delayed_work_sync(&cp->cp_recv_w); | 379 | cancel_delayed_work_sync(&cp->cp_recv_w); |
383 | 380 | ||
381 | rds_conn_path_drop(cp, true); | ||
382 | flush_work(&cp->cp_down_w); | ||
383 | |||
384 | /* tear down queued messages */ | 384 | /* tear down queued messages */ |
385 | list_for_each_entry_safe(rm, rtmp, | 385 | list_for_each_entry_safe(rm, rtmp, |
386 | &cp->cp_send_queue, | 386 | &cp->cp_send_queue, |
@@ -664,9 +664,13 @@ void rds_conn_exit(void) | |||
664 | /* | 664 | /* |
665 | * Force a disconnect | 665 | * Force a disconnect |
666 | */ | 666 | */ |
667 | void rds_conn_path_drop(struct rds_conn_path *cp) | 667 | void rds_conn_path_drop(struct rds_conn_path *cp, bool destroy) |
668 | { | 668 | { |
669 | atomic_set(&cp->cp_state, RDS_CONN_ERROR); | 669 | atomic_set(&cp->cp_state, RDS_CONN_ERROR); |
670 | |||
671 | if (!destroy && cp->cp_conn->c_destroy_in_prog) | ||
672 | return; | ||
673 | |||
670 | queue_work(rds_wq, &cp->cp_down_w); | 674 | queue_work(rds_wq, &cp->cp_down_w); |
671 | } | 675 | } |
672 | EXPORT_SYMBOL_GPL(rds_conn_path_drop); | 676 | EXPORT_SYMBOL_GPL(rds_conn_path_drop); |
@@ -674,7 +678,7 @@ EXPORT_SYMBOL_GPL(rds_conn_path_drop); | |||
674 | void rds_conn_drop(struct rds_connection *conn) | 678 | void rds_conn_drop(struct rds_connection *conn) |
675 | { | 679 | { |
676 | WARN_ON(conn->c_trans->t_mp_capable); | 680 | WARN_ON(conn->c_trans->t_mp_capable); |
677 | rds_conn_path_drop(&conn->c_path[0]); | 681 | rds_conn_path_drop(&conn->c_path[0], false); |
678 | } | 682 | } |
679 | EXPORT_SYMBOL_GPL(rds_conn_drop); | 683 | EXPORT_SYMBOL_GPL(rds_conn_drop); |
680 | 684 | ||
@@ -706,5 +710,5 @@ __rds_conn_path_error(struct rds_conn_path *cp, const char *fmt, ...) | |||
706 | vprintk(fmt, ap); | 710 | vprintk(fmt, ap); |
707 | va_end(ap); | 711 | va_end(ap); |
708 | 712 | ||
709 | rds_conn_path_drop(cp); | 713 | rds_conn_path_drop(cp, false); |
710 | } | 714 | } |
diff --git a/net/rds/rds.h b/net/rds/rds.h index 516bcc89b46f..3382695bf46c 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h | |||
@@ -700,7 +700,7 @@ struct rds_connection *rds_conn_create_outgoing(struct net *net, | |||
700 | void rds_conn_shutdown(struct rds_conn_path *cpath); | 700 | void rds_conn_shutdown(struct rds_conn_path *cpath); |
701 | void rds_conn_destroy(struct rds_connection *conn); | 701 | void rds_conn_destroy(struct rds_connection *conn); |
702 | void rds_conn_drop(struct rds_connection *conn); | 702 | void rds_conn_drop(struct rds_connection *conn); |
703 | void rds_conn_path_drop(struct rds_conn_path *cpath); | 703 | void rds_conn_path_drop(struct rds_conn_path *cpath, bool destroy); |
704 | void rds_conn_connect_if_down(struct rds_connection *conn); | 704 | void rds_conn_connect_if_down(struct rds_connection *conn); |
705 | void rds_conn_path_connect_if_down(struct rds_conn_path *cp); | 705 | void rds_conn_path_connect_if_down(struct rds_conn_path *cp); |
706 | void rds_for_each_conn_info(struct socket *sock, unsigned int len, | 706 | void rds_for_each_conn_info(struct socket *sock, unsigned int len, |
diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 431404dbdad1..6b7ee71f40c6 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c | |||
@@ -592,7 +592,7 @@ static void rds_tcp_sysctl_reset(struct net *net) | |||
592 | continue; | 592 | continue; |
593 | 593 | ||
594 | /* reconnect with new parameters */ | 594 | /* reconnect with new parameters */ |
595 | rds_conn_path_drop(tc->t_cpath); | 595 | rds_conn_path_drop(tc->t_cpath, false); |
596 | } | 596 | } |
597 | spin_unlock_irq(&rds_tcp_conn_lock); | 597 | spin_unlock_irq(&rds_tcp_conn_lock); |
598 | } | 598 | } |
diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c index cbe08a1fa4c7..46f74dad0e16 100644 --- a/net/rds/tcp_connect.c +++ b/net/rds/tcp_connect.c | |||
@@ -69,14 +69,14 @@ void rds_tcp_state_change(struct sock *sk) | |||
69 | if (!IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr) && | 69 | if (!IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr) && |
70 | rds_conn_path_transition(cp, RDS_CONN_CONNECTING, | 70 | rds_conn_path_transition(cp, RDS_CONN_CONNECTING, |
71 | RDS_CONN_ERROR)) { | 71 | RDS_CONN_ERROR)) { |
72 | rds_conn_path_drop(cp); | 72 | rds_conn_path_drop(cp, false); |
73 | } else { | 73 | } else { |
74 | rds_connect_path_complete(cp, RDS_CONN_CONNECTING); | 74 | rds_connect_path_complete(cp, RDS_CONN_CONNECTING); |
75 | } | 75 | } |
76 | break; | 76 | break; |
77 | case TCP_CLOSE_WAIT: | 77 | case TCP_CLOSE_WAIT: |
78 | case TCP_CLOSE: | 78 | case TCP_CLOSE: |
79 | rds_conn_path_drop(cp); | 79 | rds_conn_path_drop(cp, false); |
80 | default: | 80 | default: |
81 | break; | 81 | break; |
82 | } | 82 | } |
diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c index 0d8616aa5bad..dc860d1bb608 100644 --- a/net/rds/tcp_send.c +++ b/net/rds/tcp_send.c | |||
@@ -157,7 +157,7 @@ out: | |||
157 | "returned %d, " | 157 | "returned %d, " |
158 | "disconnecting and reconnecting\n", | 158 | "disconnecting and reconnecting\n", |
159 | &conn->c_faddr, cp->cp_index, ret); | 159 | &conn->c_faddr, cp->cp_index, ret); |
160 | rds_conn_path_drop(cp); | 160 | rds_conn_path_drop(cp, false); |
161 | } | 161 | } |
162 | } | 162 | } |
163 | } | 163 | } |
diff --git a/net/rds/threads.c b/net/rds/threads.c index 2852bc1d37d4..f121daa402c8 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c | |||
@@ -78,7 +78,7 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr) | |||
78 | "current state is %d\n", | 78 | "current state is %d\n", |
79 | __func__, | 79 | __func__, |
80 | atomic_read(&cp->cp_state)); | 80 | atomic_read(&cp->cp_state)); |
81 | rds_conn_path_drop(cp); | 81 | rds_conn_path_drop(cp, false); |
82 | return; | 82 | return; |
83 | } | 83 | } |
84 | 84 | ||