in drbd/drbd_receiver.c [4435:4630]
static int receive_state(struct drbd_connection *connection, struct packet_info *pi)
{
struct drbd_peer_device *peer_device;
struct drbd_device *device;
struct p_state *p = pi->data;
union drbd_state os, ns, peer_state;
enum drbd_disk_state real_peer_disk;
enum chg_state_flags cs_flags;
int rv;
peer_device = conn_peer_device(connection, pi->vnr);
if (!peer_device)
return config_unknown_volume(connection, pi);
device = peer_device->device;
peer_state.i = be32_to_cpu(p->state);
real_peer_disk = peer_state.disk;
if (peer_state.disk == D_NEGOTIATING) {
real_peer_disk = device->p_uuid[UI_FLAGS] & 4 ? D_INCONSISTENT : D_CONSISTENT;
drbd_info(device, "real peer disk state = %s\n", drbd_disk_str(real_peer_disk));
}
spin_lock_irq(&device->resource->req_lock);
retry:
os = ns = drbd_read_state(device);
spin_unlock_irq(&device->resource->req_lock);
/* If some other part of the code (ack_receiver thread, timeout)
* already decided to close the connection again,
* we must not "re-establish" it here. */
if (os.conn <= C_TEAR_DOWN)
return -ECONNRESET;
/* If this is the "end of sync" confirmation, usually the peer disk
* transitions from D_INCONSISTENT to D_UP_TO_DATE. For empty (0 bits
* set) resync started in PausedSyncT, or if the timing of pause-/
* unpause-sync events has been "just right", the peer disk may
* transition from D_CONSISTENT to D_UP_TO_DATE as well.
*/
if ((os.pdsk == D_INCONSISTENT || os.pdsk == D_CONSISTENT) &&
real_peer_disk == D_UP_TO_DATE &&
os.conn > C_CONNECTED && os.disk == D_UP_TO_DATE) {
/* If we are (becoming) SyncSource, but peer is still in sync
* preparation, ignore its uptodate-ness to avoid flapping, it
* will change to inconsistent once the peer reaches active
* syncing states.
* It may have changed syncer-paused flags, however, so we
* cannot ignore this completely. */
if (peer_state.conn > C_CONNECTED &&
peer_state.conn < C_SYNC_SOURCE)
real_peer_disk = D_INCONSISTENT;
/* if peer_state changes to connected at the same time,
* it explicitly notifies us that it finished resync.
* Maybe we should finish it up, too? */
else if (os.conn >= C_SYNC_SOURCE &&
peer_state.conn == C_CONNECTED) {
if (drbd_bm_total_weight(device) <= device->rs_failed)
drbd_resync_finished(device);
return 0;
}
}
/* explicit verify finished notification, stop sector reached. */
if (os.conn == C_VERIFY_T && os.disk == D_UP_TO_DATE &&
peer_state.conn == C_CONNECTED && real_peer_disk == D_UP_TO_DATE) {
ov_out_of_sync_print(device);
drbd_resync_finished(device);
return 0;
}
/* peer says his disk is inconsistent, while we think it is uptodate,
* and this happens while the peer still thinks we have a sync going on,
* but we think we are already done with the sync.
* We ignore this to avoid flapping pdsk.
* This should not happen, if the peer is a recent version of drbd. */
if (os.pdsk == D_UP_TO_DATE && real_peer_disk == D_INCONSISTENT &&
os.conn == C_CONNECTED && peer_state.conn > C_SYNC_SOURCE)
real_peer_disk = D_UP_TO_DATE;
if (ns.conn == C_WF_REPORT_PARAMS)
ns.conn = C_CONNECTED;
if (peer_state.conn == C_AHEAD)
ns.conn = C_BEHIND;
/* TODO:
* if (primary and diskless and peer uuid != effective uuid)
* abort attach on peer;
*
* If this node does not have good data, was already connected, but
* the peer did a late attach only now, trying to "negotiate" with me,
* AND I am currently Primary, possibly frozen, with some specific
* "effective" uuid, this should never be reached, really, because
* we first send the uuids, then the current state.
*
* In this scenario, we already dropped the connection hard
* when we received the unsuitable uuids (receive_uuids().
*
* Should we want to change this, that is: not drop the connection in
* receive_uuids() already, then we would need to add a branch here
* that aborts the attach of "unsuitable uuids" on the peer in case
* this node is currently Diskless Primary.
*/
if (device->p_uuid && peer_state.disk >= D_NEGOTIATING &&
get_ldev_if_state(device, D_NEGOTIATING)) {
int cr; /* consider resync */
/* if we established a new connection */
cr = (os.conn < C_CONNECTED);
/* if we had an established connection
* and one of the nodes newly attaches a disk */
cr |= (os.conn == C_CONNECTED &&
(peer_state.disk == D_NEGOTIATING ||
os.disk == D_NEGOTIATING));
/* if we have both been inconsistent, and the peer has been
* forced to be UpToDate with --force */
cr |= test_bit(CONSIDER_RESYNC, &device->flags);
/* if we had been plain connected, and the admin requested to
* start a sync by "invalidate" or "invalidate-remote" */
cr |= (os.conn == C_CONNECTED &&
(peer_state.conn >= C_STARTING_SYNC_S &&
peer_state.conn <= C_WF_BITMAP_T));
if (cr)
ns.conn = drbd_sync_handshake(peer_device, peer_state.role, real_peer_disk);
put_ldev(device);
if (ns.conn == C_MASK) {
ns.conn = C_CONNECTED;
if (device->state.disk == D_NEGOTIATING) {
drbd_force_state(device, NS(disk, D_FAILED));
} else if (peer_state.disk == D_NEGOTIATING) {
drbd_err(device, "Disk attach process on the peer node was aborted.\n");
peer_state.disk = D_DISKLESS;
real_peer_disk = D_DISKLESS;
} else {
if (test_and_clear_bit(CONN_DRY_RUN, &peer_device->connection->flags))
return -EIO;
D_ASSERT(device, os.conn == C_WF_REPORT_PARAMS);
conn_request_state(peer_device->connection, NS(conn, C_DISCONNECTING), CS_HARD);
return -EIO;
}
}
}
spin_lock_irq(&device->resource->req_lock);
if (os.i != drbd_read_state(device).i)
goto retry;
clear_bit(CONSIDER_RESYNC, &device->flags);
ns.peer = peer_state.role;
ns.pdsk = real_peer_disk;
ns.peer_isp = (peer_state.aftr_isp | peer_state.user_isp);
if ((ns.conn == C_CONNECTED || ns.conn == C_WF_BITMAP_S) && ns.disk == D_NEGOTIATING)
ns.disk = device->new_state_tmp.disk;
cs_flags = CS_VERBOSE + (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED ? 0 : CS_HARD);
if (ns.pdsk == D_CONSISTENT && drbd_suspended(device) && ns.conn == C_CONNECTED && os.conn < C_CONNECTED &&
test_bit(NEW_CUR_UUID, &device->flags)) {
/* Do not allow tl_restart(RESEND) for a rebooted peer. We can only allow this
for temporal network outages! */
spin_unlock_irq(&device->resource->req_lock);
drbd_err(device, "Aborting Connect, can not thaw IO with an only Consistent peer\n");
tl_clear(peer_device->connection);
drbd_uuid_new_current(device);
clear_bit(NEW_CUR_UUID, &device->flags);
conn_request_state(peer_device->connection, NS2(conn, C_PROTOCOL_ERROR, susp, 0), CS_HARD);
return -EIO;
}
rv = _drbd_set_state(device, ns, cs_flags, NULL);
ns = drbd_read_state(device);
spin_unlock_irq(&device->resource->req_lock);
if (rv < SS_SUCCESS) {
conn_request_state(peer_device->connection, NS(conn, C_DISCONNECTING), CS_HARD);
return -EIO;
}
if (os.conn > C_WF_REPORT_PARAMS) {
if (ns.conn > C_CONNECTED && peer_state.conn <= C_CONNECTED &&
peer_state.disk != D_NEGOTIATING ) {
/* we want resync, peer has not yet decided to sync... */
/* Nowadays only used when forcing a node into primary role and
setting its disk to UpToDate with that */
drbd_send_uuids(peer_device);
drbd_send_current_state(peer_device);
}
}
clear_bit(DISCARD_MY_DATA, &device->flags);
drbd_md_sync(device); /* update connected indicator, la_size_sect, ... */
return 0;
}