[Devel] [PATCH RH7 1/2] ploop: io_kaio: Introduce 4K discard_granuality with 1M clearing indexes when possible

Kirill Tkhai ktkhai at virtuozzo.com
Thu Aug 20 16:28:04 MSK 2020


1)In case of discard size is less then 1 cluster,
  a small hole is punched.
2)In case of discard request covers the whole cluster,
  index in BAT is also cleared.

Since small 4K holes require 4K discard_granuality,
this makes impossible to use block level to make
discard requests 1 cluster aligned. This requires
us to split the requests manually, so force_split_discard_reqs
parameter was introduced.

See comments in kaio_queue_settings() for details.

https://jira.sw.ru/browse/PSBM-105347

Signed-off-by: Kirill Tkhai <ktkhai at virtuozzo.com>
---
 drivers/block/ploop/dev.c     |   12 ++++++++----
 drivers/block/ploop/io_kaio.c |   36 +++++++++++++++++++++++++++++++++++-
 include/linux/ploop/ploop.h   |    1 +
 3 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/block/ploop/dev.c b/drivers/block/ploop/dev.c
index 5f1a0c289a26..25b516cad9cc 100644
--- a/drivers/block/ploop/dev.c
+++ b/drivers/block/ploop/dev.c
@@ -979,7 +979,8 @@ static void ploop_make_request(struct request_queue *q, struct bio *bio)
 	 * bio layer assumes that it can prepare single-page bio
 	 * not depending on any alignment constraints. So be it.
 	 */
-	if (!(bio->bi_rw & REQ_DISCARD) && bio->bi_size &&
+	if ((!(bio->bi_rw & REQ_DISCARD) || plo->force_split_discard_reqs) &&
+	    bio->bi_size &&
 	    (bio->bi_sector >> cluster_log) !=
 	    ((bio->bi_sector + (bio->bi_size >> 9) - 1) >> cluster_log)) {
 		struct bio_pair *bp;
@@ -988,7 +989,8 @@ static void ploop_make_request(struct request_queue *q, struct bio *bio)
 
 		plo->st.bio_splits++;
 
-		BUG_ON(bio->bi_vcnt != 1 || bio->bi_idx != 0);
+		if (!(bio->bi_rw & REQ_DISCARD))
+			BUG_ON(bio->bi_vcnt != 1 || bio->bi_idx != 0);
 
 		bp = bio_split(bio, first_sectors);
 		ploop_make_request(q, &bp->bio1);
@@ -2298,7 +2300,7 @@ static bool ploop_can_issue_discard(struct ploop_device *plo,
 	if (!list_is_singular(&plo->map.delta_list))
 		return false;
 
-	return whole_block(plo, preq);
+	return whole_block(plo, preq) || plo->force_split_discard_reqs;
 }
 
 static void
@@ -2568,7 +2570,8 @@ ploop_entry_request(struct ploop_request * preq)
 				}
 				preq->iblock = iblk;
 				if (!(preq->req_rw & REQ_DISCARD) ||
-				    (delta->ops->capability & PLOOP_FMT_CAP_IDENTICAL))
+				    (delta->ops->capability & PLOOP_FMT_CAP_IDENTICAL) ||
+				    !whole_block(plo, preq))
 					preq->eng_state = PLOOP_E_COMPLETE;
 				else
 					preq->eng_state = PLOOP_E_DATA_WBI;
@@ -4205,6 +4208,7 @@ static int ploop_start(struct ploop_device * plo, struct block_device *bdev)
 	blk_queue_merge_bvec(q, ploop_merge_bvec);
 	blk_queue_flush(q, REQ_FLUSH);
 
+	plo->force_split_discard_reqs = false;
 	top_delta->io.ops->queue_settings(&top_delta->io, q);
 	/* REQ_WRITE_SAME is not supported */
 	blk_queue_max_write_same_sectors(q, 0);
diff --git a/drivers/block/ploop/io_kaio.c b/drivers/block/ploop/io_kaio.c
index 2052dac43c1a..365e2e3850d4 100644
--- a/drivers/block/ploop/io_kaio.c
+++ b/drivers/block/ploop/io_kaio.c
@@ -1140,7 +1140,41 @@ static void kaio_queue_settings(struct ploop_io * io, struct request_queue * q)
 	if (inode->i_sb->s_magic == EXT4_SUPER_MAGIC) {
 		WARN_ON(!kaio_backed_ext4);
 		blk_queue_stack_limits(q, bdev_get_queue(io->files.bdev));
-		ploop_set_discard_limits(io->plo);
+		/*
+		 * There is no a way to force block engine to split a request
+		 * to fit a single cluster, when discard granuality is 4K
+		 * (inherited from fs block size in blk_queue_stack_limits()).
+		 * So, ploop_make_request() splits them.
+		 */
+		io->plo->force_split_discard_reqs = true;
+		/*
+		 * Why not (1 << io->plo->cluster_log)?
+		 * Someone may want to clear indexes in case of a request
+		 * is big enough to fit the whole cluster.
+		 * In case of max_discard_sectors is 1 cluster, a request
+		 * for [cluster_start - 4K, cluster_start + cluster_size)
+		 * at block level will be splitted in two requests:
+		 *
+		 * [cluster_start - 4K, cluster_start + cluster_size - 4K)
+		 * [cluster_start + cluster_size - 4K, cluster_start + cluster_size)
+		 *
+		 * Then, ploop_make_request() splits the first of them in two
+		 * to fit a single cluster, so all three requests will be smaller
+		 * then 1 cluster, and no index will be cleared.
+		 *
+		 * Note, this does not solve a problem, when a request covers
+		 * 3 clusters: [cluster_start - 4K, cluster_start + 2 * cluster_size],
+		 * so the third cluster's index will remain. This will require
+		 * unlimited max_discard_sectors and splitting every request
+		 * in ploop_make_request(). We don't want that in that context.
+		 *
+		 * But even in current view, this makes indexes to be cleared
+		 * more frequently, and index-clearing code will be tested better.
+		 *
+		 * Anyway, in general this may be an excess functionality.
+		 * If it's so, it will be dropped later.
+		 */
+		q->limits.max_discard_sectors = (1 << io->plo->cluster_log) * 2 - 1;
 		return;
 	}
 
diff --git a/include/linux/ploop/ploop.h b/include/linux/ploop/ploop.h
index 3e1b59ea7881..fe6f94ef550c 100644
--- a/include/linux/ploop/ploop.h
+++ b/include/linux/ploop/ploop.h
@@ -404,6 +404,7 @@ struct ploop_device
 
 	int			cluster_log;
 	int			fmt_version;
+	bool			force_split_discard_reqs;
 
 	int			active_reqs;
 	int			fastpath_reqs;




More information about the Devel mailing list