<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);" class="elementToProof">
Looks good to me.</div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);" class="elementToProof">
Reviewed-by: Vasileios Almpanis <a style="margin: 0px; text-align: left;" data-linkindex="0" title="mailto:andrey.zhadchenko@virtuozzo.com" class="OWAAutoLink elementToProof" id="LPlnkOWA1e8c1128-feb3-880e-eddd-c9ff60694920" href="mailto:andrey.zhadchenko@virtuozzo.com">
<vasileios.almpanis@virtuozzo.com></a></div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> devel-bounces@openvz.org <devel-bounces@openvz.org> on behalf of Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com><br>
<b>Sent:</b> Monday, September 8, 2025 7:27 PM<br>
<b>To:</b> Konstantin Khorenko <khorenko@virtuozzo.com>; Pavel Tikhomirov <ptikhomirov@virtuozzo.com><br>
<b>Cc:</b> devel@openvz.org <devel@openvz.org><br>
<b>Subject:</b> [Devel] [PATCH VZ9] dm-qcow2: allow specifying depth to merge intermediate images</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">dm-qcow2 is almost ready to merge intermediate images, thanks<br>
to separated metadata tables and qio being per-qcow2.<br>
So this patch mostly propagates which qcow2 we want to use<br>
instead of tgt->top.<br>
<br>
qcow2_merge_backward_complete is a bit harder due to in-middle<br>
replacement and needing to recalculate img_id for a part of<br>
image chain.<br>
<br>
<a href="https://virtuozzo.atlassian.net/browse/VSTOR-101375">https://virtuozzo.atlassian.net/browse/VSTOR-101375</a><br>
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com><br>
---<br>
drivers/md/dm-qcow2-cmd.c | 82 ++++++++++++++++++++++++++++-----------<br>
drivers/md/dm-qcow2.h | 1 +<br>
2 files changed, 61 insertions(+), 22 deletions(-)<br>
<br>
diff --git a/drivers/md/dm-qcow2-cmd.c b/drivers/md/dm-qcow2-cmd.c<br>
index e77052ad61e6b..d59fa82dbffbc 100644<br>
--- a/drivers/md/dm-qcow2-cmd.c<br>
+++ b/drivers/md/dm-qcow2-cmd.c<br>
@@ -116,9 +116,9 @@ static int qcow2_service_iter(struct qcow2_target *tgt, struct qcow2 *qcow2,<br>
}<br>
ALLOW_ERROR_INJECTION(qcow2_service_iter, ERRNO);<br>
<br>
-static int qcow2_merge_common(struct qcow2_target *tgt)<br>
+static int qcow2_merge_common(struct qcow2_target *tgt, struct qcow2 *qcow2)<br>
{<br>
- struct qcow2 *qcow2 = tgt->top, *lower = qcow2->lower;<br>
+ struct qcow2 *lower = qcow2->lower;<br>
u32 clu_size = qcow2->clu_size;<br>
loff_t end = lower->hdr.size;<br>
<br>
@@ -139,9 +139,8 @@ static int qcow2_merge_forward(struct qcow2_target *tgt)<br>
}<br>
ALLOW_ERROR_INJECTION(qcow2_merge_forward, ERRNO);<br>
<br>
-static int qcow2_break_l1cow(struct qcow2_target *tgt)<br>
+static int qcow2_break_l1cow(struct qcow2_target *tgt, struct qcow2 *qcow2)<br>
{<br>
- struct qcow2 *qcow2 = tgt->top;<br>
loff_t end = qcow2->hdr.size;<br>
loff_t step = (u64)qcow2->l2_entries * qcow2->clu_size;<br>
<br>
@@ -152,25 +151,35 @@ static int qcow2_break_l1cow(struct qcow2_target *tgt)<br>
static void set_backward_merge_in_process(struct qcow2_target *tgt,<br>
struct qcow2 *qcow2, bool set)<br>
{<br>
+ struct qcow2 *top = tgt->top;<br>
LIST_HEAD(list);<br>
<br>
+ /*<br>
+ * There are no writes if it is not the top qcow2 image.<br>
+ * so we do not need to stop and flush requests when setting<br>
+ */<br>
+ if (qcow2 != top && set) {<br>
+ qcow2->backward_merge_in_process = set;<br>
+ return;<br>
+ }<br>
+<br>
/*<br>
* To avoid race between allocations and COWS<br>
* we completely stop queueing qios and wait<br>
* for pending qios. Lock is for visability.<br>
*/<br>
- spin_lock_irq(&qcow2->deferred_lock);<br>
- qcow2->pause_submitting_qios = true;<br>
- spin_unlock_irq(&qcow2->deferred_lock);<br>
+ spin_lock_irq(&top->deferred_lock);<br>
+ top->pause_submitting_qios = true;<br>
+ spin_unlock_irq(&top->deferred_lock);<br>
qcow2_inflight_ref_switch(tgt);<br>
<br>
/* queue is stopped */<br>
- spin_lock_irq(&qcow2->deferred_lock);<br>
+ spin_lock_irq(&top->deferred_lock);<br>
WARN_ON_ONCE(qcow2->backward_merge_in_process == set);<br>
qcow2->backward_merge_in_process = set;<br>
- qcow2->pause_submitting_qios = false;<br>
- list_splice_init(&qcow2->paused_qios, &list);<br>
- spin_unlock_irq(&qcow2->deferred_lock);<br>
+ top->pause_submitting_qios = false;<br>
+ list_splice_init(&top->paused_qios, &list);<br>
+ spin_unlock_irq(&top->deferred_lock);<br>
<br>
qcow2_submit_embedded_qios(tgt, &list);<br>
}<br>
@@ -240,11 +249,15 @@ static int qcow2_merge_backward_progress(struct qcow2_target *tgt,<br>
<br>
static int qcow2_merge_backward_set_eventfd(struct qcow2_target *tgt, int efd);<br>
<br>
-static int qcow2_merge_backward_start(struct qcow2_target *tgt, int efd)<br>
+static int qcow2_merge_backward_start(struct qcow2_target *tgt, int efd, int depth)<br>
{<br>
- struct qcow2 *qcow2 = tgt->top, *lower = qcow2->lower;<br>
+ struct qcow2 *qcow2 = tgt->top, *lower;<br>
int ret;<br>
<br>
+ while (depth-- > 0 && qcow2->lower)<br>
+ qcow2 = qcow2->lower;<br>
+ lower = qcow2->lower;<br>
+<br>
lockdep_assert_held(&tgt->ctl_mutex);<br>
<br>
if (!lower)<br>
@@ -263,6 +276,7 @@ static int qcow2_merge_backward_start(struct qcow2_target *tgt, int efd)<br>
if (ret)<br>
return ret;<br>
<br>
+ tgt->backward_merge.qcow2 = qcow2;<br>
tgt->backward_merge.state = BACKWARD_MERGE_START;<br>
__backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_START);<br>
tgt->backward_merge.error = 0;<br>
@@ -291,7 +305,7 @@ void qcow2_merge_backward_work(struct work_struct *work)<br>
__backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_BREAK_L1COW);<br>
mutex_unlock(&tgt->ctl_mutex);<br>
<br>
- qcow2 = tgt->top;<br>
+ qcow2 = tgt->backward_merge.qcow2;<br>
lower = qcow2->lower;<br>
<br>
/*<br>
@@ -300,7 +314,7 @@ void qcow2_merge_backward_work(struct work_struct *work)<br>
* we'd have to freeze IO going to all data clusters<br>
* under every L1 entry related to several snapshots.<br>
*/<br>
- ret = qcow2_break_l1cow(tgt);<br>
+ ret = qcow2_break_l1cow(tgt, qcow2);<br>
if (ret) {<br>
QC_ERR(tgt->ti, "Can't break L1 COW");<br>
goto out_err;<br>
@@ -316,7 +330,7 @@ void qcow2_merge_backward_work(struct work_struct *work)<br>
<br>
/* Start merge */<br>
backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_RUNNING);<br>
- ret = qcow2_merge_common(tgt);<br>
+ ret = qcow2_merge_common(tgt, qcow2);<br>
if (ret) {<br>
set_backward_merge_in_process(tgt, qcow2, false);<br>
ret2 = qcow2_set_image_file_features(lower, false);<br>
@@ -357,9 +371,29 @@ static int qcow2_merge_backward_complete(struct qcow2_target *tgt)<br>
<br>
if (tgt->backward_merge.state != BACKWARD_MERGE_WAIT_COMPLETION)<br>
return -EBUSY;<br>
+<br>
+ if (tgt->top == tgt->backward_merge.qcow2) {<br>
+ tgt->top = lower;<br>
+ } else {<br>
+ struct qcow2 *top = tgt->top;<br>
+<br>
+ while (top && top->lower != tgt->backward_merge.qcow2)<br>
+ top = top->lower;<br>
+ if (!top)<br>
+ return -EFAULT;<br>
+<br>
+ qcow2 = tgt->backward_merge.qcow2;<br>
+ lower = qcow2->lower;<br>
+ top->lower = lower;<br>
+<br>
+ top = tgt->top;<br>
+ while (top != lower) {<br>
+ top->img_id--;<br>
+ top = top->lower;<br>
+ }<br>
+ }<br>
+<br>
__backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_COMPLETING);<br>
-<br>
- tgt->top = lower;<br>
smp_wmb(); /* Pairs with qcow2_ref_inc() */<br>
qcow2_inflight_ref_switch(tgt); /* Pending qios */<br>
qcow2_flush_deferred_activity(tgt, qcow2); /* Delayed md pages */<br>
@@ -396,7 +430,7 @@ void qcow2_merge_backward_cancel(struct qcow2_target *tgt)<br>
} else if (tgt->backward_merge.state == BACKWARD_MERGE_STOP) {<br>
flush = true;<br>
} else if (tgt->backward_merge.state == BACKWARD_MERGE_WAIT_COMPLETION) {<br>
- set_backward_merge_in_process(tgt, tgt->top, false);<br>
+ set_backward_merge_in_process(tgt, tgt->backward_merge.qcow2, false);<br>
tgt->backward_merge.state = BACKWARD_MERGE_STOPPED;<br>
__backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_NONE);<br>
}<br>
@@ -572,7 +606,7 @@ int qcow2_message(struct dm_target *ti, unsigned int argc, char **argv,<br>
struct qcow2_target *tgt = to_qcow2_target(ti);<br>
int ret = -EPERM;<br>
u32 val, val2;<br>
- int efd;<br>
+ int efd, depth = 0;<br>
<br>
if (!capable(CAP_SYS_ADMIN))<br>
goto out;<br>
@@ -652,11 +686,15 @@ int qcow2_message(struct dm_target *ti, unsigned int argc, char **argv,<br>
} else if (!strcmp(argv[0], "merge_backward")) {<br>
/* argc >= 2 */<br>
if (!strcmp(argv[1], "start")) {<br>
- if (argc != 3 || kstrtoint(argv[2], 10, &efd) || efd < 0) {<br>
+ if (argc < 3 || argc > 4 || kstrtoint(argv[2], 10, &efd) || efd < 0) {<br>
ret = -EINVAL;<br>
goto out_unlock;<br>
}<br>
- ret = qcow2_merge_backward_start(tgt, efd);<br>
+ if (argc == 4 && kstrtou32(argv[3], 10, &depth)) {<br>
+ ret = -EINVAL;<br>
+ goto out_unlock;<br>
+ }<br>
+ ret = qcow2_merge_backward_start(tgt, efd, depth);<br>
} else if (!strcmp(argv[1], "complete")) {<br>
if (argc != 2) {<br>
ret = -EINVAL;<br>
diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h<br>
index 5aa00c6a5ebd5..c2b2193561461 100644<br>
--- a/drivers/md/dm-qcow2.h<br>
+++ b/drivers/md/dm-qcow2.h<br>
@@ -172,6 +172,7 @@ enum qcow2_backward_merge_stage {<br>
<br>
struct qcow2_backward_merge {<br>
struct work_struct work;<br>
+ struct qcow2 *qcow2;<br>
enum qcow2_backward_merge_state state;<br>
int error;<br>
struct eventfd_ctx *eventfd_ctx;<br>
-- <br>
2.43.5<br>
<br>
_______________________________________________<br>
Devel mailing list<br>
Devel@openvz.org<br>
<a href="https://lists.openvz.org/mailman/listinfo/devel">https://lists.openvz.org/mailman/listinfo/devel</a><br>
</div>
</span></font></div>
</body>
</html>