From f571cabed0e2955cc3b3bf5ba89fb371780bf0c6 Mon Sep 17 00:00:00 2001
From: Philipp Reisner <philipp.reisner@linbit.com>
Date: Tue, 29 Jan 2019 12:45:31 +0100
Subject: [PATCH] drbd: Improve the resync controller for fast back end devices
 and network

So far we had the issue that the resync controller was not able to scale to
resync speeds possible with recent storage devices like NVMe drives.

In order to deal with such scenarios more gracefully, reschedule the
rs_make_request() function early in case the resync source fulfilled all
resync requests before the resync timer expired (rs_sect_in reaches the
value of rs_in_flight).

We scale the sect_in value with the "earliness". I.e. if it took only
half of the time, sect_in is multiplied by 2.

But it still consumes a whole slot from the the fifo. With that we narrow
the timing characteristics of the controller a bit, but no problem since
the data flows faster than expected anyways.
---
 drbd/drbd_int.h      |  1 +
 drbd/drbd_receiver.c | 21 ++++++++++++++++-----
 drbd/drbd_sender.c   | 21 +++++++++++++++++----
 3 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/drbd/drbd_int.h b/drbd/drbd_int.h
index 3e437a2681e4..026b9aa253f8 100644
--- a/drbd/drbd_int.h
+++ b/drbd/drbd_int.h
@@ -1172,6 +1172,7 @@ struct drbd_peer_device {
 	int rs_last_events;  /* counter of read or write "events" (unit sectors)
 			      * on the lower level device when we last looked. */
 	int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */
+	unsigned long rs_last_mk_req_jif;
 	unsigned long ov_left; /* in bits */
 	unsigned long ov_skipped; /* in bits */
 
diff --git a/drbd/drbd_receiver.c b/drbd/drbd_receiver.c
index de1fb9f36cdd..f031d9cb90fb 100644
--- a/drbd/drbd_receiver.c
+++ b/drbd/drbd_receiver.c
@@ -218,6 +218,17 @@ static struct page *__drbd_alloc_pages(unsigned int number, gfp_t gfp_mask)
 	return NULL;
 }
 
+static void rs_sectors_came_in(struct drbd_peer_device *peer_device, int size)
+{
+	int rs_sect_in = atomic_add_return(size >> 9, &peer_device->rs_sect_in);
+
+	/* In case resync runs faster than anticipated, run the resync_work early */
+	if (rs_sect_in >= peer_device->rs_in_flight)
+		drbd_queue_work_if_unqueued(
+			&peer_device->connection->sender_work,
+			&peer_device->resync_work);
+}
+
 /* kick lower level device, if we have more than (arbitrary number)
  * reference counts on it, which typically are locally submitted io
  * requests.  don't use unacked_cnt, so we speed up proto A and B, too. */
@@ -2273,7 +2284,7 @@ static int receive_RSDataReply(struct drbd_connection *connection, struct packet
 		drbd_send_ack_dp(peer_device, P_NEG_ACK, &d);
 	}
 
-	atomic_add(d.bi_size >> 9, &peer_device->rs_sect_in);
+	rs_sectors_came_in(peer_device, d.bi_size);
 
 	return err;
 }
@@ -3286,7 +3297,7 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet
 			peer_device->use_csums = true;
 		} else if (pi->cmd == P_OV_REPLY) {
 			/* track progress, we may need to throttle */
-			atomic_add(size >> 9, &peer_device->rs_sect_in);
+			rs_sectors_came_in(peer_device, size);
 			peer_req->w.cb = w_e_end_ov_reply;
 			dec_rs_pending(peer_device);
 			/* drbd_rs_begin_io done when we sent this request,
@@ -7520,7 +7531,7 @@ static int receive_rs_deallocated(struct drbd_connection *connection, struct pac
 		drbd_send_ack_ex(peer_device, P_NEG_ACK, sector, size, ID_SYNCER);
 	}
 
-	atomic_add(size >> 9, &peer_device->rs_sect_in);
+	rs_sectors_came_in(peer_device, size);
 
 	return err;
 }
@@ -8382,7 +8393,7 @@ static int got_IsInSync(struct drbd_connection *connection, struct packet_info *
 		put_ldev(device);
 	}
 	dec_rs_pending(peer_device);
-	atomic_add(blksize >> 9, &peer_device->rs_sect_in);
+	rs_sectors_came_in(peer_device, blksize);
 
 	return 0;
 }
@@ -8555,7 +8566,7 @@ static int got_NegRSDReply(struct drbd_connection *connection, struct packet_inf
 				mutex_unlock(&device->bm_resync_fo_mutex);
 			}
 
-			atomic_add(size >> 9, &peer_device->rs_sect_in);
+			rs_sectors_came_in(peer_device, size);
 			mod_timer(&peer_device->resync_timer, jiffies + RS_MAKE_REQS_INTV);
 			break;
 		default:
diff --git a/drbd/drbd_sender.c b/drbd/drbd_sender.c
index 17f25299bf87..ec8be16e28aa 100644
--- a/drbd/drbd_sender.c
+++ b/drbd/drbd_sender.c
@@ -554,7 +554,7 @@ struct fifo_buffer *fifo_alloc(int fifo_size)
 	return fb;
 }
 
-static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int sect_in)
+static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int sect_in, unsigned long duration)
 {
 	struct peer_device_conf *pdc;
 	unsigned int want;     /* The number of sectors we want in-flight */
@@ -566,6 +566,13 @@ static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int
 	int max_sect;
 	struct fifo_buffer *plan;
 
+	if (duration == 0)
+		duration = 1;
+	else if (duration > RS_MAKE_REQS_INTV * 10)
+		duration = RS_MAKE_REQS_INTV * 10;
+
+	sect_in = (u64)sect_in * RS_MAKE_REQS_INTV / duration;
+
 	pdc = rcu_dereference(peer_device->conf);
 	plan = rcu_dereference(peer_device->rs_plan_s);
 
@@ -575,7 +582,7 @@ static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int
 		want = ((pdc->resync_rate * 2 * RS_MAKE_REQS_INTV) / HZ) * steps;
 	} else { /* normal path */
 		want = pdc->c_fill_target ? pdc->c_fill_target :
-			sect_in * pdc->c_delay_target * HZ / (RS_MAKE_REQS_INTV * 10);
+			sect_in * pdc->c_delay_target * HZ / (duration * 10);
 	}
 
 	correction = want - peer_device->rs_in_flight - plan->total;
@@ -593,7 +600,7 @@ static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int
 	if (req_sect < 0)
 		req_sect = 0;
 
-	max_sect = (pdc->c_max_rate * 2 * RS_MAKE_REQS_INTV) / HZ;
+	max_sect = (pdc->c_max_rate * 2 * duration) / HZ;
 	if (req_sect > max_sect)
 		req_sect = max_sect;
 
@@ -609,17 +616,22 @@ static int drbd_rs_controller(struct drbd_peer_device *peer_device, unsigned int
 static int drbd_rs_number_requests(struct drbd_peer_device *peer_device)
 {
 	struct net_conf *nc;
+	unsigned long duration, now;
 	unsigned int sect_in;  /* Number of sectors that came in since the last turn */
 	int number, mxb;
 
 	sect_in = atomic_xchg(&peer_device->rs_sect_in, 0);
 	peer_device->rs_in_flight -= sect_in;
 
+	now = jiffies;
+	duration = now - peer_device->rs_last_mk_req_jif;
+	peer_device->rs_last_mk_req_jif = now;
+
 	rcu_read_lock();
 	nc = rcu_dereference(peer_device->connection->transport.net_conf);
 	mxb = nc ? nc->max_buffers : 0;
 	if (rcu_dereference(peer_device->rs_plan_s)->size) {
-		number = drbd_rs_controller(peer_device, sect_in) >> (BM_BLOCK_SHIFT - 9);
+		number = drbd_rs_controller(peer_device, sect_in, duration) >> (BM_BLOCK_SHIFT - 9);
 		peer_device->c_sync_rate = number * HZ * (BM_BLOCK_SIZE / 1024) / RS_MAKE_REQS_INTV;
 	} else {
 		peer_device->c_sync_rate = rcu_dereference(peer_device->conf)->resync_rate;
@@ -1816,6 +1828,7 @@ void drbd_rs_controller_reset(struct drbd_peer_device *peer_device)
 
 	atomic_set(&peer_device->rs_sect_in, 0);
 	atomic_set(&peer_device->device->rs_sect_ev, 0);  /* FIXME: ??? */
+	peer_device->rs_last_mk_req_jif = jiffies;
 	peer_device->rs_in_flight = 0;
 	peer_device->rs_last_events =
 		drbd_backing_bdev_events(peer_device->device->ldev->backing_bdev->bd_contains->bd_disk);
-- 
2.26.2

