From nobody Sun Apr 12 00:55:24 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1771532792; cv=none; d=zohomail.com; s=zohoarc; b=ehiRWx8bBwoQjO126ye6QnXnwAedNmqzTxWO3gx8Jwucvpew8xQVi4TN+Jcc0gJLjwdB9R2yUZdEoQfpF6a1W9r0TBxdyOl/2WXJyivn6ld/IuxnIXqdQCvAKjvIAUbtuhkbSOHHOdRbTa0b7W5tK2c5MufywoOwGHCwFVH5XS8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1771532792; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=58qWb9R8ozlzmlYn0tL0BRCdKM9hBwD/n3rT+b7X6Dc=; b=NRhP6zN0FaJ7jGq8sH6NyJU3Fcvq2fw4214zanF2C7rzCmFxDrsHNQdUlefQKlxDvgOQ0b7dfYjOKax3xpuLG9MrWEVkD09BBWU93Y1lDQnSGHeatX9/FdYtwr7bX0lTuIizYErGdhP/jRBaZV8F4dhaSUQ6QXwEbRNTJ0IdDtc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1771532792271419.48768614897574; Thu, 19 Feb 2026 12:26:32 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vtAb3-0002YW-IO; Thu, 19 Feb 2026 15:26:02 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vtAak-0002Tn-8e for qemu-devel@nongnu.org; Thu, 19 Feb 2026 15:25:44 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vtAab-00027x-Pn for qemu-devel@nongnu.org; Thu, 19 Feb 2026 15:25:40 -0500 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-342-B1k2YI6lMYGzGOLMwmcfCg-1; Thu, 19 Feb 2026 15:25:30 -0500 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5FB981955F21; Thu, 19 Feb 2026 20:25:28 +0000 (UTC) Received: from merkur.redhat.com (unknown [10.45.224.47]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 26391195410F; Thu, 19 Feb 2026 20:25:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1771532732; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=58qWb9R8ozlzmlYn0tL0BRCdKM9hBwD/n3rT+b7X6Dc=; b=Kv7WZLd0zvr3LusrWPF244xeLVjzt6TIZ7ok3ZRSwKlmahqDoA+8nGc15QfJn+uCWhEvb3 HsCIlPI1eW8s6iCYtatHv9nd1mRpzOOmDcI9QttvP/4QUAHb2l3/DwaA8qw3u4aBtHuPAo EPprsm/3QwYUbYZ2/VRCQT/jSe8wj58= X-MC-Unique: B1k2YI6lMYGzGOLMwmcfCg-1 X-Mimecast-MFC-AGG-ID: B1k2YI6lMYGzGOLMwmcfCg_1771532729 From: Kevin Wolf To: qemu-block@nongnu.org Cc: kwolf@redhat.com, hreitz@redhat.com, f.ebner@proxmox.com, vsementsov@yandex-team.ru, jsnow@redhat.com, jean-louis@dupond.be, dionbosschieter@gmail.com, qemu-devel@nongnu.org, qemu-stable@nongnu.org Subject: [PATCH] mirror: Fix missed dirty bitmap writes during startup Date: Thu, 19 Feb 2026 21:24:46 +0100 Message-ID: <20260219202446.312493-1-kwolf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=kwolf@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.045, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1771532794395158500 Content-Type: text/plain; charset="utf-8" Currently, mirror disables the block layer's dirty bitmap before its own replacement is working. This means that during startup, there is a window in which the allocation status of blocks in the source has already been checked, but new writes coming in aren't tracked yet, resulting in a corrupted copy: 1. Dirty bitmap is disabled in mirror_start_job() 2. Some request are started in mirror_top_bs while s->job =3D=3D NULL 3. mirror_dirty_init() -> bdrv_co_is_allocated_above() runs and because the request hasn't completed yet, the block isn't allocated 4. The request completes, still sees s->job =3D=3D NULL and skips the bitmap, and nothing else will mark it dirty either One ingredient is that mirror_top_opaque->job is only set after the job is fully initialized. For the rationale, see commit 32125b1460 ("mirror: Fix access of uninitialised fields during start"). Fix this by giving mirror_top_bs access to dirty_bitmap and enabling it to track writes from the beginning. Disabling the block layer's tracking and enabling the mirror_top_bs one happens in a drained section, so there is no danger of races with in-flight requests any more. All of this happens well before the block allocation status is checked, so we can be sure that no writes will be missed. Cc: qemu-stable@nongnu.org Closes: https://gitlab.com/qemu-project/qemu/-/issues/3273 Fixes: 32125b14606a ('mirror: Fix access of uninitialised fields during sta= rt') Signed-off-by: Kevin Wolf Tested-by: Jean-Louis Dupond --- Supersedes: <20260212120411.369498-1-f.ebner@proxmox.com> --- block/mirror.c | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index b344182c747..f38636e7457 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -99,6 +99,7 @@ typedef struct MirrorBlockJob { =20 typedef struct MirrorBDSOpaque { MirrorBlockJob *job; + BdrvDirtyBitmap *dirty_bitmap; bool stop; bool is_commit; } MirrorBDSOpaque; @@ -1672,9 +1673,9 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, Mirror= Method method, abort(); } =20 - if (!copy_to_target && s->job && s->job->dirty_bitmap) { + if (!copy_to_target) { qatomic_set(&s->job->actively_synced, false); - bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, bytes); } =20 if (ret < 0) { @@ -1901,13 +1902,35 @@ static BlockJob *mirror_start_job( =20 bdrv_drained_begin(bs); ret =3D bdrv_append(mirror_top_bs, bs, errp); - bdrv_drained_end(bs); - if (ret < 0) { + bdrv_drained_end(bs); + bdrv_unref(mirror_top_bs); + return NULL; + } + + bs_opaque->dirty_bitmap =3D bdrv_create_dirty_bitmap(mirror_top_bs, + granularity, + NULL, errp); + if (!bs_opaque->dirty_bitmap) { + bdrv_drained_end(bs); bdrv_unref(mirror_top_bs); return NULL; } =20 + /* + * The mirror job doesn't use the block layer's dirty tracking because= it + * needs to be able to switch seemlessly between background copy mode = (which + * does need dirty tracking) and write blocking mode (which doesn't) a= nd + * doing that would require draining the node. Instead, mirror_top_bs = takes + * care of updating the dirty bitmap as appropriate. + * + * Note that write blocking mode only becomes effective after mirror_r= un() + * sets mirror_top_opaque->job (see should_copy_to_target()). Until th= en, + * we're still in background copy mode irrespective of @copy_mode. + */ + bdrv_disable_dirty_bitmap(bs_opaque->dirty_bitmap); + bdrv_drained_end(bs); + /* Make sure that the source is not resized while the job is running */ s =3D block_job_create(job_id, driver, NULL, mirror_top_bs, BLK_PERM_CONSISTENT_READ, @@ -2002,24 +2025,13 @@ static BlockJob *mirror_start_job( s->base_overlay =3D bdrv_find_overlay(bs, base); s->granularity =3D granularity; s->buf_size =3D ROUND_UP(buf_size, granularity); + s->dirty_bitmap =3D bs_opaque->dirty_bitmap; s->unmap =3D unmap; if (auto_complete) { s->should_complete =3D true; } bdrv_graph_rdunlock_main_loop(); =20 - s->dirty_bitmap =3D bdrv_create_dirty_bitmap(s->mirror_top_bs, granula= rity, - NULL, errp); - if (!s->dirty_bitmap) { - goto fail; - } - - /* - * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in a= ctive - * mode. - */ - bdrv_disable_dirty_bitmap(s->dirty_bitmap); - bdrv_graph_wrlock_drained(); ret =3D block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | @@ -2099,9 +2111,6 @@ fail: g_free(s->replaces); blk_unref(s->target); bs_opaque->job =3D NULL; - if (s->dirty_bitmap) { - bdrv_release_dirty_bitmap(s->dirty_bitmap); - } job_early_fail(&s->common.job); } =20 @@ -2115,6 +2124,7 @@ fail: bdrv_graph_wrunlock(); bdrv_drained_end(bs); =20 + bdrv_release_dirty_bitmap(bs_opaque->dirty_bitmap); bdrv_unref(mirror_top_bs); =20 return NULL; --=20 2.53.0