From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (unknown [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id BF8AD80024 for ; Fri, 28 Aug 2026 17:05:26 +0000 (UTC) Received: from mail.openvz.org (localhost [127.0.0.1]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 67SH4HSu011604; Fri, 28 Aug 2026 20:04:17 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 67SH4HSu011604 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="mbrPyVpv" Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 67SH3t2Z011536 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Fri, 28 Aug 2026 20:03:56 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 67SH3t2Z011536 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-482e12bb58aso699099f8f.0 for ; Fri, 28 Aug 2026 10:03:55 -0700 (PDT) X-Gm-Message-State: AFuF++nbG38MayaEcz+OHvdwEbDTTwd5uQJe5gDMwesLehFIbk/x7cRg wmeNSOoXr8Nbt2h5bh01LnI3D8PgiDmdSTIVdGI/K+ikQq49y0IYk6nWgzSOe0B0QAXMQczmnee x/HawZ2TcROgHmZFen6LndFOgG2zVIm2RahBx6ntGMtmtVcbLrp03Sg== X-Gm-Gg: AR+sD112ugHNKahdAUieQj7HbOmzfOL3asJmG3ICCNjcA0ygKfBVGTzhfINZiuog9vq r/IqdOPL1nCowX3aM9pppiQ1ZAcciAL1ss+zko4bpVMUqJ6U8Pg57s1p0gCXCo74hchY/ZCbc4e Yw2n6fCHbcN1l7D70zo4X1u1bRdiUh8kPQtFofi7NjUER844TTP+390lBecw1648B5hkhznIieB KdTcifJmKuyBvZy9Prn/ohrxivER1OIV8VK/lYJ7JqeezO1KxEhtgqnVeUCRSxeFSIgCDNF+2Q1 RvcS1HicQI4EC3Lp31fIROMgFCMfpcJNhbe0nZF9DdGgFf/4Rr9THOA8BhL8EMBh2Amrjr8jiJk KRDkQMMMy767I+5cYsA== X-Received: by 2002:a05:6000:4619:b0:481:51b5:7503 with SMTP id ffacd0b85a97d-482f7995f31mr14890625f8f.7.1787936635597; Fri, 28 Aug 2026 10:03:55 -0700 (PDT) X-Received: by 2002:a05:6000:4619:b0:481:51b5:7503 with SMTP id ffacd0b85a97d-482f7995f31mr14890486f8f.7.1787936635061; Fri, 28 Aug 2026 10:03:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1787936635; cv=none; d=google.com; s=arc-20260327; b=KV0OYTBW18SFfC7OyQm6qpF5G2L+N4HQfgVA45RPxfXPwPD8dS3ygisIBVuA3p2U07 MiC4BhEjm4dnxcxbpuMxOqy294NsYbJjUASJ+DS5R6Xn1t0B245AQ8tg0r9HbYqZzBAv d6WTq2/CGJnC18Mi2222GDxZHuTziylu2wRpYhSR3oZTuZBakVL973NidW0W6PRXIB5A PsP9Z0aJLHpUOdmC0Xx51Q/xePD8JhTnwVaaxsP6EGIkFjyrEON1VMNa+6tYTLmilefb Ogom3RpB+HT7AWebqEKdu3RR1+scxCmyfRfxQye3aiu3n8AAa64I2Wee7EOBSJFzKLSg WTDQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=subject:in-reply-to:cc:to:from:message-id:date:dkim-signature; bh=2NtlWhLzkiahhwrcrLaexWiIQFUaA7WSeWl4yZAQ/2c=; fh=Vum9jOrhj7HN5ByxRUgrKjNRrylYGqSqAPC0VlAu4Lc=; b=K/lW4HF1CzyzS/1C9Tyosoy3slbXiRMTSRRGSwafu7Tk+vXDFmqKUu/MCfSZNqCzVr vl0vVJqBxNpjZicH0KscmlLP7q38IDEoc1AocymxaWDfFeVFgvPNS+sOa0Ou8/kjA6BX xGynOgnxQXWOy8XfQVVUDVvaWBk8oUgJt2JZ9o0OGHC4yqkz5b1kNvnKwoWcXQrHxsCe 2dv6n02mHU15k5PZgpEiAVvNcNVrk0EcggWQWSjuYLFuQ/SdC25EWnETwhdXC9nnCwEg TNggQYS6viPqXQM/RrIbGdb8Kx5EMoZWrAJ2xCx/r31ohtZ/qEyBKK9coAf7qeaVxKB6 cQPw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=mbrPyVpv; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com Received: from relay.virtuozzo.com (relay.virtuozzo.com. [130.117.225.111]) by mx.google.com with ESMTPS id ffacd0b85a97d-482fbab34b4si3928528f8f.35.2026.08.28.10.03.54 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2026 10:03:55 -0700 (PDT) Received-SPF: pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) client-ip=130.117.225.111; Authentication-Results: mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=mbrPyVpv; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=Subject:From:Message-Id:Date:Content-Type: MIME-Version; bh=2NtlWhLzkiahhwrcrLaexWiIQFUaA7WSeWl4yZAQ/2c=; b=mbrPyVpvs3Db zu9PEb0ESNYn1V5GRqTJOSTdU9ApTibGiWRY3kkOFP0r01Lp/9bpblin/r/IolEKiFMS8nzqc8su2 XlY+jRpOdN5CzRafeQOxuCSWjtME4Rf0iYyDinvGAIHjghFFnMdT/X965CMdFAleP9Iu+AOE8prvN 6lya4rOBRuKczSN6HndRSU5XiEb28MLmArhqvT/zlkurC9auH8vCcL7SgPhHAxc801wiuJm3UojgH /0R7QsjXJ6fbNf5W8JSJfaO6O+hezZEM8ZvkRuBonCxc/+fS+OJVSc41DbloWHCyu+0pl6zm2EDCP JRoaWCnA+dMOXLDx2YgJcQ==; Received: from ch-demo-asa.virtuozzo.com ([130.117.225.8] helo=f0.sw.ru) by relay.virtuozzo.com with esmtps (TLS1.3) tls TLS_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wzzxM-008duH-37; Fri, 28 Aug 2026 19:03:50 +0200 Received: from f0.sw.ru (localhost [127.0.0.1]) by f0.sw.ru (8.18.1/8.18.1/Debian-2) with ESMTP id 67SH3sqM1064123; Fri, 28 Aug 2026 19:03:54 +0200 Received: (from kostja@localhost) by f0.sw.ru (8.18.1/8.18.1/Submit) id 67SH3s9M1064122; Fri, 28 Aug 2026 19:03:54 +0200 Date: Fri, 28 Aug 2026 19:03:54 +0200 Message-Id: <202608281703.67SH3s9M1064122@f0.sw.ru> X-Authentication-Warning: f0.sw.ru: kostja set sender to khorenko@virtuozzo.com using -f From: Konstantin Khorenko To: Andrey Zhadchenko In-Reply-to: <20260827160619.303398-5-andrey.zhadchenko@virtuozzo.com> X-OZ-Fwd: true Cc: OpenVZ devel Subject: Re: [Devel] [PATCH RHEL10 COMMIT] drivers/md/dm-qcow2: update metadata on whole cluster discard X-BeenThere: devel@openvz.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: OpenVZ development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: devel-bounces@openvz.org Errors-To: devel-bounces@openvz.org The commit is pushed to "branch-rh10-6.12.0-211.39.1.16.x.vz10-ovz" and will appear at git@bitbucket.org:openvz/vzkernel.git after rh10-6.12.0-211.39.1.16.11.vz10 ------> commit 26927a70e1ac3b4f52d68fda7369c6224237462f Author: Andrey Zhadchenko Date: Thu Aug 27 19:06:12 2026 +0300 drivers/md/dm-qcow2: update metadata on whole cluster discard Discard of a mapped cluster used to only punch a hole in the image file: the L2 entry and refcounts were left untouched, so the cluster remained allocated in qcow2 metadata forever. Handle discards covering a whole cluster via the L1/L2 entry replace machinery: prepare_cluster_discard() locks the L2 entry, then issue_discard() punches the data cluster out of the image file and queues the qio to write the zeroed L2 entry (and extended L2 bitmap) in process_indexes_write(). Refcounts of the discarded cluster are decremented in process_indexes_end() after the L2 writeback, like COW does with its source clusters. Clusters shared with internal snapshots or holding compressed data are not punched: only their usage count is decremented. Also teach revert_l_entries_update() that a changed index may contain a discard-cleared entry, which has no allocation to revert. Partial cluster discards keep the previous behavior: punch a hole without touching metadata. Feature: dm-qcow2: block device over QCOW2 files driver https://virtuozzo.atlassian.net/browse/VSTOR-139406 Signed-off-by: Andrey Zhadchenko Reviewed-by: Pavel Tikhomirov Reviewed-by: Vasileios Almpanis Reviewed-by: Konstantin Khorenko --- drivers/md/dm-qcow2-map.c | 110 +++++++++++++++++++++++++++++++++++++++++++--- drivers/md/dm-qcow2.h | 2 +- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c index 00a49a89f0cb3..8cdacaa57d3d5 100644 --- a/drivers/md/dm-qcow2-map.c +++ b/drivers/md/dm-qcow2-map.c @@ -101,6 +101,12 @@ static loff_t bio_sector_to_file_pos(struct qcow2 *qcow2, struct qio *qio, return map->data_clu_pos + bytes_off_in_cluster(qcow2, qio); } +static bool qio_covers_full_clu(struct qcow2 *qcow2, struct qio *qio) +{ + return bytes_off_in_cluster(qcow2, qio) == 0 && + qio->bi_iter.bi_size == qcow2->clu_size; +} + static loff_t compressed_clu_end_pos(loff_t start, sector_t compressed_sectors) { if (start % SECTOR_SIZE == 0) @@ -1324,6 +1330,8 @@ static void revert_l_entries_update(struct qcow2 *qcow2, struct wb_desc *wbd) set_u64_to_be_page(wbd->md->page, i, 0); if (skip_odd && (i & 1)) continue; /* pos contains ext_l2 part of L2 entry */ + if (!pos) + continue; /* no cluster was allocated */ spin_unlock(&qcow2->md_pages_lock); pos &= ~LX_REFCOUNT_EXACTLY_ONE; @@ -2015,7 +2023,12 @@ static int parse_metadata(struct qcow2 *qcow2, struct qio **qio, return ret; map->data_clu_pos = pos; - if (!write || !map->clu_is_cow) + if (!write) + return 0; + + /* discards also need to update r1r2 */ + if (!map->clu_is_cow && + !(op_is_discard((*qio)->bi_op) && qio_covers_full_clu(qcow2, *qio))) return 0; /* Now refcounters table/block */ @@ -3317,12 +3330,86 @@ static void issue_discard(struct qcow2_map *map, struct qio *qio) int ret; WARN_ON_ONCE(!(map->level & L2_LEVEL)); - pos = bio_sector_to_file_pos(qcow2, qio, map); - ret = qcow2_punch_hole(qcow2->file, pos, qio->bi_iter.bi_size); - if (ret) - qio->bi_status = errno_to_blk_status(ret); - qio_endio(qio); + if (!map->clu_is_cow) { + pos = bio_sector_to_file_pos(qcow2, qio, map); + ret = qcow2_punch_hole(qcow2->file, pos, qio->bi_iter.bi_size); + + if (ret) { + qio->bi_status = errno_to_blk_status(ret); + qio_endio(qio); + return; + } + } + + /* Clear metadata if needed */ + if (qio->flags & QIO_IS_DISCARD_FL) { + qio->queue_list_id = QLIST_INDEXES_WRITE; + qcow2_dispatch_qios(qcow2, qio, NULL); + } else { + qio_endio(qio); + } +} + +static bool qio_discard_updates_metadata(struct qcow2 *qcow2, struct qio *qio, + struct qcow2_map *map) +{ + if (!(map->level & L2_LEVEL) || !map->data_clu_alloced) + return false; + if (qio_covers_full_clu(qcow2, qio)) + return true; + return false; +} + +/* + * Discard changes metadata: prepare L2 entry update. It gets locked + * here, new value is written in process_indexes_write(), and + * refcounts are handled after the L2 writeback in process_indexes_end(). + * + * Discard covering the whole cluster replaces the entry and unuses + * the discarded (or COW source) cluster. + */ +static int prepare_cluster_discard(struct qcow2 *qcow2, struct qio **qio, + struct qcow2_map *map) +{ + u32 index_in_page = map->l2.index_in_page; + struct md_page *md = map->l2.md; + loff_t unuse_pos, unuse_end; + struct qio_ext *ext; + int ret; + + WARN_ON_ONCE(!(map->level & L2_LEVEL) || !map->data_clu_alloced); + + spin_lock_irq(&qcow2->md_pages_lock); + if (delay_if_dirty(qcow2, md, index_in_page, qio) || + __delay_if_writeback(qcow2, md, index_in_page, qio, true) || + (qcow2->ext_l2 && + delay_if_dirty(qcow2, md, index_in_page + 1, qio))) { + spin_unlock_irq(&qcow2->md_pages_lock); + return 0; + } + spin_unlock_irq(&qcow2->md_pages_lock); + + if (map->clu_is_cow) { + /* Cluster is shared or compressed. Decrement refcount. */ + unuse_pos = map->cow_clu_pos; + unuse_end = map->cow_clu_end; + } else { + /* Nobody else refers the cluster: unuse it after discard */ + unuse_pos = map->data_clu_pos; + unuse_end = map->data_clu_pos + qcow2->clu_size; + } + + ret = prepare_l_entry_replace(qcow2, map, *qio, md, index_in_page, + unuse_pos, unuse_end, L2_LEVEL); + if (ret <= 0) + return ret; + + ext = (*qio)->ext; + ext->lx_md = md; + + (*qio)->flags |= QIO_IS_DISCARD_FL; + return 1; } static int handle_metadata(struct qcow2 *qcow2, struct qio **qio, @@ -3344,6 +3431,9 @@ static int handle_metadata(struct qcow2 *qcow2, struct qio **qio, /* Nothing to COW or L1 is mapped exactly once */ qio_endio(*qio); ret = 0; + } else if (unlikely(op_is_discard((*qio)->bi_op)) && + qio_discard_updates_metadata(qcow2, *qio, map)) { + ret = prepare_cluster_discard(qcow2, qio, map); } else if (write && (!qio_is_fully_alloced(qcow2, *qio, map) || map->clu_is_cow)) { if (map->clu_is_cow) { @@ -3365,7 +3455,7 @@ static int handle_metadata(struct qcow2 *qcow2, struct qio **qio, qio_endio(*qio); ret = 0; } - /* Otherwise issue_discard(). TODO: update L2 */ + /* Otherwise issue_discard(). */ } else { /* Wants L1 or L2 entry allocation */ ret = prepare_l1l2_allocation(qcow2, *qio, map); @@ -3479,6 +3569,12 @@ static void process_one_qio(struct qcow2 *qcow2, struct qio *qio) write = op_is_write(qio->bi_op); + /* Discard with prepared metadata update, see prepare_cluster_discard() */ + if (unlikely(qio->flags & QIO_IS_DISCARD_FL)) { + issue_discard(&map, qio); + return; + } + if (unlikely(map.compressed)) { /* Compressed qio never uses sub-clus */ submit_read_compressed(&map, qio, write); diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h index 230e7a4a34e76..8a24e04130e4d 100644 --- a/drivers/md/dm-qcow2.h +++ b/drivers/md/dm-qcow2.h @@ -340,7 +340,7 @@ struct qio { blk_status_t bi_status; #define QIO_FREE_ON_ENDIO_FL (1 << 0) /* Free this qio memory from qio_endio() */ #define QIO_IS_MERGE_FL (1 << 3) /* This is service merge qio */ -#define QIO_IS_DISCARD_FL (1 << 4) /* This zeroes index on backward merge */ +#define QIO_IS_DISCARD_FL (1 << 4) /* This zeroes index (discard or backward merge) */ #define QIO_IS_L1COW_FL (1 << 5) /* This qio only wants COW at L1 */ #define QIO_SPLIT_INHERITED_FLAGS (QIO_IS_DISCARD_FL) u8 flags; _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel