From 0a21e5155f011c96620e76c31b6ca335cb99522d Mon Sep 17 00:00:00 2001
From: Joel Colledge <joel.colledge@linbit.com>
Date: Thu, 27 Jun 2019 16:31:26 +0200
Subject: [PATCH] drbd: Scan bitmap per-peer instead of once for the device

This is relevant when we have multiple sync sources from which we sync
in turn. For instance, when we resync from a secondary and have a
primary in paused sync source state. When the unstable resync from the
secondary is finished, we then switch to sync from the primary.

This fixes an issue where resync stalled as follows:
* Unstable resync with a secondary peer is ongoing
* We exchange bitmaps with the primary
* While exchanging bitmaps, P_PEERS_IN_SYNC is received as part of the
  ongoing resync
* When the bitmap exchange is complete, bits in the regions for which
  P_PEERS_IN_SYNC were received are marked dirty
* The unstable resync finishes
* We start to sync from the primary, starting from the device level
  resync position from the first resync, just rounded down
* The sync stalls because it reaches the end of the bitmap without
  syncing the dirty bits from the bitmap exchange
---
 drbd/drbd_debugfs.c  |  2 +-
 drbd/drbd_int.h      |  4 ++--
 drbd/drbd_main.c     |  3 ++-
 drbd/drbd_receiver.c | 14 +++++++-------
 drbd/drbd_sender.c   | 21 ++++++++++-----------
 drbd/drbd_state.c    |  6 ------
 6 files changed, 22 insertions(+), 28 deletions(-)

diff --git a/drbd/drbd_debugfs.c b/drbd/drbd_debugfs.c
index 96403c6c8873..e1f911dc6f6d 100644
--- a/drbd/drbd_debugfs.c
+++ b/drbd/drbd_debugfs.c
@@ -1552,7 +1552,7 @@ static void drbd_syncer_progress(struct drbd_peer_device *pd, struct seq_file *s
 			if (verify_can_do_stop_sector(pd))
 				stop_sector = pd->ov_stop_sector;
 		} else
-			bit_pos = pd->device->bm_resync_fo;
+			bit_pos = pd->resync_next_bit;
 		/* Total sectors may be slightly off for oddly
 		 * sized devices. So what. */
 		seq_printf(seq,
diff --git a/drbd/drbd_int.h b/drbd/drbd_int.h
index 47844e36f634..8d318008f177 100644
--- a/drbd/drbd_int.h
+++ b/drbd/drbd_int.h
@@ -1150,6 +1150,8 @@ struct drbd_peer_device {
 	unsigned int resync_wenr;
 	enum drbd_disk_state resync_finished_pdsk; /* Finished while starting resync */
 	int resync_again; /* decided to resync again while resync running */
+	unsigned long resync_next_bit; /* bitmap bit to search from for next resync request */
+	struct mutex resync_next_bit_mutex;
 
 	atomic_t ap_pending_cnt; /* AP data packets on the wire, ack expected */
 	atomic_t unacked_cnt;	 /* Need to send replies for */
@@ -1321,8 +1323,6 @@ struct drbd_device {
 	struct list_head pending_completion[2];
 
 	struct drbd_bitmap *bitmap;
-	unsigned long bm_resync_fo; /* bit offset for drbd_bm_find_next */
-	struct mutex bm_resync_fo_mutex;
 
 	int open_rw_cnt, open_ro_cnt;
 	/* FIXME clean comments, restructure so it is more obvious which
diff --git a/drbd/drbd_main.c b/drbd/drbd_main.c
index 0310395e58d1..062c5ca11982 100644
--- a/drbd/drbd_main.c
+++ b/drbd/drbd_main.c
@@ -3650,6 +3650,8 @@ struct drbd_peer_device *create_peer_device(struct drbd_device *device, struct d
 	INIT_LIST_HEAD(&peer_device->propagate_uuids_work.list);
 	peer_device->propagate_uuids_work.cb = w_send_uuids;
 
+	mutex_init(&peer_device->resync_next_bit_mutex);
+
 	atomic_set(&peer_device->ap_pending_cnt, 0);
 	atomic_set(&peer_device->unacked_cnt, 0);
 	atomic_set(&peer_device->rs_pending_cnt, 0);
@@ -3733,7 +3735,6 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
 	spin_lock_init(&device->timing_lock);
 #endif
 	spin_lock_init(&device->al_lock);
-	mutex_init(&device->bm_resync_fo_mutex);
 
 	INIT_LIST_HEAD(&device->pending_master_completion[0]);
 	INIT_LIST_HEAD(&device->pending_master_completion[1]);
diff --git a/drbd/drbd_receiver.c b/drbd/drbd_receiver.c
index 2ebafdeb5ee7..c3741cda5c35 100644
--- a/drbd/drbd_receiver.c
+++ b/drbd/drbd_receiver.c
@@ -7344,7 +7344,7 @@ static int receive_out_of_sync(struct drbd_connection *connection, struct packet
 	conn_wait_active_ee_empty_or_disconnect(connection);
 	conn_wait_done_ee_empty_or_disconnect(connection);
 
-	mutex_lock(&device->bm_resync_fo_mutex);
+	mutex_lock(&peer_device->resync_next_bit_mutex);
 	switch (peer_device->repl_state[NOW]) {
 	case L_WF_SYNC_UUID:
 	case L_WF_BITMAP_T:
@@ -7352,8 +7352,8 @@ static int receive_out_of_sync(struct drbd_connection *connection, struct packet
 		break;
 	case L_SYNC_TARGET:
 		bit = BM_SECT_TO_BIT(sector);
-		if (bit < device->bm_resync_fo)
-			device->bm_resync_fo = bit;
+		if (bit < peer_device->resync_next_bit)
+			peer_device->resync_next_bit = bit;
 		break;
 	default:
 #if 0
@@ -7371,7 +7371,7 @@ static int receive_out_of_sync(struct drbd_connection *connection, struct packet
 
 	drbd_set_out_of_sync(peer_device, sector, be32_to_cpu(p->blksize));
 
-	mutex_unlock(&device->bm_resync_fo_mutex);
+	mutex_unlock(&peer_device->resync_next_bit_mutex);
 
 	return 0;
 }
@@ -8625,9 +8625,9 @@ static int got_NegRSDReply(struct drbd_connection *connection, struct packet_inf
 				verify_skipped_block(peer_device, sector, size);
 			} else {
 				bit = BM_SECT_TO_BIT(sector);
-				mutex_lock(&device->bm_resync_fo_mutex);
-				device->bm_resync_fo = min(device->bm_resync_fo, bit);
-				mutex_unlock(&device->bm_resync_fo_mutex);
+				mutex_lock(&peer_device->resync_next_bit_mutex);
+				peer_device->resync_next_bit = min(peer_device->resync_next_bit, bit);
+				mutex_unlock(&peer_device->resync_next_bit_mutex);
 			}
 
 			rs_sectors_came_in(peer_device, size);
diff --git a/drbd/drbd_sender.c b/drbd/drbd_sender.c
index 02cf97a22901..700cc4d85700 100644
--- a/drbd/drbd_sender.c
+++ b/drbd/drbd_sender.c
@@ -469,9 +469,8 @@ int w_resync_timer(struct drbd_work *w, int cancel)
 {
 	struct drbd_peer_device *peer_device =
 		container_of(w, struct drbd_peer_device, resync_work);
-	struct drbd_device *device = peer_device->device;
 
-	mutex_lock(&device->bm_resync_fo_mutex);
+	mutex_lock(&peer_device->resync_next_bit_mutex);
 	switch (peer_device->repl_state[NOW]) {
 	case L_VERIFY_S:
 		make_ov_request(peer_device, cancel);
@@ -485,7 +484,7 @@ int w_resync_timer(struct drbd_work *w, int cancel)
 	default:
 		break;
 	}
-	mutex_unlock(&device->bm_resync_fo_mutex);
+	mutex_unlock(&peer_device->resync_next_bit_mutex);
 
 	return 0;
 }
@@ -747,22 +746,22 @@ static int make_resync_request(struct drbd_peer_device *peer_device, int cancel)
 
 next_sector:
 		size = BM_BLOCK_SIZE;
-		bit  = drbd_bm_find_next(peer_device, device->bm_resync_fo);
+		bit  = drbd_bm_find_next(peer_device, peer_device->resync_next_bit);
 
 		if (bit == DRBD_END_OF_BITMAP) {
-			device->bm_resync_fo = drbd_bm_bits(device);
+			peer_device->resync_next_bit = drbd_bm_bits(device);
 			goto request_done;
 		}
 
 		sector = BM_BIT_TO_SECT(bit);
 
 		if (drbd_try_rs_begin_io(peer_device, sector, true)) {
-			device->bm_resync_fo = bit;
+			peer_device->resync_next_bit = bit;
 			goto request_done;
 		}
 
 		if (unlikely(drbd_bm_test_bit(peer_device, bit) == 0)) {
-			device->bm_resync_fo = bit + 1;
+			peer_device->resync_next_bit = bit + 1;
 			drbd_rs_complete_io(peer_device, sector);
 			goto next_sector;
 		}
@@ -803,7 +802,7 @@ next_sector:
 			i++;
 		}
 		/* set the offset to start the next drbd_bm_find_next from */
-		device->bm_resync_fo = bit + 1;
+		peer_device->resync_next_bit = bit + 1;
 
 		/* adjust very last sectors, in case we are oddly sized */
 		if (sector + (size>>9) > capacity)
@@ -816,7 +815,7 @@ next_sector:
 				return -EIO;
 			case -EAGAIN: /* allocation failed, or ldev busy */
 				drbd_rs_complete_io(peer_device, sector);
-				device->bm_resync_fo = BM_SECT_TO_BIT(sector);
+				peer_device->resync_next_bit = BM_SECT_TO_BIT(sector);
 				i = rollback_i;
 				goto request_done;
 			case 0:
@@ -845,7 +844,7 @@ request_done:
 	/* ... but do a correction, in case we had to break/goto request_done; */
 	peer_device->rs_in_flight -= (number - i) * BM_SECT_PER_BIT;
 
-	if (device->bm_resync_fo >= drbd_bm_bits(device)) {
+	if (peer_device->resync_next_bit >= drbd_bm_bits(device)) {
 		/* last syncer _request_ was sent,
 		 * but the P_RS_DATA_REPLY not yet received.  sync will end (and
 		 * next sync group will resume), as soon as we receive the last
@@ -2088,7 +2087,7 @@ skip_helper:
 		     (unsigned long) peer_device->rs_total << (BM_BLOCK_SHIFT-10),
 		     (unsigned long) peer_device->rs_total);
 		if (side == L_SYNC_TARGET) {
-			device->bm_resync_fo = 0;
+			peer_device->resync_next_bit = 0;
 			peer_device->use_csums = use_checksum_based_resync(connection, device);
 		} else {
 			peer_device->use_csums = false;
diff --git a/drbd/drbd_state.c b/drbd/drbd_state.c
index 449e69b8449b..c3371e18f2b6 100644
--- a/drbd/drbd_state.c
+++ b/drbd/drbd_state.c
@@ -2345,12 +2345,6 @@ static void finish_state_change(struct drbd_resource *resource, struct completio
 						  -(long)peer_device->rs_mark_time[peer_device->rs_last_mark];
 				if (repl_state[NEW] == L_SYNC_TARGET)
 					mod_timer(&peer_device->resync_timer, jiffies);
-
-				device->bm_resync_fo &= ~BM_BLOCKS_PER_BM_EXT_MASK;
-				/* Setting the find_offset back is necessary when switching resync from
-				   one peer to the other. Since in the bitmap of the new peer, there
-				   might be bits before the current find_offset. Since the peer is
-				   notified about the resync progress in BM_EXT sized chunks. */
 			}
 
 			if ((repl_state[OLD] == L_SYNC_TARGET  || repl_state[OLD] == L_SYNC_SOURCE) &&
-- 
2.26.2

