All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
To: ptikhomirov@virtuozzo.com, vasileios.almpanis@virtuozzo.com
Cc: devel@openvz.org
Subject: [Devel] [PATCH VZ10 v4 03/11] drivers/md/dm-qcow2: generalize COW index update machinery
Date: Thu, 27 Aug 2026 19:06:11 +0300	[thread overview]
Message-ID: <20260827160619.303398-4-andrey.zhadchenko@virtuozzo.com> (raw)
In-Reply-To: <20260827160619.303398-1-andrey.zhadchenko@virtuozzo.com>

The two-stage L1/L2 entry update pipeline is not COW-specific:
backward merge already uses it to zero L2 entries, and upcoming
discard support will be the third user. Rename it to reflect
what it does.

No functional changes.

Feature: dm-qcow2: block device over QCOW2 files driver
https://virtuozzo.atlassian.net/browse/VSTOR-139406
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---
v3:
 - remove unused qvec from process_indexes_write() since we already
touch this function

 drivers/md/dm-qcow2-map.c | 104 +++++++++++++++++++-------------------
 drivers/md/dm-qcow2.h     |  10 ++--
 2 files changed, 56 insertions(+), 58 deletions(-)

diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c
index 651730bd15901..83b540ccefee6 100644
--- a/drivers/md/dm-qcow2-map.c
+++ b/drivers/md/dm-qcow2-map.c
@@ -2579,10 +2579,10 @@ static int prepare_l1l2_allocation(struct qcow2 *qcow2, struct qio *qio,
  * we have to wait all previous READs. We do that around
  * index wb. See md->wpc_noread_count update details.
  */
-static int prepare_l_entry_cow(struct qcow2 *qcow2, struct qcow2_map *map,
-			       struct qio *qio, struct md_page *md,
-			       u32 index_in_page, loff_t cow_clu_pos,
-			       loff_t cow_clu_end, u8 cow_level)
+static int prepare_l_entry_replace(struct qcow2 *qcow2, struct qcow2_map *map,
+				   struct qio *qio, struct md_page *md,
+				   u32 index_in_page, loff_t unuse_clu_pos,
+				   loff_t unuse_clu_end, u8 lx_level)
 {
 	struct lock_desc *lockd = NULL;
 	struct qio_ext *ext;
@@ -2591,9 +2591,9 @@ static int prepare_l_entry_cow(struct qcow2 *qcow2, struct qcow2_map *map,
 		return -ENOMEM;
 
 	ext = qio->ext;
-	ext->cow_clu_pos = cow_clu_pos;
-	ext->cow_clu_end = cow_clu_end;
-	ext->cow_level = cow_level;
+	ext->unuse_clu_pos = unuse_clu_pos;
+	ext->unuse_clu_end = unuse_clu_end;
+	ext->lx_level = lx_level;
 
 	spin_lock_irq(&qcow2->md_pages_lock);
 	if (!md->lockd) {
@@ -2614,23 +2614,23 @@ static int prepare_l_entry_cow(struct qcow2 *qcow2, struct qcow2_map *map,
 	return 1;
 }
 
-static int prepare_l1l2_cow(struct qcow2 *qcow2, struct qio *qio,
-			    struct qcow2_map *map)
+static int prepare_l1l2_replace(struct qcow2 *qcow2, struct qio *qio,
+				struct qcow2_map *map)
 {
 	if (WARN_ON_ONCE(!(map->level & L1_LEVEL)))
 		return -EIO; /* Sanity check: L1 must be cached */
 
 	if (!(map->level & L2_LEVEL)) {
-		return prepare_l_entry_cow(qcow2, map, qio, map->l1.md,
-					   map->l1.index_in_page,
-					   map->cow_clu_pos,
-					   map->cow_clu_end, L1_LEVEL);
+		return prepare_l_entry_replace(qcow2, map, qio, map->l1.md,
+					       map->l1.index_in_page,
+					       map->cow_clu_pos,
+					       map->cow_clu_end, L1_LEVEL);
 	}
 
-	return prepare_l_entry_cow(qcow2, map, qio, map->l2.md,
-				  map->l2.index_in_page,
-				  map->cow_clu_pos,
-				  map->cow_clu_end, L2_LEVEL);
+	return prepare_l_entry_replace(qcow2, map, qio, map->l2.md,
+				       map->l2.index_in_page,
+				       map->cow_clu_pos,
+				       map->cow_clu_end, L2_LEVEL);
 }
 
 static void backward_merge_write_complete(struct qcow2_target *tgt, struct qio *unused,
@@ -2648,7 +2648,7 @@ static void backward_merge_write_complete(struct qcow2_target *tgt, struct qio *
 	WARN_ON_ONCE(qio->flags & QIO_IS_DISCARD_FL);
 	qio->flags |= QIO_IS_DISCARD_FL;
 
-	qio->queue_list_id = QLIST_COW_INDEXES;
+	qio->queue_list_id = QLIST_INDEXES_WRITE;
 	qcow2_dispatch_qios(qcow2, qio, NULL);
 }
 
@@ -2704,7 +2704,7 @@ static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio,
 	if (!op_is_write((*qio)->bi_op)) {
 		/*
 		 * READ qio may data may be contained in several deltas.
-		 * We can't read lower delta after prepare_l1l2_cow()
+		 * We can't read lower delta after prepare_l1l2_replace()
 		 * prepares us.
 		 */
 		aux_qio = qcow2_alloc_qio(qcow2->tgt->qio_pool, true);
@@ -2725,10 +2725,10 @@ static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio,
 	}
 
 	/*
-	 * Mark as COW, as this completely defers any parallel qios.
-	 * @qio is COW status holder.
+	 * Lock the entry, as this completely defers any parallel qios.
+	 * @qio is the lock holder.
 	 */
-	ret = prepare_l1l2_cow(qcow2, *qio, map);
+	ret = prepare_l1l2_replace(qcow2, *qio, map);
 	if (ret < 0) {
 		(*qio)->bi_status = errno_to_blk_status(ret);
 		goto endio;
@@ -2736,13 +2736,13 @@ static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio,
 
 	if (!map->clu_is_cow) {
 		/* Forced set these to unuse them after discard */
-		(*qio)->ext->cow_clu_pos = map->data_clu_pos;
-		(*qio)->ext->cow_clu_end = map->data_clu_pos + qcow2->clu_size;
+		(*qio)->ext->unuse_clu_pos = map->data_clu_pos;
+		(*qio)->ext->unuse_clu_end = map->data_clu_pos + qcow2->clu_size;
 	}
 
 	return 1;
 endio:
-	qio_endio(*qio); /* Breaks COW set in prepare_l1l2_cow() */
+	qio_endio(*qio); /* Releases the lock set in prepare_l1l2_replace() */
 	return 0;
 }
 
@@ -3348,7 +3348,7 @@ static int handle_metadata(struct qcow2 *qcow2, struct qio **qio,
 		   (!qio_is_fully_alloced(qcow2, *qio, map) || map->clu_is_cow)) {
 		if (map->clu_is_cow) {
 			/* COW to compressed or shared with snapshot cluster */
-			ret = prepare_l1l2_cow(qcow2, *qio, map);
+			ret = prepare_l1l2_replace(qcow2, *qio, map);
 		} else if ((map->level & L2_LEVEL) &&
 		    qio_border_is_inside_unmapped_unit(qcow2, *qio, map) &&
 		    maybe_mapped_in_lower_delta(qcow2, *qio)) {
@@ -3358,7 +3358,7 @@ static int handle_metadata(struct qcow2 *qcow2, struct qio **qio,
 			 * snapshots). Here is data COW on L2_LEVEL.
 			 */
 			map->backing_file_cow = true;
-			ret = prepare_l1l2_cow(qcow2, *qio, map);
+			ret = prepare_l1l2_replace(qcow2, *qio, map);
 		} else if (unlikely(op_is_discard((*qio)->bi_op) &&
 				    (map->level & L2_LEVEL))) {
 			if (!map->data_clu_alloced) {
@@ -3841,7 +3841,7 @@ static void cow_data_write_endio(struct qcow2_target *tgt, struct qio *unused,
 		qio->bi_status = bi_status;
 		qio_endio(qio);
 	} else {
-		qio->queue_list_id = QLIST_COW_INDEXES;
+		qio->queue_list_id = QLIST_INDEXES_WRITE;
 		qcow2_dispatch_qios(qcow2, qio, NULL);
 	}
 }
@@ -3889,7 +3889,7 @@ static void sliced_cow_data_write_complete(struct qcow2_target *tgt, struct qio
 		qio->bi_status = bi_status;
 		qio_endio(qio);
 	} else {
-		qio->queue_list_id = QLIST_COW_INDEXES;
+		qio->queue_list_id = QLIST_INDEXES_WRITE;
 		qcow2_dispatch_qios(qcow2, qio, NULL);
 	}
 }
@@ -3923,7 +3923,7 @@ static void process_cow_data_write(struct qcow2 *qcow2, struct list_head *cow_li
 		ext = qio->ext;
 
 		if (ext->only_set_ext_l2) {
-			WARN_ON_ONCE(ext->cow_level != L2_LEVEL);
+			WARN_ON_ONCE(ext->lx_level != L2_LEVEL);
 			pos = ext->allocated_clu_pos;
 			goto submit;
 		}
@@ -3942,17 +3942,16 @@ static void process_cow_data_write(struct qcow2 *qcow2, struct list_head *cow_li
 		ext->allocated_clu_pos = pos;
 		ext->cleanup_mask |= FREE_ALLOCATED_CLU;
 submit:
-		if (ext->cow_level == L2_LEVEL)
+		if (ext->lx_level == L2_LEVEL)
 			submit_sliced_cow_data_write(qcow2, qio, pos);
 		else
 			submit_cow_data_write(qcow2, qio, pos);
 	}
 }
 
-static void process_cow_indexes_write(struct qcow2 *qcow2,
-				      struct list_head *qio_list)
+static void process_indexes_write(struct qcow2 *qcow2,
+				  struct list_head *qio_list)
 {
-	struct qcow2_bvec *qvec;
 	struct md_page *lx_md;
 	struct qio_ext *ext;
 	struct qio *qio;
@@ -3965,11 +3964,10 @@ static void process_cow_indexes_write(struct qcow2 *qcow2,
 		if (!qio)
 			break;
 		ext = qio->ext;
-		qvec = qio->data;
 		lx_md = ext->lx_md;
 
 		/* Return back to the same stage in case of writeback */
-		qio->queue_list_id = QLIST_COW_INDEXES;
+		qio->queue_list_id = QLIST_INDEXES_WRITE;
 		if (delay_if_writeback(qcow2, lx_md, -1, &qio, true))
 			continue;
 
@@ -3978,7 +3976,7 @@ static void process_cow_indexes_write(struct qcow2 *qcow2,
 
 		arg_mask = (discard ? 0 : LU_SET_ONE_MASK) | LU_WANTS_PE_PAGE;
 		if (ext->only_set_ext_l2) {
-			WARN_ON_ONCE(ext->cow_level != L2_LEVEL);
+			WARN_ON_ONCE(ext->lx_level != L2_LEVEL);
 			goto set_ext_l2;
 		}
 
@@ -3986,14 +3984,14 @@ static void process_cow_indexes_write(struct qcow2 *qcow2,
 		ret = prepare_l_entry_update(qcow2, qio, lx_md,
 					     ext->lx_index_in_page,
 					     &ext->allocated_clu_pos,
-					     arg_mask, ext->cow_level);
+					     arg_mask, ext->lx_level);
 		if (ret < 0) {
 			qio->bi_status = errno_to_blk_status(ret);
 			qio_endio(qio);
 			continue;
 		}
 set_ext_l2:
-		if (qcow2->ext_l2 && ext->cow_level == L2_LEVEL) {
+		if (qcow2->ext_l2 && ext->lx_level == L2_LEVEL) {
 			arg_mask &= ~LU_SET_ONE_MASK;
 			ret = prepare_l_entry_update(qcow2, qio, lx_md,
 					     ext->lx_index_in_page + 1,
@@ -4003,7 +4001,7 @@ static void process_cow_indexes_write(struct qcow2 *qcow2,
 		}
 
 		/* Next stage */
-		qio->queue_list_id = QLIST_COW_END;
+		qio->queue_list_id = QLIST_INDEXES_END;
 
 		spin_lock_irq(&qcow2->md_pages_lock);
 		/*
@@ -4019,8 +4017,8 @@ static void process_cow_indexes_write(struct qcow2 *qcow2,
 	}
 }
 
-/* Finalize successful COW */
-static void process_cow_end(struct qcow2 *qcow2, struct list_head *qio_list)
+/* Finalize successful L1/L2 entry replace */
+static void process_indexes_end(struct qcow2 *qcow2, struct list_head *qio_list)
 {
 	struct dm_target *ti = qcow2->tgt->ti;
 	u32 mask, clu_size = qcow2->clu_size;
@@ -4040,7 +4038,7 @@ next:		qio = qio_list_pop(qio_list);
 			ext->cleanup_mask &= ~FREE_ALLOCATED_CLU;
 
 		/* Should be already set... */
-		qio->queue_list_id = QLIST_COW_END;
+		qio->queue_list_id = QLIST_INDEXES_END;
 		/*
 		 * Wait last user before we (possible) mark clusters
 		 * unused. In real only compressed COW requires this.
@@ -4048,8 +4046,8 @@ next:		qio = qio_list_pop(qio_list);
 		if (delay_if_has_wpc_readers(qcow2, ext->lx_md, &qio))
 			goto next;
 
-		pos = ext->cow_clu_pos;
-		for (; pos < ext->cow_clu_end; pos += clu_size) {
+		pos = ext->unuse_clu_pos;
+		for (; pos < ext->unuse_clu_end; pos += clu_size) {
 			ret = __handle_r1r2_maps(qcow2, pos, &qio, &r1, &r2);
 			if (ret == 0) /* We never shrink md pages, impossible */
 				goto next;
@@ -4057,7 +4055,7 @@ next:		qio = qio_list_pop(qio_list);
 				QC_ERR(ti, "clu at %lld leaked", pos);
 			else
 				dec_cluster_usage(qcow2, r2.md, r2.index_in_page, pos);
-			ext->cow_clu_pos += clu_size;
+			ext->unuse_clu_pos += clu_size;
 		}
 
 		mask = MD_INDEX_SET_UNLOCKED|DEC_WPC_NOREAD_COUNT;
@@ -4065,7 +4063,7 @@ next:		qio = qio_list_pop(qio_list);
 			mask |= FREE_QIO_DATA_QVEC;
 		WARN_ON_ONCE(ext->cleanup_mask != mask); /* Sanity check */
 
-		if (ext->cow_level == L1_LEVEL) {
+		if (ext->lx_level == L1_LEVEL) {
 			finalize_qio_ext(qio);
 			/* COW on L1 completed, it's time for COW on L2 */
 			qio->queue_list_id = QLIST_DEFERRED;
@@ -4105,8 +4103,8 @@ void do_qcow2_work(struct work_struct *ws)
 	LIST_HEAD(zread_qios);
 	LIST_HEAD(bwrite_qios);
 	LIST_HEAD(cow_data_qios);
-	LIST_HEAD(cow_indexes_qios);
-	LIST_HEAD(cow_end_qios);
+	LIST_HEAD(indexes_write_qios);
+	LIST_HEAD(indexes_end_qios);
 	LIST_HEAD(resubmit_qios);
 	LIST_HEAD(seek_qios);
 	unsigned int pflags = current->flags;
@@ -4118,8 +4116,8 @@ void do_qcow2_work(struct work_struct *ws)
 	list_splice_init(&qcow2->qios[QLIST_ZREAD], &zread_qios);
 	list_splice_init(&qcow2->qios[QLIST_BMERGE_WRITE], &bwrite_qios);
 	list_splice_init(&qcow2->qios[QLIST_COW_DATA], &cow_data_qios);
-	list_splice_init(&qcow2->qios[QLIST_COW_INDEXES], &cow_indexes_qios);
-	list_splice_init(&qcow2->qios[QLIST_COW_END], &cow_end_qios);
+	list_splice_init(&qcow2->qios[QLIST_INDEXES_WRITE], &indexes_write_qios);
+	list_splice_init(&qcow2->qios[QLIST_INDEXES_END], &indexes_end_qios);
 	list_splice_init(&qcow2->resubmit_qios, &resubmit_qios);
 	list_splice_init(&qcow2->qios[QLIST_SEEK], &seek_qios);
 	spin_unlock_irq(&qcow2->deferred_lock);
@@ -4129,8 +4127,8 @@ void do_qcow2_work(struct work_struct *ws)
 	process_compressed_read(&zread_qios, &cow_data_qios);
 	process_backward_merge_write(qcow2, &bwrite_qios);
 	process_cow_data_write(qcow2, &cow_data_qios);
-	process_cow_indexes_write(qcow2, &cow_indexes_qios);
-	process_cow_end(qcow2, &cow_end_qios);
+	process_indexes_write(qcow2, &indexes_write_qios);
+	process_indexes_end(qcow2, &indexes_end_qios);
 	process_resubmit_qios(qcow2, &resubmit_qios);
 	process_seek_qios(qcow2, &seek_qios);
 
diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h
index aa3487007523f..230e7a4a34e76 100644
--- a/drivers/md/dm-qcow2.h
+++ b/drivers/md/dm-qcow2.h
@@ -228,8 +228,8 @@ enum {
 	QLIST_ZREAD,
 	QLIST_BMERGE_WRITE,
 	QLIST_COW_DATA,
-	QLIST_COW_INDEXES,
-	QLIST_COW_END,
+	QLIST_INDEXES_WRITE,
+	QLIST_INDEXES_END,
 	QLIST_SEEK,
 
 	QLIST_COUNT,
@@ -307,13 +307,13 @@ struct qio_ext {
 	u32 lx_index_in_page, r2_index_in_page;
 	u64 allocated_clu_pos;
 
-	loff_t cow_clu_pos;
-	loff_t cow_clu_end;
+	loff_t unuse_clu_pos;
+	loff_t unuse_clu_end;
 	u64 new_ext_l2;
 	u32 cow_mask;
 	bool only_set_ext_l2:1;
 
-	u8 cow_level;
+	u8 lx_level;
 
 #define MD_INDEX_SET_UNLOCKED	(1ULL << 0)
 #define DEC_WPC_NOREAD_COUNT	(1ULL << 1)
-- 
2.43.5

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

  parent reply	other threads:[~2026-08-27 16:10 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-27 16:06 [Devel] [PATCH VZ10 v4 00/11] dm-qcow2: improve discard and read-only merge handling Andrey Zhadchenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 01/11] drivers/md/dm-qcow2: do not attach a bvec to discard qios Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 02/11] drivers/md/dm-qcow2: fix revert_cluster_alloc() for ext_l2 case Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` Andrey Zhadchenko [this message]
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] drivers/md/dm-qcow2: generalize COW index update machinery Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 04/11] drivers/md/dm-qcow2: update metadata on whole cluster discard Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 05/11] drivers/md/dm-qcow2: never trigger COW or allocation on discard Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 06/11] drivers/md/dm-qcow2: set L2_READS_ALL_ZEROES after some discards Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 07/11] drivers/md/dm-qcow2: update metadata on subclusters discard Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 08/11] drivers/md/dm-qcow2: unmap cluster when discard clears last allocated subclusters Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 09/11] drivers/md/dm-qcow2: allow shared L1 entries during merge from RO image Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 10/11] drivers/md/dm-qcow2: do discards during backward merge only for writable image Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-27 16:06 ` [Devel] [PATCH VZ10 v4 11/11] drivers/md/dm-qcow2: respect zeroes during merge Andrey Zhadchenko
2026-08-28 17:03   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260827160619.303398-4-andrey.zhadchenko@virtuozzo.com \
    --to=andrey.zhadchenko@virtuozzo.com \
    --cc=devel@openvz.org \
    --cc=ptikhomirov@virtuozzo.com \
    --cc=vasileios.almpanis@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.