From nobody Sun Jul 26 11:51:04 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=openvz.org ARC-Seal: i=1; a=rsa-sha256; t=1784201028; cv=none; d=zohomail.com; s=zohoarc; b=OZ7DPO3C2qIP81aJjpv4IAZfbLx2znx0IQuLIFyyASz0UiJ1WcVwBY+g3OVFo/nJQDnQ6ZjFywixXrr/fYzWbDO/uqyY7W6LK0Tjzg2GJTLa8ee0dSU3Ek3qNVYYdJGM/XkFc/yCCrdy/Cc8feB25Ake9YMVlqfROHbPYXZhTcg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784201028; 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=jUbV7alaeVOad4qQ8VoPOBfCbDLvwGK/15GYJMqehXk=; b=GMl5e7+QVX2/UQjoUOfnIwcjovH0GkGNsPilGrsCmSV8hanVcqVQpk6qXjk/BRYa5Acx/SLXfx8/guIHA+z9m4BmSAiRyk5kxFvY8WeX/LaCq6d++g+6oGPsgVC6OPPj9sRA1OG4sasnZpmhKm84eS+wQV3VSqhVP+D7df6/UrE= 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1784201028654155.93307113224103; Thu, 16 Jul 2026 04:23:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wkKB2-0002RV-R2; Thu, 16 Jul 2026 07:22:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wkKB0-0002Ko-FQ for qemu-devel@nongnu.org; Thu, 16 Jul 2026 07:22:50 -0400 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wkKAy-0005E6-0E for qemu-devel@nongnu.org; Thu, 16 Jul 2026 07:22:50 -0400 Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-493d92b7db3so24108725e9.2 for ; Thu, 16 Jul 2026 04:22:47 -0700 (PDT) Received: from athena.sw.ru ([2a06:5b06:b600:300:4ca9:146f:3485:e2c5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47f464a96fdsm24019672f8f.24.2026.07.16.04.22.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jul 2026 04:22:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvz.org; s=google; t=1784200966; x=1784805766; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jUbV7alaeVOad4qQ8VoPOBfCbDLvwGK/15GYJMqehXk=; b=tzv2cKgNYralEw6f8GJDT5UipxQ0u9xxXkkTwDKr4wSg5Z6liuRky1VSYBIfb+Ug0m 8PGItIHaGWeF8ECL45sf3xvyOyFjJIJpOi5tGJsr1L16nGal9JdgB9+F9uygvHZCEpDQ +2DwMVGpBjUMaekobHu2AznLh2eiJ0Tdzf8lK/wkGQTfKMMh7gGzXdeum6Kv+n1rMlJm 1e4K3G3T2r5B6tKX2HeEXxtAMSxXzraSViv63lKlebyfDzoj8i1PhyZtNpa5vCeqBAvg 1ke0k0uUJ/bRq1cBnYlKbx/MyM5my/8OKTMXk4N3WmDSS3QHYDdUX1dxewzXjii+Z+md H6Xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784200966; x=1784805766; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=jUbV7alaeVOad4qQ8VoPOBfCbDLvwGK/15GYJMqehXk=; b=nsE0TynWHoTkX96aKaNuYRCN+kkQiWxyBqvjxJbsv3nXI95kU1/h01Fq3i/lPSvkXm i46okbeqphiF5G0g5jBfjjBZM/QYftm0zrFs+4whuFYLaJiLd9+bc+ix8AdxmKuNUx9Z aPUf2h1d95XnIYPIfphMZ1276fa8YsdT53Cn1me30GRBbq2l9PdnieYq/firqto9CfwW YqJD5AFZoAd+A0zrJn/kabX6wFV2VgC3HgA/Hc9vk1pBi6GCNDbOKXKG1cy2qUAMvihz eW4Sl1BINXWa6kUJbfRei8eUIhirTXiUt8OiJcxzouiIIxukBGeoF99ewldFj99ft2JG gCng== X-Forwarded-Encrypted: i=1; AHgh+Rq4ffi1rfV4LpWN1T4TRINUTf8PzkBmrSAZObk8TkC5OsKnqCB/QfgGHH2Sg5wCMN1LA/3h70tBRgAL@nongnu.org X-Gm-Message-State: AOJu0YxXPwoQSy9QI5+1jQR2Ji2xWn85JcGleGQzQJs+kscETDNiy3Sm LZ2ZTX56A2/Nc2rw3BVyIbILcFyd3OQwzHHv9ggl7YJWZ7lIG0e0NfZW2z79Nq1Qc74= X-Gm-Gg: AfdE7cmiEqplZnCG7+rDL98UKiZzcuB4HADUPuHf7GNQzZyat88I/D675LhqXru041X 9PvGed2Tm0h3TlilU0bg8mDs0zZdgJ4sz+T4+8OstGXKESvetnZjJcbUAUypvO7AbxOjEGEmwkt 1WTkg9FsRHPfACTY+yqN7Yhl7wM6nxHX3yEL7f86XmAS1nmpMxMwo4alxZhuVYeU5btKURntzMT x67rvS1JlwmCeRUunQnNvGChc20VL9HhNU68JWjYFZs0tfcQ/x4yanvAjuQhRfXTWAmnEe0Vw5K lmfe1U7gpZqZXh7HYhWWfttUGlXaOXgz0PJm5GpbFKuCIJJ7gjtLDYivpJuG1NSLnI4AqMkwi/B uNh6II2zHnC+D7PrRBe1pcu0kWXnAEIp4N40a1BqjJB7O1eDNMjVj8pZru8vzh37WVpP+4Z5qj5 I= X-Received: by 2002:a05:600c:4fc3:b0:495:4598:6e13 with SMTP id 5b1f17b1804b1-49545986eaemr6603995e9.17.1784200966198; Thu, 16 Jul 2026 04:22:46 -0700 (PDT) From: "Denis V. Lunev" To: qemu-block@nongnu.org, qemu-devel@nongnu.org Cc: den@openvz.org, Eric Blake , Vladimir Sementsov-Ogievskiy , John Snow , Andrey Drobyshev Subject: [PATCH v2 1/3] block/monitor: reject persistent bitmap add on a read-only node Date: Thu, 16 Jul 2026 13:22:40 +0200 Message-ID: <20260716112242.3000035-2-den@openvz.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260716112242.3000035-1-den@openvz.org> References: <20260716112242.3000035-1-den@openvz.org> 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=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32a; envelope-from=den@openvz.org; helo=mail-wm1-x32a.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @openvz.org) X-ZM-MESSAGEID: 1784201031894158500 Content-Type: text/plain; charset="utf-8" qmp_block_dirty_bitmap_add() marks a new bitmap persistent without checking write access to its node. bdrv_create_dirty_bitmap() always creates bitmaps writable, so a persistent bitmap added to an already read-only node stays writable in memory on a node that can never store it, and the next global inactivation fails: Lost persistent bitmaps during inactivation of node '': No write ac= cess migration_block_inactivate: bdrv_inactivate_all() failed: -22 Forcing it read-only instead does not help: it was never stored, so it stays unpromotable on the next reopen to read-write and can trip bdrv_set_dirty()'s readonly assert on the first write. Reject the add instead, for both read-only and inactive nodes -- an already-inactive node skips qcow2_inactivate() on close, so a bitmap added during that window would never get stored either. Wrapped in a transaction, this denies the whole transaction, since qmp_transaction() is already all-or-none. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/monitor/bitmap-qmp-cmds.c | 15 +++++++--- qapi/block-core.json | 4 ++- .../tests/remove-bitmap-from-backing | 29 ++++++++++++++++++- .../tests/remove-bitmap-from-backing.out | 20 +++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmd= s.c index a738e7bbf7..d87ca982aa 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -125,10 +125,17 @@ void qmp_block_dirty_bitmap_add(const char *node, con= st char *name, disabled =3D false; } =20 - if (persistent && - !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) - { - return; + if (persistent) { + if (!bdrv_is_writable(bs)) { + error_setg(errp, "Cannot add a persistent bitmap to " + "read-only or inactive node '%s'", + bdrv_get_node_name(bs)); + return; + } + + if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))= { + return; + } } =20 bitmap =3D bdrv_create_dirty_bitmap(bs, granularity, name, errp); diff --git a/qapi/block-core.json b/qapi/block-core.json index 1f87b07850..199efc1e00 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2353,7 +2353,9 @@ # @persistent: the bitmap is persistent, i.e. it will be saved to the # corresponding block device image file on its close. For now # only Qcow2 disks support persistent bitmaps. Default is false -# for `block-dirty-bitmap-add`. (Since: 2.10) +# for `block-dirty-bitmap-add`. This fails if the node is +# read-only or inactive, since such a bitmap could never be +# stored. (Since: 2.10) # # @disabled: the bitmap is created in the disabled state, which means # that it will not track drive changes. The bitmap may be enabled diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qe= mu-iotests/tests/remove-bitmap-from-backing index 15be32dcb9..a54984fa58 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -35,7 +35,7 @@ qemu_img('bitmap', '--add', base, 'bitmap0') # Just assert that our method of checking bitmaps in the image works. assert 'bitmaps' in qemu_img_info(base)['format-specific']['data'] =20 -vm =3D iotests.VM().add_drive(top, 'backing.node-name=3Dbase') +vm =3D iotests.VM().add_drive(top, 'node-name=3Dtop,backing.node-name=3Dba= se') vm.launch() =20 log('Trying to remove persistent bitmap from r-o base node, should fail:') @@ -66,6 +66,33 @@ result =3D vm.qmp('blockdev-reopen', **new_base_opts) if result !=3D {'return': {}}: log('Failed to reopen: ' + str(result)) =20 +log('Adding a persistent bitmap to the r-o base node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node=3D'base', name=3D'bitmap1', + persistent=3DTrue) + +log('Same add inside a transaction, preceded by an otherwise valid') +log('action: the whole transaction must fail and roll back the') +log('already-succeeded first action too:') +vm.qmp_log('transaction', actions=3D[ + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'top', 'name': 'bitmap2', 'persistent': True}}, + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'base', 'name': 'bitmap1', 'persistent': True}}, +]) + +log('bitmap2 on the rw top node must not have survived the rollback:') +vm.qmp_log('block-dirty-bitmap-remove', node=3D'top', name=3D'bitmap2') + +log('Marking the rw top node inactive:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': False}) + +log('Adding a persistent bitmap to a rw but inactive node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node=3D'top', name=3D'bitmap3', + persistent=3DTrue) + +log('Reactivating the top node:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': True}) + vm.shutdown() =20 if 'bitmaps' in qemu_img_info(base)['format-specific']['data']: diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/test= s/qemu-iotests/tests/remove-bitmap-from-backing.out index c28af82c75..fe105fe0a3 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -4,3 +4,23 @@ Trying to remove persistent bitmap from r-o base node, sho= uld fail: Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", = "node": "base"}} {"return": {}} +Adding a persistent bitmap to the r-o base node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap1", "no= de": "base", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitma= p to read-only or inactive node 'base'"}} +Same add inside a transaction, preceded by an otherwise valid +action: the whole transaction must fail and roll back the +already-succeeded first action too: +{"execute": "transaction", "arguments": {"actions": [{"data": {"name": "bi= tmap2", "node": "top", "persistent": true}, "type": "block-dirty-bitmap-add= "}, {"data": {"name": "bitmap1", "node": "base", "persistent": true}, "type= ": "block-dirty-bitmap-add"}]}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitma= p to read-only or inactive node 'base'"}} +bitmap2 on the rw top node must not have survived the rollback: +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap2", = "node": "top"}} +{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap2' not fo= und"}} +Marking the rw top node inactive: +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-na= me": "top"}} +{"return": {}} +Adding a persistent bitmap to a rw but inactive node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap3", "no= de": "top", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitma= p to read-only or inactive node 'top'"}} +Reactivating the top node: +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-nam= e": "top"}} +{"return": {}} --=20 2.53.0 From nobody Sun Jul 26 11:51:04 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=openvz.org ARC-Seal: i=1; a=rsa-sha256; t=1784201031; cv=none; d=zohomail.com; s=zohoarc; b=FnN0ENQo1RTIx1tU6Fusr6UpaQc0VE69lo3AOYNsZt6dwfB/pGn4frMTKMKD9FUkO1p+3WZseL6x74qUti13CuoU5aDZFVqAb+2zgHZ4hUaqqR02pL/XLDA+Fj+VHbdApiMjPG7ome01BxkLSRgt8QF7tWcIXKEaF1VqjdNNFuk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784201031; 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=R1x24EIH9Zws+U6zhZ4YK6KCdLCmyavM2XPJ+59zwwk=; b=RRVGsrjRjwyvox8Y/OixITMGskt6dovKDnj0UeVzDwYX6/JED6NRTenWxzKLReEDQdNW9z/IeUpxvGExy7olTZEvR38V7x0SUUIogXNS0NhjOc+9HxdQocJiXC4T8l54lxT8j0Ar1faMSPKs4nHixXzBgpNtiOhHLi8lJBoFTZg= 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17842010311561001.0015484978611; Thu, 16 Jul 2026 04:23:51 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wkKB3-0002Rb-Co; Thu, 16 Jul 2026 07:22:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wkKB0-0002Lh-MV for qemu-devel@nongnu.org; Thu, 16 Jul 2026 07:22:50 -0400 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wkKAy-0005EE-P7 for qemu-devel@nongnu.org; Thu, 16 Jul 2026 07:22:50 -0400 Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-495437bb891so2606515e9.1 for ; Thu, 16 Jul 2026 04:22:48 -0700 (PDT) Received: from athena.sw.ru ([2a06:5b06:b600:300:4ca9:146f:3485:e2c5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47f464a96fdsm24019672f8f.24.2026.07.16.04.22.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jul 2026 04:22:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvz.org; s=google; t=1784200967; x=1784805767; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=R1x24EIH9Zws+U6zhZ4YK6KCdLCmyavM2XPJ+59zwwk=; b=ZJtq0Ub+9v021oWQQBI7hJDc9AzQlZ3Af6Q8eEQ5BCbS9DA0zCSIannX54ZzrPF/2X lzy+hp1u2JZWgPe1k5koz4HegzAWuW/G7hRzvvi5CkM1Wvd//eiW12C4NpiEyuH+wXkB pSg96pGahqx7V+G9XOBmd+3h8vmsz1eJc99aSx1+f28sArPcQtyq1aok1YSneiQwF0+l PmnGKNhw0vDB27QrEC97KjWuxSUBprAt3vbpbM123U6D8fzIYwn2EXO3dh6vAdgX1N/O 6Zh9oMAeZdv6RYO1N1Uy5IVCeuzbyHcGZ9d0YR//9RBAqZNUlML4SKNafnhMFEbsMeiY WuLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784200967; x=1784805767; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=R1x24EIH9Zws+U6zhZ4YK6KCdLCmyavM2XPJ+59zwwk=; b=Bgt54JpJ8W1gu92dnV6z2Qqt0j2FvcG685FL5KXu5Irlm08/RYvB0DPWUEo1c6DVqE +bjhrqdgjn+5LURPbPe/SHni+tcYfnxtZTg3lr0G3eijrG0iRhGjqfsOBlt8vRMEfYOf up8iu/tDXmk5msMEfi6lYh/8epJaWixCIQxHJboR3ZM+OPy8AgqlCoTcPg6ZEXewnpFS 5yEg+o9WkCPplLfYM0dSqlUixE60MgkBbWFUHncVGT/rUCF+x79GRctUMsB7S5hs+87S /WgNZ31nZBKIiXZe6GcI3ZG3Upp/DGE7rQu9ZkiZihCshfTI/nIfqqebO7PV7SwbpE0e O3Cg== X-Forwarded-Encrypted: i=1; AHgh+RqAitWvdvxlf9vaV8foWZaBqF3631AdsvPR6vhOZEBV3ZIkbjIczYu1Z0yK2Je7pw387Sc4/b99STIq@nongnu.org X-Gm-Message-State: AOJu0Yzb443QtGTRo9IzGXkRodXAvms1/UfZq85UV61U2J2D0MHBjLFl TnBsLfggXaJ1WgkkRdstIKCU1/E3cFnGo4d2eRsy5WV7vPJ2UVFCwtru/gh/dOgDqao= X-Gm-Gg: AfdE7cmwR3B9XMsFTFTGsY1kg2ri8dvBRGBoGzn802toxD3PNCaM0HOhzmPBQadVDdZ pZSwnSboLwjzcR/7BKVoTeuBHvohBqChdu8xIw+uxA3TBu6Aos1tXH1qYCe7vy6Cr2Q2eyllb1C 2nPZbMqLwUEMJIt/itLWrd4avSIh4qYx2ZOkpson0MwQO/s7o9MNSAtILFH0Lrn59FCgg/pPvDK Ef1GCWX5s4eYieSGXKTvEHUwv/RjHI9e5NLuJyEd2TEQzax62f1otwKTDnJqjPTtL5vETQnygwd CepoBKwX1eOqHshQRhVJMJSLtnXmDwMqC0V+Hk8+fIO0df6xk97aiQnVSyrZzCAFAoLLPwZkso5 XlTjR2qbQKoERKlCVG9amvV1M+XyCx+iqhZph67O4pamW9iouc2SCV1w70BUDYcmOU+DeNoQU0O U= X-Received: by 2002:a05:600c:3552:b0:494:1f7:8057 with SMTP id 5b1f17b1804b1-4954286a3b4mr22010785e9.1.1784200967328; Thu, 16 Jul 2026 04:22:47 -0700 (PDT) From: "Denis V. Lunev" To: qemu-block@nongnu.org, qemu-devel@nongnu.org Cc: den@openvz.org, Eric Blake , Vladimir Sementsov-Ogievskiy , John Snow , Andrey Drobyshev Subject: [PATCH v2 2/3] migration/block-dirty-bitmap: reject bitmap load onto ro node Date: Thu, 16 Jul 2026 13:22:41 +0200 Message-ID: <20260716112242.3000035-3-den@openvz.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260716112242.3000035-1-den@openvz.org> References: <20260716112242.3000035-1-den@openvz.org> 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=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32a; envelope-from=den@openvz.org; helo=mail-wm1-x32a.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=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 @openvz.org) X-ZM-MESSAGEID: 1784201033337158501 Content-Type: text/plain; charset="utf-8" dirty_bitmap_load_start() creates an incoming migrated bitmap with bdrv_create_dirty_bitmap() and, if the source marked it persistent, calls bdrv_dirty_bitmap_set_persistence() without checking whether the destination node can be written to. Same gap as qmp_block_dirty_bitmap_add(), reached via incoming migration: a persistent bitmap for a read-only destination (e.g. a migrated CD-ROM-class attachment with dirty-bitmaps migration enabled) ends up writable in memory on a node that can never store it. Reject it the same way, with one difference from the QMP path: every destination node is BDRV_O_INACTIVE until migration completes, so bdrv_is_writable() would reject every incoming persistent bitmap, not just read-only ones. Check bdrv_is_read_only() alone. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Reviewed-by: Vladimir Sementsov-Ogievskiy --- migration/block-dirty-bitmap.c | 22 +++++++++---- tests/qemu-iotests/tests/migrate-bitmaps-test | 33 +++++++++++++++++++ .../tests/migrate-bitmaps-test.out | 4 +-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index cba54e25cd..1b8f39c12b 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -812,13 +812,6 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoa= dState *s) error_report("Bitmap with the same name ('%s') already exists on " "destination", bdrv_dirty_bitmap_name(s->bitmap)); return -EINVAL; - } else { - s->bitmap =3D bdrv_create_dirty_bitmap(s->bs, granularity, - s->bitmap_name, &local_err); - if (!s->bitmap) { - error_report_err(local_err); - return -EINVAL; - } } =20 if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) { @@ -835,6 +828,21 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoa= dState *s) persistent =3D flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; } =20 + /* Not bdrv_is_writable(): nodes stay inactive until migration ends. */ + if (persistent && bdrv_is_read_only(s->bs)) { + error_report("Cannot make migrated bitmap '%s' persistent " + "on read-only node '%s'", s->bitmap_name, + bdrv_get_node_name(s->bs)); + return -EINVAL; + } + + s->bitmap =3D bdrv_create_dirty_bitmap(s->bs, granularity, + s->bitmap_name, &local_err); + if (!s->bitmap) { + error_report_err(local_err); + return -EINVAL; + } + if (persistent) { bdrv_dirty_bitmap_set_persistence(s->bitmap, true); } diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iot= ests/tests/migrate-bitmaps-test index 8fb4099201..cb9154ca8d 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -206,6 +206,39 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.vm_b.launch() self.check_bitmap(self.vm_b, sha256 if persistent else False) =20 + def test_migration_to_readonly_destination(self): + granularity =3D 512 + mig_caps =3D [{'capability': 'events', 'state': True}, + {'capability': 'dirty-bitmaps', 'state': True}] + + self.vm_b.add_incoming("defer") + self.vm_b.add_drive(disk_b, 'read-only=3Don') + + self.add_bitmap(self.vm_a, granularity, True) + self.vm_a.hmp_qemu_io('drive0', 'write 0 4096') + + self.vm_a.cmd('migrate-set-capabilities', capabilities=3Dmig_caps) + self.vm_a.cmd('migrate', uri=3Dmig_cmd) + while True: + event =3D self.vm_a.event_wait('MIGRATION') + if event['data']['status'] =3D=3D 'completed': + break + self.vm_a.shutdown() + + self.vm_b.launch() + self.vm_b.cmd('migrate-set-capabilities', capabilities=3Dmig_caps) + self.vm_b.cmd('migrate-incoming', uri=3Dincoming_cmd) + while True: + event =3D self.vm_b.event_wait('MIGRATION') + if event['data']['status'] in ('completed', 'failed'): + break + + self.assert_qmp(event, 'data/status', 'failed') + + # A failed incoming load makes the destination process exit on + # its own; reap it so tearDown()'s shutdown() is a clean no-op. + self.vm_b.wait() + =20 def inject_test_case(klass, suffix, method, *args, **kwargs): mc =3D operator.methodcaller(method, *args, **kwargs) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test.out b/tests/qemu= -iotests/tests/migrate-bitmaps-test.out index cafb8161f7..73e375a9d7 100644 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test.out +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test.out @@ -1,5 +1,5 @@ -..................................... +...................................... ---------------------------------------------------------------------- -Ran 37 tests +Ran 38 tests =20 OK --=20 2.53.0 From nobody Sun Jul 26 11:51:04 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=openvz.org ARC-Seal: i=1; a=rsa-sha256; t=1784200994; cv=none; d=zohomail.com; s=zohoarc; b=fNwwKxcV5LW3psOHArKSvmkaIBQ3h4CuIjYIXGt8dNm9/kdjbUM82ZoZdomu9cqHsO1Nxd3vgckWMzkp8isxKQb6NyQOBzhzQx6+mtDiGCvVY7zLj33rWO6okn/m7Dj2FVbuu7OStfy9AXyreQEg7IdgotboDQAdy98M1W8xX+0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784200994; 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=ZDV1Ev+3zB9CQS2nSF+kvUKo9rgUplCzN5eq7uGNPP0=; b=aDZbQpY2lCk9KrUwzRf9UyLnKADJqTCdUpQSnBHuKwynRhQnfLj9QSThtIyNQEgXIJ5HQAd+QgXvdMEjC3Ka6vZAQoBOurc1JGuEY48a8aIfMW1ztvIbDo093GAssiF/6RiJz5O37kDn4jwTYloGI5PJ9lA3K61W/ZZ2f5D6YeU= 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1784200993946308.1290009045151; Thu, 16 Jul 2026 04:23:13 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wkKB3-0002S3-Ur; Thu, 16 Jul 2026 07:22:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wkKB1-0002Q7-Qi for qemu-devel@nongnu.org; Thu, 16 Jul 2026 07:22:51 -0400 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wkKAz-0005EU-Vu for qemu-devel@nongnu.org; Thu, 16 Jul 2026 07:22:51 -0400 Received: by mail-wm1-x32f.google.com with SMTP id 5b1f17b1804b1-493e4ccccc2so40632625e9.2 for ; Thu, 16 Jul 2026 04:22:49 -0700 (PDT) Received: from athena.sw.ru ([2a06:5b06:b600:300:4ca9:146f:3485:e2c5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47f464a96fdsm24019672f8f.24.2026.07.16.04.22.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jul 2026 04:22:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openvz.org; s=google; t=1784200968; x=1784805768; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ZDV1Ev+3zB9CQS2nSF+kvUKo9rgUplCzN5eq7uGNPP0=; b=VK+48AFoI5a/pDYMbOGriuAlTEiQZPxxuD9L0WDN0nmHCSz2Wbwm6aQ3x6mTBTTasR W9YN9BX50oikT7FIDW9NcdXG5fCKK2JEBNwgt7clrI5vOrerg+T112y646vjcvlKRbDC RPDYONgYyOemsAl+JiaMRE6RaA6GD6PXlXIzknvszTJEScy4W13gaZedS5QKxQnO2ORe 7MvCrdpAkgBxYfmWw4eQ7D+yh6oaEL6U2CzK0El6gerwhQrju1eXjA0hrJUhOGRHNPdB hC7PXiN0UrvmvoVlwHJY0GCqSWNUV9sN5KgDUnAzNntmjcNqYgNHAaG6E7YZaRwJrkhY eL+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784200968; x=1784805768; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ZDV1Ev+3zB9CQS2nSF+kvUKo9rgUplCzN5eq7uGNPP0=; b=gNKAKEuOzh62Hz58oeKtLIy4WS2feOm4l2GGjIiJCGGP48wLk2e/3XDdhOS8YVmC1V /Ymm0I0FNNO8x0jOfcSV3LL5CftF9j0KfNGVeI89GYV08pprJJ4pnASzX/Yt1QbirYSA /C+gPdCBEMETs4vo89Svm0UnOz09zFRknJWq6tDTKZe7wOARq3GdNUNZvTCbObTvehfK N4MXYnbmmnrL3BhQvsOwGaRup6KkX59Y6DpKOsOJwj8PQBGQXF5rkgx/fQaL6OONixOZ J5Pzo2lT+8Nj6BHJO4EcAYZDc/1y5oqAAImjk+/DXomBsvCTHwXAGtBhyar/sSkfCg9w Ux6g== X-Forwarded-Encrypted: i=1; AHgh+Rq81lUQ0l1fwhr2R8XccENHKZQf4hIhQr/F3M6xKYS1FIWDJf4R7yreW/ywns2vaih9rMRhcw6woBiJ@nongnu.org X-Gm-Message-State: AOJu0YxBur1iFQGlsXVEv2cecc/n1DErMik6U4HLqXNf7gt305DQoebm odfvbAiZxdqn4UV6l6rxcQJVThqdeFAXE++AXXo4xpZI0MlPtzivUh8zPcqCQulLgwI= X-Gm-Gg: AfdE7cmQZ9RPcaOqc0MvfgF+D2w5JdTUBBkhiD0ukJUIio4Xl41CwTjC0AINmiyd5cQ Er2bz1C6BwSnNzi46bjj6ouxIkUq0L1jTXAr6sIPj2NnezI6iq9FnK2l8oOV/Z5MvszM0/rYinG jIn/Rh5loKizIuoYQB2rul4onwGTUlVdfqbORkJu72jhoEPdDM9NyDmDjxOcO6QXcUBkM+KPeBw ins0F5mxST7MNdZZL6JQB+OiLNhdacQKB4/o1+i50DjC5SFpEFMz7wrVrYLCL42zCw78uayj699 AxdZD8FK0m3kzVKM+hr1Ku/tV94FhYxNOLKEednAIF4G+qxz0Ix8wNRi95ysOl5K0vLh/+ASORo mryvj0Yi5MgMrMd/5SYjL7wCVzMf7lxVEejfkteDPMHLyRP1/AxBXeuUhGYylLWLnH9U7Ol//cD c= X-Received: by 2002:a05:600c:5292:b0:495:40aa:d982 with SMTP id 5b1f17b1804b1-49540aad9a1mr35567695e9.6.1784200968410; Thu, 16 Jul 2026 04:22:48 -0700 (PDT) From: "Denis V. Lunev" To: qemu-block@nongnu.org, qemu-devel@nongnu.org Cc: den@openvz.org, Eric Blake , Vladimir Sementsov-Ogievskiy , John Snow , Andrey Drobyshev Subject: [PATCH v2 3/3] block/monitor: allow dropping a bitmap never stored on disk Date: Thu, 16 Jul 2026 13:22:42 +0200 Message-ID: <20260716112242.3000035-4-den@openvz.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260716112242.3000035-1-den@openvz.org> References: <20260716112242.3000035-1-den@openvz.org> 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=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32f; envelope-from=den@openvz.org; helo=mail-wm1-x32f.google.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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @openvz.org) X-ZM-MESSAGEID: 1784200995683158500 Content-Type: text/plain; charset="utf-8" block-dirty-bitmap-remove refuses any readonly bitmap outright, via the generic BDRV_BITMAP_RO check in bdrv_dirty_bitmap_check(). That check cannot tell whether the bitmap is actually on disk, so it also blocks dropping one that only ever existed in memory, which needs no write at all. Drop the blanket check and let qcow2 decide: bdrv_remove_persistent_ dirty_bitmap() already treats an absent on-disk entry as a no-op, so such a bitmap is now released with no write attempted. For one that is genuinely stored, qcow2_co_remove_persistent_dirty_bitmap_locked() now checks can_write() before it would update the on-disk directory, so removal still fails there, with a message naming the actual reason instead of just the bitmap's readonly flag. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/monitor/bitmap-qmp-cmds.c | 4 ++-- block/qcow2-bitmap.c | 9 +++++++++ tests/qemu-iotests/tests/remove-bitmap-from-backing.out | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmd= s.c index d87ca982aa..aabf2790b6 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -165,11 +165,11 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char= *node, const char *name, return NULL; } =20 - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, - errp)) { + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { return NULL; } =20 + /* Dropping a bitmap needs no write access unless it is actually store= d. */ if (bdrv_dirty_bitmap_get_persistence(bitmap) && bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) { diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 256ec99878..ac5a724588 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1487,6 +1487,15 @@ int coroutine_fn qcow2_co_remove_persistent_dirty_bi= tmap(BlockDriverState *bs, goto out; } =20 + if (!can_write(bs)) { + error_setg(errp, "Cannot remove persistent bitmap '%s': " + "no write access to node '%s'", name, + bdrv_get_node_name(bs)); + ret =3D -EACCES; + bm =3D NULL; + goto out; + } + QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry); =20 ret =3D update_ext_header_and_dir(bs, bm_list); diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/test= s/qemu-iotests/tests/remove-bitmap-from-backing.out index fe105fe0a3..628fa737d9 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -1,6 +1,6 @@ Trying to remove persistent bitmap from r-o base node, should fail: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", = "node": "base"}} -{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap0' is readonly = and cannot be modified"}} +{"error": {"class": "GenericError", "desc": "Cannot remove persistent bitm= ap 'bitmap0': no write access to node 'base'"}} Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", = "node": "base"}} {"return": {}} --=20 2.53.0