in drbd/drbd_receiver.c [924:1154]
static int conn_connect(struct drbd_connection *connection)
{
struct drbd_socket sock, msock;
struct drbd_peer_device *peer_device;
struct net_conf *nc;
int vnr, timeout, h;
bool discard_my_data, ok;
enum drbd_state_rv rv;
struct accept_wait_data ad = {
.connection = connection,
.door_bell = COMPLETION_INITIALIZER_ONSTACK(ad.door_bell),
};
clear_bit(DISCONNECT_SENT, &connection->flags);
if (conn_request_state(connection, NS(conn, C_WF_CONNECTION), CS_VERBOSE) < SS_SUCCESS)
return -2;
mutex_init(&sock.mutex);
sock.sbuf = connection->data.sbuf;
sock.rbuf = connection->data.rbuf;
sock.socket = NULL;
mutex_init(&msock.mutex);
msock.sbuf = connection->meta.sbuf;
msock.rbuf = connection->meta.rbuf;
msock.socket = NULL;
/* Assume that the peer only understands protocol 80 until we know better. */
connection->agreed_pro_version = 80;
if (prepare_listen_socket(connection, &ad))
return 0;
do {
struct socket *s;
s = drbd_try_connect(connection);
if (s) {
if (!sock.socket) {
sock.socket = s;
send_first_packet(connection, &sock, P_INITIAL_DATA);
} else if (!msock.socket) {
clear_bit(RESOLVE_CONFLICTS, &connection->flags);
msock.socket = s;
send_first_packet(connection, &msock, P_INITIAL_META);
} else {
drbd_err(connection, "Logic error in conn_connect()\n");
goto out_release_sockets;
}
}
if (connection_established(connection, &sock.socket, &msock.socket))
break;
retry:
s = drbd_wait_for_connect(connection, &ad);
if (s) {
int fp = receive_first_packet(connection, s);
drbd_socket_okay(&sock.socket);
drbd_socket_okay(&msock.socket);
switch (fp) {
case P_INITIAL_DATA:
if (sock.socket) {
drbd_warn(connection, "initial packet S crossed\n");
sock_release(sock.socket);
sock.socket = s;
goto randomize;
}
sock.socket = s;
break;
case P_INITIAL_META:
set_bit(RESOLVE_CONFLICTS, &connection->flags);
if (msock.socket) {
drbd_warn(connection, "initial packet M crossed\n");
sock_release(msock.socket);
msock.socket = s;
goto randomize;
}
msock.socket = s;
break;
default:
drbd_warn(connection, "Error receiving initial packet\n");
sock_release(s);
randomize:
if (prandom_u32() & 1)
goto retry;
}
}
if (connection->cstate <= C_DISCONNECTING)
goto out_release_sockets;
if (signal_pending(current)) {
flush_signals(current);
smp_rmb();
if (get_t_state(&connection->receiver) == EXITING)
goto out_release_sockets;
}
ok = connection_established(connection, &sock.socket, &msock.socket);
} while (!ok);
if (ad.s_listen)
sock_release(ad.s_listen);
sock.socket->sk->sk_reuse = SK_CAN_REUSE; /* SO_REUSEADDR */
msock.socket->sk->sk_reuse = SK_CAN_REUSE; /* SO_REUSEADDR */
sock.socket->sk->sk_allocation = GFP_NOIO;
msock.socket->sk->sk_allocation = GFP_NOIO;
sock.socket->sk->sk_priority = TC_PRIO_INTERACTIVE_BULK;
msock.socket->sk->sk_priority = TC_PRIO_INTERACTIVE;
/* NOT YET ...
* sock.socket->sk->sk_sndtimeo = connection->net_conf->timeout*HZ/10;
* sock.socket->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
* first set it to the P_CONNECTION_FEATURES timeout,
* which we set to 4x the configured ping_timeout. */
rcu_read_lock();
nc = rcu_dereference(connection->net_conf);
sock.socket->sk->sk_sndtimeo =
sock.socket->sk->sk_rcvtimeo = nc->ping_timeo*4*HZ/10;
msock.socket->sk->sk_rcvtimeo = nc->ping_int*HZ;
timeout = nc->timeout * HZ / 10;
discard_my_data = nc->discard_my_data;
rcu_read_unlock();
msock.socket->sk->sk_sndtimeo = timeout;
/* we don't want delays.
* we use TCP_CORK where appropriate, though */
tcp_sock_set_nodelay(sock.socket->sk);
tcp_sock_set_nodelay(msock.socket->sk);
connection->data.socket = sock.socket;
connection->meta.socket = msock.socket;
connection->last_received = jiffies;
h = drbd_do_features(connection);
if (h <= 0)
return h;
if (connection->cram_hmac_tfm) {
/* drbd_request_state(device, NS(conn, WFAuth)); */
switch (drbd_do_auth(connection)) {
case -1:
drbd_err(connection, "Authentication of peer failed\n");
return -1;
case 0:
drbd_err(connection, "Authentication of peer failed, trying again.\n");
return 0;
}
}
connection->data.socket->sk->sk_sndtimeo = timeout;
connection->data.socket->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
if (drbd_send_protocol(connection) == -EOPNOTSUPP)
return -1;
/* Prevent a race between resync-handshake and
* being promoted to Primary.
*
* Grab and release the state mutex, so we know that any current
* drbd_set_role() is finished, and any incoming drbd_set_role
* will see the STATE_SENT flag, and wait for it to be cleared.
*/
idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
mutex_lock(peer_device->device->state_mutex);
/* avoid a race with conn_request_state( C_DISCONNECTING ) */
spin_lock_irq(&connection->resource->req_lock);
set_bit(STATE_SENT, &connection->flags);
spin_unlock_irq(&connection->resource->req_lock);
idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
mutex_unlock(peer_device->device->state_mutex);
rcu_read_lock();
idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
struct drbd_device *device = peer_device->device;
kref_get(&device->kref);
rcu_read_unlock();
if (discard_my_data)
set_bit(DISCARD_MY_DATA, &device->flags);
else
clear_bit(DISCARD_MY_DATA, &device->flags);
drbd_connected(peer_device);
kref_put(&device->kref, drbd_destroy_device);
rcu_read_lock();
}
rcu_read_unlock();
rv = conn_request_state(connection, NS(conn, C_WF_REPORT_PARAMS), CS_VERBOSE);
if (rv < SS_SUCCESS || connection->cstate != C_WF_REPORT_PARAMS) {
clear_bit(STATE_SENT, &connection->flags);
return 0;
}
drbd_thread_start(&connection->ack_receiver);
/* opencoded create_singlethread_workqueue(),
* to be able to use format string arguments */
connection->ack_sender =
alloc_ordered_workqueue("drbd_as_%s", WQ_MEM_RECLAIM, connection->resource->name);
if (!connection->ack_sender) {
drbd_err(connection, "Failed to create workqueue ack_sender\n");
return 0;
}
mutex_lock(&connection->resource->conf_update);
/* The discard_my_data flag is a single-shot modifier to the next
* connection attempt, the handshake of which is now well underway.
* No need for rcu style copying of the whole struct
* just to clear a single value. */
connection->net_conf->discard_my_data = 0;
mutex_unlock(&connection->resource->conf_update);
return h;
out_release_sockets:
if (ad.s_listen)
sock_release(ad.s_listen);
if (sock.socket)
sock_release(sock.socket);
if (msock.socket)
sock_release(msock.socket);
return -1;
}