diff options
author | Sowmini Varadhan <sowmini.varadhan@oracle.com> | 2016-06-30 19:11:13 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-07-01 16:45:17 -0400 |
commit | afb4164d91c7486a1d4ab098a1b88e27b5e25772 (patch) | |
tree | 9d1b76c3268d9c491fc9280237b62f7fb7a49892 /net/rds | |
parent | 02105b2ccdd6344146e0296172a9e0f17ff624ef (diff) |
RDS: TCP: Refactor connection destruction to handle multiple paths
A single rds_connection may have multiple rds_conn_paths that have
to be carefully and correctly destroyed, for both rmmod and
netns-delete cases.
For both cases, we extract a single rds_tcp_connection for
each conn into a temporary list, and then invoke rds_conn_destroy()
which iteratively dismantles every path in the rds_connection.
For the netns deletion case, we additionally have to make sure
that we do not leave a socket in TIME_WAIT state, as this will
hold up the netns deletion. Thus we call rds_tcp_conn_paths_destroy()
to reset state quickly.
Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/rds')
-rw-r--r-- | net/rds/tcp.c | 46 |
1 files changed, 39 insertions, 7 deletions
diff --git a/net/rds/tcp.c b/net/rds/tcp.c index c6b47f670990..b32772759c9d 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c | |||
@@ -323,6 +323,17 @@ static void rds_tcp_conn_free(void *arg) | |||
323 | kmem_cache_free(rds_tcp_conn_slab, tc); | 323 | kmem_cache_free(rds_tcp_conn_slab, tc); |
324 | } | 324 | } |
325 | 325 | ||
326 | static bool list_has_conn(struct list_head *list, struct rds_connection *conn) | ||
327 | { | ||
328 | struct rds_tcp_connection *tc, *_tc; | ||
329 | |||
330 | list_for_each_entry_safe(tc, _tc, list, t_tcp_node) { | ||
331 | if (tc->t_cpath->cp_conn == conn) | ||
332 | return true; | ||
333 | } | ||
334 | return false; | ||
335 | } | ||
336 | |||
326 | static void rds_tcp_destroy_conns(void) | 337 | static void rds_tcp_destroy_conns(void) |
327 | { | 338 | { |
328 | struct rds_tcp_connection *tc, *_tc; | 339 | struct rds_tcp_connection *tc, *_tc; |
@@ -330,8 +341,10 @@ static void rds_tcp_destroy_conns(void) | |||
330 | 341 | ||
331 | /* avoid calling conn_destroy with irqs off */ | 342 | /* avoid calling conn_destroy with irqs off */ |
332 | spin_lock_irq(&rds_tcp_conn_lock); | 343 | spin_lock_irq(&rds_tcp_conn_lock); |
333 | list_splice(&rds_tcp_conn_list, &tmp_list); | 344 | list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) { |
334 | INIT_LIST_HEAD(&rds_tcp_conn_list); | 345 | if (!list_has_conn(&tmp_list, tc->t_cpath->cp_conn)) |
346 | list_move_tail(&tc->t_tcp_node, &tmp_list); | ||
347 | } | ||
335 | spin_unlock_irq(&rds_tcp_conn_lock); | 348 | spin_unlock_irq(&rds_tcp_conn_lock); |
336 | 349 | ||
337 | list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) | 350 | list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) |
@@ -491,10 +504,30 @@ static struct pernet_operations rds_tcp_net_ops = { | |||
491 | .size = sizeof(struct rds_tcp_net), | 504 | .size = sizeof(struct rds_tcp_net), |
492 | }; | 505 | }; |
493 | 506 | ||
507 | /* explicitly send a RST on each socket, thereby releasing any socket refcnts | ||
508 | * that may otherwise hold up netns deletion. | ||
509 | */ | ||
510 | static void rds_tcp_conn_paths_destroy(struct rds_connection *conn) | ||
511 | { | ||
512 | struct rds_conn_path *cp; | ||
513 | struct rds_tcp_connection *tc; | ||
514 | int i; | ||
515 | struct sock *sk; | ||
516 | |||
517 | for (i = 0; i < RDS_MPATH_WORKERS; i++) { | ||
518 | cp = &conn->c_path[i]; | ||
519 | tc = cp->cp_transport_data; | ||
520 | if (!tc->t_sock) | ||
521 | continue; | ||
522 | sk = tc->t_sock->sk; | ||
523 | sk->sk_prot->disconnect(sk, 0); | ||
524 | tcp_done(sk); | ||
525 | } | ||
526 | } | ||
527 | |||
494 | static void rds_tcp_kill_sock(struct net *net) | 528 | static void rds_tcp_kill_sock(struct net *net) |
495 | { | 529 | { |
496 | struct rds_tcp_connection *tc, *_tc; | 530 | struct rds_tcp_connection *tc, *_tc; |
497 | struct sock *sk; | ||
498 | LIST_HEAD(tmp_list); | 531 | LIST_HEAD(tmp_list); |
499 | struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid); | 532 | struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid); |
500 | 533 | ||
@@ -507,13 +540,12 @@ static void rds_tcp_kill_sock(struct net *net) | |||
507 | 540 | ||
508 | if (net != c_net || !tc->t_sock) | 541 | if (net != c_net || !tc->t_sock) |
509 | continue; | 542 | continue; |
510 | list_move_tail(&tc->t_tcp_node, &tmp_list); | 543 | if (!list_has_conn(&tmp_list, tc->t_cpath->cp_conn)) |
544 | list_move_tail(&tc->t_tcp_node, &tmp_list); | ||
511 | } | 545 | } |
512 | spin_unlock_irq(&rds_tcp_conn_lock); | 546 | spin_unlock_irq(&rds_tcp_conn_lock); |
513 | list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) { | 547 | list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) { |
514 | sk = tc->t_sock->sk; | 548 | rds_tcp_conn_paths_destroy(tc->t_cpath->cp_conn); |
515 | sk->sk_prot->disconnect(sk, 0); | ||
516 | tcp_done(sk); | ||
517 | rds_conn_destroy(tc->t_cpath->cp_conn); | 549 | rds_conn_destroy(tc->t_cpath->cp_conn); |
518 | } | 550 | } |
519 | } | 551 | } |