diff options
author | Philipp Reisner <philipp.reisner@linbit.com> | 2012-07-12 08:22:37 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2012-11-08 10:58:31 -0500 |
commit | 7a426fd8d5af1d5e71cfcdf5ecbefbbad47a81fd (patch) | |
tree | 06174b9f6f35fce19e9b74a69ca30fc89065f434 /drivers/block/drbd | |
parent | 1f3e509b761d6d8f91acbf7da39624d086e1f2eb (diff) |
drbd: Keep the listening socket open while trying to connect to the peer
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block/drbd')
-rw-r--r-- | drivers/block/drbd/drbd_receiver.c | 72 |
1 files changed, 54 insertions, 18 deletions
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 46c55793dd8d..9aac1c4033c7 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c | |||
@@ -666,7 +666,32 @@ out: | |||
666 | return sock; | 666 | return sock; |
667 | } | 667 | } |
668 | 668 | ||
669 | static struct socket *prepare_listen_socket(struct drbd_tconn *tconn) | 669 | struct accept_wait_data { |
670 | struct drbd_tconn *tconn; | ||
671 | struct socket *s_listen; | ||
672 | struct completion door_bell; | ||
673 | void (*original_sk_state_change)(struct sock *sk); | ||
674 | |||
675 | }; | ||
676 | |||
677 | static void incomming_connection(struct sock *sk) | ||
678 | { | ||
679 | struct accept_wait_data *ad = sk->sk_user_data; | ||
680 | struct drbd_tconn *tconn = ad->tconn; | ||
681 | |||
682 | if (sk->sk_state != TCP_ESTABLISHED) | ||
683 | conn_warn(tconn, "unexpected tcp state change. sk_state = %d\n", sk->sk_state); | ||
684 | |||
685 | write_lock_bh(&sk->sk_callback_lock); | ||
686 | sk->sk_state_change = ad->original_sk_state_change; | ||
687 | sk->sk_user_data = NULL; | ||
688 | write_unlock_bh(&sk->sk_callback_lock); | ||
689 | |||
690 | sk->sk_state_change(sk); | ||
691 | complete(&ad->door_bell); | ||
692 | } | ||
693 | |||
694 | static int prepare_listen_socket(struct drbd_tconn *tconn, struct accept_wait_data *ad) | ||
670 | { | 695 | { |
671 | int err, sndbuf_size, rcvbuf_size, my_addr_len; | 696 | int err, sndbuf_size, rcvbuf_size, my_addr_len; |
672 | struct sockaddr_in6 my_addr; | 697 | struct sockaddr_in6 my_addr; |
@@ -678,7 +703,7 @@ static struct socket *prepare_listen_socket(struct drbd_tconn *tconn) | |||
678 | nc = rcu_dereference(tconn->net_conf); | 703 | nc = rcu_dereference(tconn->net_conf); |
679 | if (!nc) { | 704 | if (!nc) { |
680 | rcu_read_unlock(); | 705 | rcu_read_unlock(); |
681 | return NULL; | 706 | return -EIO; |
682 | } | 707 | } |
683 | sndbuf_size = nc->sndbuf_size; | 708 | sndbuf_size = nc->sndbuf_size; |
684 | rcvbuf_size = nc->rcvbuf_size; | 709 | rcvbuf_size = nc->rcvbuf_size; |
@@ -703,12 +728,19 @@ static struct socket *prepare_listen_socket(struct drbd_tconn *tconn) | |||
703 | if (err < 0) | 728 | if (err < 0) |
704 | goto out; | 729 | goto out; |
705 | 730 | ||
731 | ad->s_listen = s_listen; | ||
732 | write_lock_bh(&s_listen->sk->sk_callback_lock); | ||
733 | ad->original_sk_state_change = s_listen->sk->sk_state_change; | ||
734 | s_listen->sk->sk_state_change = incomming_connection; | ||
735 | s_listen->sk->sk_user_data = ad; | ||
736 | write_unlock_bh(&s_listen->sk->sk_callback_lock); | ||
737 | |||
706 | what = "listen"; | 738 | what = "listen"; |
707 | err = s_listen->ops->listen(s_listen, 5); | 739 | err = s_listen->ops->listen(s_listen, 5); |
708 | if (err < 0) | 740 | if (err < 0) |
709 | goto out; | 741 | goto out; |
710 | 742 | ||
711 | return s_listen; | 743 | return 0; |
712 | out: | 744 | out: |
713 | if (s_listen) | 745 | if (s_listen) |
714 | sock_release(s_listen); | 746 | sock_release(s_listen); |
@@ -719,14 +751,13 @@ out: | |||
719 | } | 751 | } |
720 | } | 752 | } |
721 | 753 | ||
722 | return NULL; | 754 | return -EIO; |
723 | } | 755 | } |
724 | 756 | ||
725 | static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn) | 757 | static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn, struct accept_wait_data *ad) |
726 | { | 758 | { |
727 | int timeo, connect_int, err = 0; | 759 | int timeo, connect_int, err = 0; |
728 | struct socket *s_estab = NULL; | 760 | struct socket *s_estab = NULL; |
729 | struct socket *s_listen; | ||
730 | struct net_conf *nc; | 761 | struct net_conf *nc; |
731 | 762 | ||
732 | rcu_read_lock(); | 763 | rcu_read_lock(); |
@@ -741,18 +772,11 @@ static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn) | |||
741 | timeo = connect_int * HZ; | 772 | timeo = connect_int * HZ; |
742 | timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */ | 773 | timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */ |
743 | 774 | ||
744 | s_listen = prepare_listen_socket(tconn); | 775 | err = wait_for_completion_interruptible_timeout(&ad->door_bell, timeo); |
745 | if (!s_listen) | 776 | if (err <= 0) |
746 | goto out; | 777 | return NULL; |
747 | |||
748 | s_listen->sk->sk_rcvtimeo = timeo; | ||
749 | s_listen->sk->sk_sndtimeo = timeo; | ||
750 | |||
751 | err = kernel_accept(s_listen, &s_estab, 0); | ||
752 | 778 | ||
753 | out: | 779 | err = kernel_accept(ad->s_listen, &s_estab, 0); |
754 | if (s_listen) | ||
755 | sock_release(s_listen); | ||
756 | if (err < 0) { | 780 | if (err < 0) { |
757 | if (err != -EAGAIN && err != -EINTR && err != -ERESTARTSYS) { | 781 | if (err != -EAGAIN && err != -EINTR && err != -ERESTARTSYS) { |
758 | conn_err(tconn, "accept failed, err = %d\n", err); | 782 | conn_err(tconn, "accept failed, err = %d\n", err); |
@@ -855,6 +879,10 @@ static int conn_connect(struct drbd_tconn *tconn) | |||
855 | int vnr, timeout, try, h, ok; | 879 | int vnr, timeout, try, h, ok; |
856 | bool discard_my_data; | 880 | bool discard_my_data; |
857 | enum drbd_state_rv rv; | 881 | enum drbd_state_rv rv; |
882 | struct accept_wait_data ad = { | ||
883 | .tconn = tconn, | ||
884 | .door_bell = COMPLETION_INITIALIZER_ONSTACK(ad.door_bell), | ||
885 | }; | ||
858 | 886 | ||
859 | if (conn_request_state(tconn, NS(conn, C_WF_CONNECTION), CS_VERBOSE) < SS_SUCCESS) | 887 | if (conn_request_state(tconn, NS(conn, C_WF_CONNECTION), CS_VERBOSE) < SS_SUCCESS) |
860 | return -2; | 888 | return -2; |
@@ -873,6 +901,9 @@ static int conn_connect(struct drbd_tconn *tconn) | |||
873 | /* Assume that the peer only understands protocol 80 until we know better. */ | 901 | /* Assume that the peer only understands protocol 80 until we know better. */ |
874 | tconn->agreed_pro_version = 80; | 902 | tconn->agreed_pro_version = 80; |
875 | 903 | ||
904 | if (prepare_listen_socket(tconn, &ad)) | ||
905 | return 0; | ||
906 | |||
876 | do { | 907 | do { |
877 | struct socket *s; | 908 | struct socket *s; |
878 | 909 | ||
@@ -911,7 +942,7 @@ static int conn_connect(struct drbd_tconn *tconn) | |||
911 | } | 942 | } |
912 | 943 | ||
913 | retry: | 944 | retry: |
914 | s = drbd_wait_for_connect(tconn); | 945 | s = drbd_wait_for_connect(tconn, &ad); |
915 | if (s) { | 946 | if (s) { |
916 | try = receive_first_packet(tconn, s); | 947 | try = receive_first_packet(tconn, s); |
917 | drbd_socket_okay(&sock.socket); | 948 | drbd_socket_okay(&sock.socket); |
@@ -957,6 +988,9 @@ retry: | |||
957 | } | 988 | } |
958 | } while (1); | 989 | } while (1); |
959 | 990 | ||
991 | if (ad.s_listen) | ||
992 | sock_release(ad.s_listen); | ||
993 | |||
960 | sock.socket->sk->sk_reuse = 1; /* SO_REUSEADDR */ | 994 | sock.socket->sk->sk_reuse = 1; /* SO_REUSEADDR */ |
961 | msock.socket->sk->sk_reuse = 1; /* SO_REUSEADDR */ | 995 | msock.socket->sk->sk_reuse = 1; /* SO_REUSEADDR */ |
962 | 996 | ||
@@ -1052,6 +1086,8 @@ retry: | |||
1052 | return h; | 1086 | return h; |
1053 | 1087 | ||
1054 | out_release_sockets: | 1088 | out_release_sockets: |
1089 | if (ad.s_listen) | ||
1090 | sock_release(ad.s_listen); | ||
1055 | if (sock.socket) | 1091 | if (sock.socket) |
1056 | sock_release(sock.socket); | 1092 | sock_release(sock.socket); |
1057 | if (msock.socket) | 1093 | if (msock.socket) |