From nobody Tue Apr 7 21:45:29 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 ARC-Seal: i=1; a=rsa-sha256; t=1773242594; cv=none; d=zohomail.com; s=zohoarc; b=JmHCHOCuHLS1TvGGPEyfQ9wltSzynjN2v5qZgW2BjCesgUjeizOZiQ1hFZox9FcgYqJsgGHvn0+ISPC7CjM9egNiEyMZX2Yw5yvFXD5rl3mcsU946LXxR2HMiTMRbcXdW7KifWS7hU7ZJ1sLpfRIFCY7PVd6krkot5E5uDsE+fg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773242594; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=DZZ9FiibWf5kYZGfwMY+6mP68JCZnbdpf824LmjGFlg=; b=egoN5ThV7YXU8DG8Tc2luchJ6JLdCvxQgdQr1m+fB6Hj1yP+YT5JVqgZp82DOBWaKpihlKR8+UzFNYDF/CSv5AbkZMvpGdMyn1iCn5qu7ENt7T4+Iaw8UoiaFFUwNU9CHQUQtHRmMmhUcR5SzDwlVTF83BDK5AmPYNfXANH0ffw= 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 Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773242594618182.54218361561823; Wed, 11 Mar 2026 08:23:14 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w0LAJ-0004xO-53; Wed, 11 Mar 2026 11:08:05 -0400 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 1w0L7c-0008Qm-48; Wed, 11 Mar 2026 11:05:18 -0400 Received: from isrv.corpit.ru ([212.248.84.144]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w0L7Z-0004KM-Mo; Wed, 11 Mar 2026 11:05:15 -0400 Received: from tsrv.corpit.ru (tsrv.tls.msk.ru [192.168.177.2]) by isrv.corpit.ru (Postfix) with ESMTP id B3C9F191E5E; Wed, 11 Mar 2026 18:01:45 +0300 (MSK) Received: from think4mjt.tls.msk.ru (mjtthink.wg.tls.msk.ru [192.168.177.146]) by tsrv.corpit.ru (Postfix) with ESMTP id 9EE1337C2B0; Wed, 11 Mar 2026 18:02:23 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tls.msk.ru; s=202602; t=1773241305; bh=EobHR6aZuNGAg48s15DbsxWeloD5KaiXWl+B9fhDdvc=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Iti9VnXkjKZ+HeEB0ukEX08k1JW7uxg/KgcEoGQOabBCb+uyRE4Jmst3+5SrcsdAN WiCAYlzk6QwTjpveLcwhP8TBl3UThgMvmvYN3xUfb9DeQQqwholDuHyHVQNTFsNrlW hEpPFil3bqiGqXf67+AvzqRSs4R/Elk6r+A5tzS/nOUv1IZo/tJq/za6dzUeChLRSH yvCFNRHGjC05pXn97OiCwjCymvTqNFLeRTy1G78CXKTxyv6JZkNoOk7WJdgASOWBUh 5ILFm9+qRZyUwB0bKc2dRp4DNZqNb/4N3VeuDSk828JzuBoLfFhm1ynY0KfciOV4q4 ieSIzUQwMSPgw== From: Michael Tokarev To: qemu-devel@nongnu.org Cc: qemu-stable@nongnu.org, Kevin Wolf , Fiona Ebner , Jean-Louis Dupond , Michael Tokarev Subject: [Stable-10.0.9 32/44] mirror: Fix missed dirty bitmap writes during startup Date: Wed, 11 Mar 2026 18:02:04 +0300 Message-ID: <20260311150221.1084186-32-mjt@tls.msk.ru> X-Mailer: git-send-email 2.47.3 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=212.248.84.144; envelope-from=mjt@tls.msk.ru; helo=isrv.corpit.ru X-Spam_score_int: -2 X-Spam_score: -0.3 X-Spam_bar: / X-Spam_report: (-0.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.819, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.903, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no 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 @tls.msk.ru) X-ZM-MESSAGEID: 1773242595283158500 Content-Type: text/plain; charset="utf-8" From: Kevin Wolf 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 Message-ID: <20260219202446.312493-1-kwolf@redhat.com> Reviewed-by: Fiona Ebner Tested-by: Jean-Louis Dupond Signed-off-by: Kevin Wolf (cherry picked from commit 0f51f9c3420b31bb383e456dd7bf24d3056eeb73) (Mjt: context fix in block/mirror.c:mirror_start_job() - bdrv_graph_wrlock_= drained() is not in 10.0) Signed-off-by: Michael Tokarev diff --git a/block/mirror.c b/block/mirror.c index a53582f17b..db2af08050 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -98,6 +98,7 @@ typedef struct MirrorBlockJob { =20 typedef struct MirrorBDSOpaque { MirrorBlockJob *job; + BdrvDirtyBitmap *dirty_bitmap; bool stop; bool is_commit; } MirrorBDSOpaque; @@ -1557,9 +1558,11 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, Mirro= rMethod method, abort(); } =20 - if (!copy_to_target && s->job && s->job->dirty_bitmap) { - qatomic_set(&s->job->actively_synced, false); - bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + if (!copy_to_target) { + if (s->job) { + qatomic_set(&s->job->actively_synced, false); + } + bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, bytes); } =20 if (ret < 0) { @@ -1785,13 +1788,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, @@ -1886,24 +1911,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(); ret =3D block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | @@ -1983,9 +1997,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 @@ -1999,6 +2010,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.47.3