Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a426fd8 authored by Philipp Reisner's avatar Philipp Reisner
Browse files

drbd: Keep the listening socket open while trying to connect to the peer

parent 1f3e509b
Loading
Loading
Loading
Loading
+54 −18
Original line number Diff line number Diff line
@@ -666,7 +666,32 @@ static struct socket *drbd_try_connect(struct drbd_tconn *tconn)
	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 @@ static struct socket *prepare_listen_socket(struct drbd_tconn *tconn)
		}
	}

	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 @@ static int conn_connect(struct drbd_tconn *tconn)
		}
	} 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 @@ static int conn_connect(struct drbd_tconn *tconn)
	return h;

out_release_sockets:
	if (ad.s_listen)
		sock_release(ad.s_listen);
	if (sock.socket)
		sock_release(sock.socket);
	if (msock.socket)