[Devel] [PATCH RH9 12/12] dm_qcow2: get next hole

Kirill Tkhai ktkhai at virtuozzo.com
Wed Jan 19 19:20:12 MSK 2022


---
 drivers/md/dm-qcow2-cmd.c |   46 ++++++++++++++++++++++++++++-------
 drivers/md/dm-qcow2-map.c |   60 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/md/dm-qcow2.h     |    2 ++
 3 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/drivers/md/dm-qcow2-cmd.c b/drivers/md/dm-qcow2-cmd.c
index e3c2de89d1bf..19b1f6c33265 100644
--- a/drivers/md/dm-qcow2-cmd.c
+++ b/drivers/md/dm-qcow2-cmd.c
@@ -317,24 +317,52 @@ static int qcow2_set_fault_injection(struct qcow2_target *tgt,
 	return 0;
 }
 
-static int qcow2_seek_hole(struct qcow2_target *tgt, u64 pos)
+static int qcow2_seek_hole(struct qcow2_target *tgt, u64 old_pos,
+			   char *result, unsigned int maxlen)
 {
+	loff_t pos, hole_pos, search_pos, end_pos;
 	struct qcow2 *qcow2, *img;
+	unsigned int sz = 0;
 	u8 ref_index;
+	u32 clu_size;
 	u64 step;
 	int ret;
 
 	qcow2 = img = qcow2_ref_inc(tgt, &ref_index);
+	if (old_pos >= qcow2->hdr.size) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/* This expects lower deltas have the same clu_size and ext_l2 */
-	step = (u64)qcow2->l2_entries * qcow2->clu_size;
+	clu_size = qcow2->clu_size;
+	search_pos = round_down(old_pos, clu_size);
+	step = (u64)qcow2->l2_entries * clu_size;
+
+	hole_pos = OFFSET_MAX;
+	while (search_pos < qcow2->hdr.size) {
+		end_pos = round_up(search_pos + 1, step);
+		img = qcow2;
+		while (img && search_pos < img->hdr.size) {
+			ret = pos = qcow2_find_next_hole(tgt, img, search_pos, end_pos);
+			if (pos < 0)
+				goto out;
+			if (pos < hole_pos)
+				hole_pos = pos;
+			img = img->lower;
+		}
 
-	while (img) { /* Cache metadata of all images */
-		ret = qcow2_service_iter(tgt, img, img->hdr.size, step,
-					 REQ_OP_READ, QIO_IS_FAKEREAD_FL);
-		if (ret)
-			goto out;
-		img = img->lower;
+		if (hole_pos != OFFSET_MAX)
+			break;
+		search_pos = end_pos;
 	}
+
+	if (hole_pos < old_pos) /* initial @search_pos is rounded down */
+		hole_pos = old_pos;
+	if (hole_pos == OFFSET_MAX)
+		hole_pos = qcow2->hdr.size;
+	ret = DMEMIT("hole_pos=%lld\n", hole_pos);
+	ret = ret ? 1 : 0;
 out:
 	qcow2_ref_dec(tgt, ref_index);
 	return ret;
@@ -382,7 +410,7 @@ int qcow2_message(struct dm_target *ti, unsigned int argc, char **argv,
 			ret = -EINVAL;
 			goto out;
 		}
-		ret = qcow2_seek_hole(tgt, val64);
+		ret = qcow2_seek_hole(tgt, val64, result, maxlen);
 		goto out;
 	}
 
diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c
index 4531d680fa63..bb7ecc3423f5 100644
--- a/drivers/md/dm-qcow2-map.c
+++ b/drivers/md/dm-qcow2-map.c
@@ -1996,6 +1996,66 @@ static int parse_metadata(struct qcow2 *qcow2, struct qio **qio,
 	return ret < 0 ? ret : 0;
 }
 
+static void wake_wq_qio_endio(struct qcow2_target *tgt, struct qio *qio,
+			      void *completed_ptr, blk_status_t bi_status)
+{
+	bool *completed = completed_ptr;
+
+	*completed = true;
+	wake_up(&tgt->service_wq);
+}
+
+/* Find a hole in diapason referred by L2 containing @pos (starting from @pos) */
+loff_t qcow2_find_next_hole(struct qcow2_target *tgt, struct qcow2 *qcow2,
+			    u64 pos, u64 end_pos)
+{
+	u32 clu_size = qcow2->clu_size;
+	bool completed, found = false;
+	struct qio *qio, *qio_orig;
+	int ret;
+
+	qio = qio_orig = alloc_qio(tgt->qio_pool, true);
+	if (!qio)
+		return -ENOMEM;
+
+	while (pos < end_pos) {
+		struct qcow2_map map = { .qcow2 = qcow2, };
+
+		qio = qio_orig;
+		init_qio(qio, REQ_OP_READ, qcow2);
+		qio->flags = QIO_IS_FAKEREAD_FL;
+		qio->bi_iter.bi_sector = to_sector(pos);
+		qio->endio_cb = wake_wq_qio_endio;
+		qio->endio_cb_data = &completed;
+		completed = false;
+
+		ret = parse_metadata(qcow2, &qio, &map);
+		if (ret < 0)
+			goto out;
+		if (!qio) {
+			wait_event(tgt->service_wq, completed == true);
+			if (qio_orig->bi_status) {
+				ret = blk_status_to_errno(qio_orig->bi_status);
+				goto out;
+			}
+			continue;
+		}
+		if (!map.data_clu_alloced) {
+			found = true;
+			break; /* Found */
+		}
+		pos += clu_size;
+	}
+out:
+	free_qio(qio_orig, tgt->qio_pool);
+	if (ret < 0)
+		return ret;
+	if (!found)
+		return OFFSET_MAX;
+	return pos;
+}
+
+
 /*
  * This occupies cluster at @r2_pos for R2 cluster,
  * and connects it to R1 table entry.
diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h
index 5c59cc4a7c25..34e0c271c9e8 100644
--- a/drivers/md/dm-qcow2.h
+++ b/drivers/md/dm-qcow2.h
@@ -304,6 +304,8 @@ void qcow2_ref_dec(struct qcow2_target *tgt, u8 ref_index);
 int qcow2_inflight_ref_switch(struct qcow2_target *tgt);
 void flush_deferred_activity(struct qcow2_target *tgt, struct qcow2 *qcow2);
 int qcow2_truncate_safe(struct file *file, loff_t new_len);
+loff_t qcow2_find_next_hole(struct qcow2_target *tgt,
+			    struct qcow2 *qcow2, u64 pos, u64 end_pos);
 
 static inline struct qcow2_target *to_qcow2_target(struct dm_target *ti)
 {




More information about the Devel mailing list