From mboxrd@z Thu Jan 1 00:00:00 1970 From: Andrey Zhadchenko Date: Tue, 25 Aug 2026 15:24:29 +0300 Subject: [Devel] [PATCH VZ10 v3 10/11] drivers/md/dm-qcow2: allow shared L1 entries during merge from RO image In-Reply-To: <20260825122430.252094-1-andrey.zhadchenko@virtuozzo.com> References: <20260825122430.252094-1-andrey.zhadchenko@virtuozzo.com> Message-ID: <20260825122430.252094-11-andrey.zhadchenko@virtuozzo.com> List-Id: For RO images with internal snapshots L1 entries are shared. Write-mode metadata parsing stops at such entries to avoid modifying a shared L2 table, which made prepare_backward_merge() see the cluster as unallocated and skip it with "nothing to merge": the merged result would silently lose all data under shared L1 entries. Allow such scenario in parse_l1() and ease WARN in prepare_backward_merge(). 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 | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index c790b2ad3787e..41a504c65b722 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -1726,6 +1726,14 @@ static bool qio_is_fully_alloced(struct qcow2 *qcow2, struct qio *qio, return !(subclus_mask & ~alloced_mask); } +static bool qio_may_modify_image(struct qcow2 *qcow2, struct qio *qio) +{ + if (qcow2_file_is_writable(qcow2)) + return true; + + return !fake_merge_qio(qio); +} + static loff_t parse_l1(struct qcow2 *qcow2, struct qcow2_map *map, struct qio **qio, bool write) @@ -1762,7 +1770,8 @@ static loff_t parse_l1(struct qcow2 *qcow2, struct qcow2_map *map, goto out; if (delay_if_dirty(qcow2, l1->md, l1->index_in_page, qio)) goto out; - if (write && map->clu_is_cow) + /* Don't refuse L1 parse for merge qios with readonly disks */ + if (write && map->clu_is_cow && qio_may_modify_image(qcow2, *qio)) goto out; /* Avoid to return pos */ ret = pos; @@ -2061,6 +2070,10 @@ static int parse_metadata(struct qcow2 *qcow2, struct qio **qio, qio_discard_unmaps_cluster(qcow2, *qio, map))) return 0; + /* Don't need refcount table if we don't modify the image */ + if (!qio_may_modify_image(qcow2, *qio)) + return 0; + /* Now refcounters table/block */ ret = qcow2_handle_r1r2_maps(qcow2, pos, qio, &map->r1, &map->r2, map->compressed); @@ -2749,7 +2762,8 @@ static int prepare_backward_merge(struct qcow2 *qcow2, struct qio **qio, } if (!map->data_clu_alloced) { - WARN_ON_ONCE(map->clu_is_cow); /* Strange COW at L1 */ + /* 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)) { /* Nothing is to merge */ goto endio; -- 2.43.5