From nobody Sun Jul 26 13:29:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781173449604552.660313858799; Thu, 11 Jun 2026 03:24:09 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wXcZ4-0001kd-BM; Thu, 11 Jun 2026 06:23:10 -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 1wXcYz-0001gX-AK; Thu, 11 Jun 2026 06:23:05 -0400 Received: from proxmox-new.maurer-it.com ([94.136.29.106]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wXcYw-0002RL-Nq; Thu, 11 Jun 2026 06:23:04 -0400 Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id BE20645CAE; Thu, 11 Jun 2026 12:22:52 +0200 (CEST) From: Fiona Ebner To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, hreitz@redhat.com, kwolf@redhat.com Subject: [PATCH 1/3] iotests/041: add test for mirror with throttle node as source Date: Thu, 11 Jun 2026 12:22:26 +0200 Message-ID: <20260611102247.622275-2-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260611102247.622275-1-f.ebner@proxmox.com> References: <20260611102247.622275-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1781173323914 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=94.136.29.106; envelope-from=f.ebner@proxmox.com; helo=proxmox-new.maurer-it.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, 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-ZM-MESSAGEID: 1781173450281158501 Content-Type: text/plain; charset="utf-8" Mostly in preparation for commit "iotests/041: add tests for mirroring below throttle node" which adds test cases for commit "blockdev: mirror: do not require a root block node". Signed-off-by: Fiona Ebner --- tests/qemu-iotests/041 | 88 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/041.out | 4 +- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 8452845f44..3eda59db8e 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -1373,6 +1373,94 @@ class TestFilters(iotests.QMPTestCase): =20 self.complete_and_wait('mirror') =20 +class TestThrottle(iotests.QMPTestCase): + image_len =3D 3 * 1024 * 1024 + attachTargetImage =3D True + + def mirror_command(self, device): + self.vm.cmd('blockdev-mirror', + job_id=3D'mirror', + device=3Ddevice, + target=3D'target', + sync=3D'full') + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, + test_img, str(self.image_len)); + qemu_img('create', '-f', iotests.imgfmt, + target_img, str(self.image_len)); + qemu_io('-c', 'write -P 23 0 3M', test_img) + + self.vm =3D iotests.VM().add_device('virtio-scsi,id=3Dvio-scsi') + self.vm.launch() + + self.vm.cmd('object-add', { + 'id': 'thrgr0', + 'qom-type': 'throttle-group', + 'limits': { + 'bps-total': 1 * 1024 + } + }) + + if self.attachTargetImage: + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': target_img + }, + }) + + source =3D { + 'node-name': 'source', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': test_img + }, + } + + self.vm.cmd('blockdev-add', { + 'node-name': 'throttle', + 'driver': 'throttle', + 'throttle-group': 'thrgr0', + 'file': source, + }) + + self.vm.cmd('device_add', + driver=3D'scsi-hd', + id=3D'virtio', + bus=3D'vio-scsi.0', + drive=3D'throttle') + + def tearDown(self): + os.remove(test_img) + os.remove(target_img) + + def test_throttled(self): + self.mirror_command('throttle') + + time.sleep(1) + result =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/ready', False) + + self.vm.qtest(f'clock_step {4 * 1000 * 1000 * 1000 * 1000}') + self.complete_and_wait('mirror') + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, target_img), + 'target image does not match source after mirrorin= g') + +class TestThrottleDriveMirror(TestThrottle): + attachTargetImage =3D False + + def mirror_command(self, device): + self.vm.cmd('drive-mirror', + job_id=3D'mirror', + device=3Ddevice, + target=3Dtarget_img, + sync=3D'full', + mode=3D'existing') =20 if __name__ =3D=3D '__main__': iotests.main(supported_fmts=3D['qcow2', 'qed'], diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 46651953e8..96a0752f44 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -..........................................................................= ................................. +..........................................................................= ................................... ---------------------------------------------------------------------- -Ran 107 tests +Ran 109 tests =20 OK --=20 2.47.3 From nobody Sun Jul 26 13:29:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781173438072716.1803361778727; Thu, 11 Jun 2026 03:23:58 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wXcZ1-0001hF-JS; Thu, 11 Jun 2026 06:23:07 -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 1wXcYy-0001gP-W9; Thu, 11 Jun 2026 06:23:05 -0400 Received: from proxmox-new.maurer-it.com ([94.136.29.106]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wXcYw-0002RK-MK; Thu, 11 Jun 2026 06:23:04 -0400 Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B491E4314D; Thu, 11 Jun 2026 12:22:52 +0200 (CEST) From: Fiona Ebner To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, hreitz@redhat.com, kwolf@redhat.com Subject: [PATCH 2/3] blockdev: mirror: do not require a root block node Date: Thu, 11 Jun 2026 12:22:27 +0200 Message-ID: <20260611102247.622275-3-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260611102247.622275-1-f.ebner@proxmox.com> References: <20260611102247.622275-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1781173323975 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=94.136.29.106; envelope-from=f.ebner@proxmox.com; helo=proxmox-new.maurer-it.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, 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-ZM-MESSAGEID: 1781173440439158500 Content-Type: text/plain; charset="utf-8" The mirror block job currently only operates on a root block node. It might be desired to use a throttle node as the root node for limiting the guest IO, without also limiting mirror operations. There is no obvious reason why mirror should only be able to operate on a root block node. When there is no explicit @replaces argument, implicit filters on top are skipped. Allowing mirror to operate on non-root nodes does not change that fact and keeps the behavior consistent with the documentation for @replaces. Signed-off-by: Fiona Ebner --- blockdev.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/blockdev.c b/blockdev.c index 6e86c6262f..0f70973326 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2987,13 +2987,19 @@ void qmp_drive_mirror(DriveMirror *arg, Error **err= p) bool target_is_zero; int ret; =20 - bs =3D qmp_get_root_bs(arg->device, errp); + bdrv_graph_rdlock_main_loop(); + bs =3D bdrv_lookup_bs(arg->device, arg->device, errp); if (!bs) { + bdrv_graph_rdunlock_main_loop(); + return; + } + if (!bdrv_is_inserted(bs)) { + error_setg(errp, "Device has no medium"); + bdrv_graph_rdunlock_main_loop(); return; } =20 /* Early check to avoid creating target */ - bdrv_graph_rdlock_main_loop(); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { bdrv_graph_rdunlock_main_loop(); return; @@ -3150,10 +3156,18 @@ void qmp_blockdev_mirror(const char *job_id, BlockMirrorBackingMode backing_mode =3D MIRROR_LEAVE_BACKING_CHAIN; int ret; =20 - bs =3D qmp_get_root_bs(device, errp); + bdrv_graph_rdlock_main_loop(); + bs =3D bdrv_lookup_bs(device, device, errp); if (!bs) { + bdrv_graph_rdunlock_main_loop(); return; } + if (!bdrv_is_inserted(bs)) { + error_setg(errp, "Device has no medium"); + bdrv_graph_rdunlock_main_loop(); + return; + } + bdrv_graph_rdunlock_main_loop(); =20 target_bs =3D bdrv_lookup_bs(target, target, errp); if (!target_bs) { --=20 2.47.3 From nobody Sun Jul 26 13:29:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781173425974189.45322456620067; Thu, 11 Jun 2026 03:23:45 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wXcZ6-0001lo-7j; Thu, 11 Jun 2026 06:23:12 -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 1wXcYz-0001gY-Bq; Thu, 11 Jun 2026 06:23:05 -0400 Received: from proxmox-new.maurer-it.com ([94.136.29.106]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wXcYw-0002RM-Qz; Thu, 11 Jun 2026 06:23:05 -0400 Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 06EDF44ED0; Thu, 11 Jun 2026 12:22:53 +0200 (CEST) From: Fiona Ebner To: qemu-devel@nongnu.org Cc: qemu-block@nongnu.org, hreitz@redhat.com, kwolf@redhat.com Subject: [PATCH 3/3] iotests/041: add tests for mirroring below throttle node Date: Thu, 11 Jun 2026 12:22:28 +0200 Message-ID: <20260611102247.622275-4-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260611102247.622275-1-f.ebner@proxmox.com> References: <20260611102247.622275-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1781173324036 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=94.136.29.106; envelope-from=f.ebner@proxmox.com; helo=proxmox-new.maurer-it.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, 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-ZM-MESSAGEID: 1781173430157158500 Content-Type: text/plain; charset="utf-8" Test mirror with a non-root node as a source. This restriction was dropped with commit "blockdev: mirror: do not require a root block node". Signed-off-by: Fiona Ebner --- tests/qemu-iotests/041 | 40 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/041.out | 4 ++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 3eda59db8e..38a207a724 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -1434,10 +1434,19 @@ class TestThrottle(iotests.QMPTestCase): bus=3D'vio-scsi.0', drive=3D'throttle') =20 + self.drive_qdev =3D '/machine/peripheral/virtio' + def tearDown(self): os.remove(test_img) os.remove(target_img) =20 + def blockstats(self, device): + result =3D self.vm.qmp("query-blockstats") + for r in result['return']: + if r['node-name'] =3D=3D device: + return r['stats'] + raise Exception(f'Device {device} not found when querying blocksta= ts') + def test_throttled(self): self.mirror_command('throttle') =20 @@ -1451,6 +1460,37 @@ class TestThrottle(iotests.QMPTestCase): self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirrorin= g') =20 + def test_below_throttle(self): + self.mirror_command('source') + + # The throttling above should not block the mirror from completing. + self.complete_and_wait('mirror') + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, target_img), + 'target image does not match source after mirrorin= g') + + def test_below_throttle_with_io_on_throttle(self): + self.mirror_command('source') + + reqs =3D 3 + req_len =3D 1 * 1024 + + for i in range(0, reqs): + req =3D f'aio_write -P 7 {i}M {req_len}' + self.vm.hmp_qemu_io(self.drive_qdev, req, qdev=3DTrue) + + self.wait_ready('mirror') + # The throttle node above should still have in-flight requests. + assert self.blockstats('throttle')['wr_bytes'] <=3D 1 * req_len + + self.complete_and_wait('mirror', wait_ready=3DFalse) + assert self.blockstats('throttle')['wr_bytes'] =3D=3D reqs * req_l= en + self.vm.shutdown() + # The throttle node above is drained too when the mirror finishes,= so + # the images should match. + self.assertTrue(iotests.compare_images(test_img, target_img), + 'target image does not match source after mirrorin= g') + class TestThrottleDriveMirror(TestThrottle): attachTargetImage =3D False =20 diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 96a0752f44..ec055a9915 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -..........................................................................= ................................... +..........................................................................= ....................................... ---------------------------------------------------------------------- -Ran 109 tests +Ran 113 tests =20 OK --=20 2.47.3