From mboxrd@z Thu Jan 1 00:00:00 1970 From: Andrey Zhadchenko Date: Wed, 12 Aug 2026 23:20:05 +0300 Subject: [Devel] [PATCH VZ10 v2 10/10] drivers/md/dm-qcow2: respect zeroes during merge In-Reply-To: <20260812202005.288994-1-andrey.zhadchenko@virtuozzo.com> References: <20260812202005.288994-1-andrey.zhadchenko@virtuozzo.com> Message-ID: <20260812202005.288994-11-andrey.zhadchenko@virtuozzo.com> List-Id: The merge machinery used to skip unallocated clusters, therefore breaking all clusters with zero bits and non-zero backing. Technically this bug was present even before discard changes, as qcow2 image could have 'reads as zero' bits before being inserted into dm-qcow2. Properly commit all zero clusters (albeit with data for now). https://virtuozzo.atlassian.net/browse/VSTOR-138288 Feature: dm-qcow2: block device over QCOW2 files driver Signed-off-by: Andrey Zhadchenko --- drivers/md/dm-qcow2-map.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index baa6778f1686c..87629328f8968 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -2744,6 +2744,13 @@ static void requeue_if_ok(struct qcow2_target *tgt, struct qio *unused, qcow2_dispatch_qios(qio->qcow2, qio, NULL); } +static bool clu_zeroes_present(struct qcow2 *qcow2, struct qcow2_map *map) +{ + if (qcow2->ext_l2) + return (map->ext_l2 >> 32) != 0; + return map->all_zeroes; +} + static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio, struct qcow2_map *map, bool write) { @@ -2761,7 +2768,7 @@ static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio, } } - if (!map->data_clu_alloced) { + if (!map->data_clu_alloced && !clu_zeroes_present(qcow2, map)) { /* Strange COW at L1, except the merge from RO image */ WARN_ON_ONCE(map->clu_is_cow && qio_may_modify_image(qcow2, *qio)); if (fake_merge_qio(*qio)) { @@ -2808,7 +2815,7 @@ static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio, goto endio; } - if (!map->clu_is_cow) { + if (!map->clu_is_cow && map->data_clu_alloced) { /* Forced set these to unuse them after discard */ (*qio)->ext->unuse_clu_pos = map->data_clu_pos; (*qio)->ext->unuse_clu_end = map->data_clu_pos + qcow2->clu_size; -- 2.43.5