|
@@ -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)
|