The commit is pushed to "branch-rh10-6.12.0-55.13.1.2.x.vz10-ovz" and will 
appear at g...@bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-55.13.1.2.7.vz10
------>
commit 656b0d6a8508c397e7d47d88108b412e6d07fd88
Author: Andrey Zhadchenko <andrey.zhadche...@virtuozzo.com>
Date:   Thu Sep 18 13:45:21 2025 +0300

    dm-qcow2: allow specifying depth to merge intermediate images
    
    dm-qcow2 is almost ready to merge intermediate images, thanks
    to separated metadata tables and qio being per-qcow2.
    So this patch mostly propagates which qcow2 we want to use
    instead of tgt->top.
    
    qcow2_merge_backward_complete is a bit harder due to in-middle
    replacement and needing to recalculate img_id for a part of
    image chain.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-101375
    
    Signed-off-by: Andrey Zhadchenko <andrey.zhadche...@virtuozzo.com>
    Reviewed-by: Pavel Tikhomirov <ptikhomi...@virtuozzo.com>
    
    Feature: dm-qcow2: block device over QCOW2 files driver
---
 drivers/md/dm-qcow2-cmd.c | 59 ++++++++++++++++++++++++++++++++++-------------
 drivers/md/dm-qcow2.h     |  1 +
 2 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/drivers/md/dm-qcow2-cmd.c b/drivers/md/dm-qcow2-cmd.c
index 97e0c46cc8426..c15c46a0fe8b7 100644
--- a/drivers/md/dm-qcow2-cmd.c
+++ b/drivers/md/dm-qcow2-cmd.c
@@ -116,9 +116,9 @@ static int qcow2_service_iter(struct qcow2_target *tgt, 
struct qcow2 *qcow2,
 }
 ALLOW_ERROR_INJECTION(qcow2_service_iter, ERRNO);
 
-static int qcow2_merge_common(struct qcow2_target *tgt)
+static int qcow2_merge_common(struct qcow2_target *tgt, struct qcow2 *qcow2)
 {
-       struct qcow2 *qcow2 = tgt->top, *lower = qcow2->lower;
+       struct qcow2 *lower = qcow2->lower;
        u32 clu_size = qcow2->clu_size;
        loff_t end = lower->hdr.size;
 
@@ -139,9 +139,8 @@ static int qcow2_merge_forward(struct qcow2_target *tgt)
 }
 ALLOW_ERROR_INJECTION(qcow2_merge_forward, ERRNO);
 
-static int qcow2_break_l1cow(struct qcow2_target *tgt)
+static int qcow2_break_l1cow(struct qcow2_target *tgt, struct qcow2 *qcow2)
 {
-       struct qcow2 *qcow2 = tgt->top;
        loff_t end = qcow2->hdr.size;
        loff_t step = (u64)qcow2->l2_entries * qcow2->clu_size;
 
@@ -154,6 +153,15 @@ static void set_backward_merge_in_process(struct 
qcow2_target *tgt,
 {
        LIST_HEAD(list);
 
+       /*
+        * There are no writes if it is not the top qcow2 image.
+        * so we do not need to stop and flush requests when setting
+        */
+       if (qcow2 != tgt->top && set) {
+               qcow2->backward_merge_in_process = set;
+               return;
+       }
+
        /*
         * To avoid race between allocations and COWS
         * we completely stop queueing qios and wait
@@ -240,13 +248,20 @@ static int qcow2_merge_backward_progress(struct 
qcow2_target *tgt,
 
 static int qcow2_merge_backward_set_eventfd(struct qcow2_target *tgt, int efd);
 
-static int qcow2_merge_backward_start(struct qcow2_target *tgt, int efd)
+static int qcow2_merge_backward_start(struct qcow2_target *tgt, int efd, u32 
depth)
 {
-       struct qcow2 *qcow2 = tgt->top, *lower = qcow2->lower;
+       struct qcow2 *qcow2, *lower, **pqcow2 = &tgt->top;
        int ret;
 
        lockdep_assert_held(&tgt->ctl_mutex);
 
+       while (depth-- && *pqcow2)
+               pqcow2 = &(*pqcow2)->lower;
+
+       qcow2 = *pqcow2;
+       if (!qcow2)
+               return -ENOENT;
+       lower = qcow2->lower;
        if (!lower)
                return -ENOENT;
        if (!(lower->file->f_mode & FMODE_WRITE))
@@ -263,6 +278,7 @@ static int qcow2_merge_backward_start(struct qcow2_target 
*tgt, int efd)
        if (ret)
                return ret;
 
+       tgt->backward_merge.pqcow2 = pqcow2;
        tgt->backward_merge.state = BACKWARD_MERGE_START;
        __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_START);
        tgt->backward_merge.error = 0;
@@ -291,7 +307,7 @@ void qcow2_merge_backward_work(struct work_struct *work)
        __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_BREAK_L1COW);
        mutex_unlock(&tgt->ctl_mutex);
 
-       qcow2 = tgt->top;
+       qcow2 = *tgt->backward_merge.pqcow2;
        lower = qcow2->lower;
 
        /*
@@ -300,7 +316,7 @@ void qcow2_merge_backward_work(struct work_struct *work)
         * we'd have to freeze IO going to all data clusters
         * under every L1 entry related to several snapshots.
         */
-       ret = qcow2_break_l1cow(tgt);
+       ret = qcow2_break_l1cow(tgt, qcow2);
        if (ret) {
                QC_ERR(tgt->ti, "Can't break L1 COW");
                goto out_err;
@@ -316,7 +332,7 @@ void qcow2_merge_backward_work(struct work_struct *work)
 
        /* Start merge */
        backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_RUNNING);
-       ret = qcow2_merge_common(tgt);
+       ret = qcow2_merge_common(tgt, qcow2);
        if (ret) {
                set_backward_merge_in_process(tgt, qcow2, false);
                ret2 = qcow2_set_image_file_features(lower, false);
@@ -350,16 +366,23 @@ void qcow2_merge_backward_work(struct work_struct *work)
 
 static int qcow2_merge_backward_complete(struct qcow2_target *tgt)
 {
-       struct qcow2 *qcow2 = tgt->top, *lower = qcow2->lower;
+       struct qcow2 *qcow2 = *tgt->backward_merge.pqcow2, *i;
        int ret;
 
        lockdep_assert_held(&tgt->ctl_mutex);
 
        if (tgt->backward_merge.state != BACKWARD_MERGE_WAIT_COMPLETION)
                return -EBUSY;
-       __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_COMPLETING);
 
-       tgt->top = lower;
+       *tgt->backward_merge.pqcow2 = qcow2->lower;
+
+       i = tgt->top;
+       while (i != qcow2->lower) {
+               i->img_id--;
+               i = i->lower;
+       }
+
+       __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_COMPLETING);
        smp_wmb(); /* Pairs with qcow2_ref_inc() */
        qcow2_inflight_ref_switch(tgt); /* Pending qios */
        qcow2_flush_deferred_activity(tgt, qcow2); /* Delayed md pages */
@@ -396,7 +419,7 @@ void qcow2_merge_backward_cancel(struct qcow2_target *tgt)
        } else if (tgt->backward_merge.state == BACKWARD_MERGE_STOP) {
                flush = true;
        } else if (tgt->backward_merge.state == BACKWARD_MERGE_WAIT_COMPLETION) 
{
-               set_backward_merge_in_process(tgt, tgt->top, false);
+               set_backward_merge_in_process(tgt, *tgt->backward_merge.pqcow2, 
false);
                tgt->backward_merge.state = BACKWARD_MERGE_STOPPED;
                __backward_merge_update_stage(tgt, BACKWARD_MERGE_STAGE_NONE);
        }
@@ -571,7 +594,7 @@ int qcow2_message(struct dm_target *ti, unsigned int argc, 
char **argv,
 {
        struct qcow2_target *tgt = to_qcow2_target(ti);
        int ret = -EPERM;
-       u32 val, val2;
+       u32 val, val2, depth = 0;
        int efd;
 
        if (!capable(CAP_SYS_ADMIN))
@@ -652,11 +675,15 @@ int qcow2_message(struct dm_target *ti, unsigned int 
argc, char **argv,
        } else if (!strcmp(argv[0], "merge_backward")) {
                /* argc >= 2 */
                if (!strcmp(argv[1], "start")) {
-                       if (argc != 3 || kstrtoint(argv[2], 10, &efd) || efd < 
0) {
+                       if (argc < 3 || argc > 4 || kstrtoint(argv[2], 10, 
&efd) || efd < 0) {
+                               ret = -EINVAL;
+                               goto out_unlock;
+                       }
+                       if (argc == 4 && kstrtou32(argv[3], 10, &depth)) {
                                ret = -EINVAL;
                                goto out_unlock;
                        }
-                       ret = qcow2_merge_backward_start(tgt, efd);
+                       ret = qcow2_merge_backward_start(tgt, efd, depth);
                } else if (!strcmp(argv[1], "complete")) {
                        if (argc != 2) {
                                ret = -EINVAL;
diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h
index bdc96f921af94..86f0688e7345a 100644
--- a/drivers/md/dm-qcow2.h
+++ b/drivers/md/dm-qcow2.h
@@ -171,6 +171,7 @@ enum qcow2_backward_merge_stage {
 
 struct qcow2_backward_merge {
        struct work_struct work;
+       struct qcow2 **pqcow2;
        enum qcow2_backward_merge_state state;
        int error;
        struct eventfd_ctx *eventfd_ctx;
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to