From: Philipp Reisner Date: Thu, 12 Jul 2012 12:22:37 +0000 (+0200) Subject: drbd: Keep the listening socket open while trying to connect to the peer X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=7a426fd8d5af1d5e71cfcdf5ecbefbbad47a81fd;p=linux-beck.git drbd: Keep the listening socket open while trying to connect to the peer Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- 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: return sock; } -static struct socket *prepare_listen_socket(struct drbd_tconn *tconn) +struct accept_wait_data { + struct drbd_tconn *tconn; + struct socket *s_listen; + struct completion door_bell; + void (*original_sk_state_change)(struct sock *sk); + +}; + +static void incomming_connection(struct sock *sk) +{ + struct accept_wait_data *ad = sk->sk_user_data; + struct drbd_tconn *tconn = ad->tconn; + + if (sk->sk_state != TCP_ESTABLISHED) + conn_warn(tconn, "unexpected tcp state change. sk_state = %d\n", sk->sk_state); + + write_lock_bh(&sk->sk_callback_lock); + sk->sk_state_change = ad->original_sk_state_change; + sk->sk_user_data = NULL; + write_unlock_bh(&sk->sk_callback_lock); + + sk->sk_state_change(sk); + complete(&ad->door_bell); +} + +static int prepare_listen_socket(struct drbd_tconn *tconn, struct accept_wait_data *ad) { int err, sndbuf_size, rcvbuf_size, my_addr_len; struct sockaddr_in6 my_addr; @@ -678,7 +703,7 @@ static struct socket *prepare_listen_socket(struct drbd_tconn *tconn) nc = rcu_dereference(tconn->net_conf); if (!nc) { rcu_read_unlock(); - return NULL; + return -EIO; } sndbuf_size = nc->sndbuf_size; rcvbuf_size = nc->rcvbuf_size; @@ -703,12 +728,19 @@ static struct socket *prepare_listen_socket(struct drbd_tconn *tconn) if (err < 0) goto out; + ad->s_listen = s_listen; + write_lock_bh(&s_listen->sk->sk_callback_lock); + ad->original_sk_state_change = s_listen->sk->sk_state_change; + s_listen->sk->sk_state_change = incomming_connection; + s_listen->sk->sk_user_data = ad; + write_unlock_bh(&s_listen->sk->sk_callback_lock); + what = "listen"; err = s_listen->ops->listen(s_listen, 5); if (err < 0) goto out; - return s_listen; + return 0; out: if (s_listen) sock_release(s_listen); @@ -719,14 +751,13 @@ out: } } - return NULL; + return -EIO; } -static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn) +static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn, struct accept_wait_data *ad) { int timeo, connect_int, err = 0; struct socket *s_estab = NULL; - struct socket *s_listen; struct net_conf *nc; rcu_read_lock(); @@ -741,18 +772,11 @@ static struct socket *drbd_wait_for_connect(struct drbd_tconn *tconn) timeo = connect_int * HZ; timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */ - s_listen = prepare_listen_socket(tconn); - if (!s_listen) - goto out; - - s_listen->sk->sk_rcvtimeo = timeo; - s_listen->sk->sk_sndtimeo = timeo; - - err = kernel_accept(s_listen, &s_estab, 0); + err = wait_for_completion_interruptible_timeout(&ad->door_bell, timeo); + if (err <= 0) + return NULL; -out: - if (s_listen) - sock_release(s_listen); + err = kernel_accept(ad->s_listen, &s_estab, 0); if (err < 0) { if (err != -EAGAIN && err != -EINTR && err != -ERESTARTSYS) { conn_err(tconn, "accept failed, err = %d\n", err); @@ -855,6 +879,10 @@ static int conn_connect(struct drbd_tconn *tconn) int vnr, timeout, try, h, ok; bool discard_my_data; enum drbd_state_rv rv; + struct accept_wait_data ad = { + .tconn = tconn, + .door_bell = COMPLETION_INITIALIZER_ONSTACK(ad.door_bell), + }; if (conn_request_state(tconn, NS(conn, C_WF_CONNECTION), CS_VERBOSE) < SS_SUCCESS) return -2; @@ -873,6 +901,9 @@ static int conn_connect(struct drbd_tconn *tconn) /* Assume that the peer only understands protocol 80 until we know better. */ tconn->agreed_pro_version = 80; + if (prepare_listen_socket(tconn, &ad)) + return 0; + do { struct socket *s; @@ -911,7 +942,7 @@ static int conn_connect(struct drbd_tconn *tconn) } retry: - s = drbd_wait_for_connect(tconn); + s = drbd_wait_for_connect(tconn, &ad); if (s) { try = receive_first_packet(tconn, s); drbd_socket_okay(&sock.socket); @@ -957,6 +988,9 @@ retry: } } while (1); + if (ad.s_listen) + sock_release(ad.s_listen); + sock.socket->sk->sk_reuse = 1; /* SO_REUSEADDR */ msock.socket->sk->sk_reuse = 1; /* SO_REUSEADDR */ @@ -1052,6 +1086,8 @@ retry: return h; out_release_sockets: + if (ad.s_listen) + sock_release(ad.s_listen); if (sock.socket) sock_release(sock.socket); if (msock.socket)