public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Fiona Ebner <f.ebner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH qemu-server 2/9] partially fix #7299: fix regression with guest IO limits applying to mirror job
Date: Fri, 24 Jul 2026 16:32:23 +0200	[thread overview]
Message-ID: <20260724143240.211130-3-f.ebner@proxmox.com> (raw)
In-Reply-To: <20260724143240.211130-1-f.ebner@proxmox.com>

Since the switch to -blockdev was made in Proxmox VE 9, the root block
node is now always a throttle node for optionally limiting the guest
IO. Mirror jobs are accidentally affected by a set limit too, because
the throttle root block node was passed as the source of the mirror
job. Since pve-qemu-kvm 11.0.3, non-root nodes can be used for the
mirror job too. Use the node node below throttle as the mirror source.

During mirror completion, the throttle node on top is also drained by
QEMU (which temporarily ignores the throttling).

For migration, the dirty bitmap also needs to be created on the node
below throttle.

For the tests, it is necessary to mock the query-block QMP command for
the blockdev module.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---

The QEMU binary version in the runs_at_least_qemu_version() check
needs to be adapted when applying, to the pve-qemu version which
actually does allow non-root nodes as a mirror source.

 src/PVE/QemuMigrate.pm             |  8 ++++--
 src/PVE/QemuServer/BlockJob.pm     | 42 +++++++++++++++++++++++++++---
 src/test/MigrationTest/Shared.pm   | 35 +++++++++++++++++++++++++
 src/test/run_qemu_migrate_tests.pl |  8 +++---
 4 files changed, 83 insertions(+), 10 deletions(-)

diff --git a/src/PVE/QemuMigrate.pm b/src/PVE/QemuMigrate.pm
index 8da6f15d..ab930955 100644
--- a/src/PVE/QemuMigrate.pm
+++ b/src/PVE/QemuMigrate.pm
@@ -626,7 +626,9 @@ sub handle_replication {
 
             # start tracking before replication to get full delta + a few duplicates
             $self->log('info', "$drive: start tracking writes using block-dirty-bitmap '$bitmap'");
-            mon_cmd($vmid, 'block-dirty-bitmap-add', node => "drive-$drive", name => $bitmap);
+            my $block_node_name =
+                PVE::QemuServer::BlockJob::get_mirror_source_node($vmid, "drive-$drive");
+            mon_cmd($vmid, 'block-dirty-bitmap-add', node => "$block_node_name", name => $bitmap);
 
             # other info comes from target node in phase 2
             $self->{target_drive}->{$drive}->{bitmap} = $bitmap;
@@ -817,9 +819,11 @@ sub cleanup_bitmaps {
         my $bitmap = $self->{target_drive}->{$drive}->{bitmap};
         next if !$bitmap;
         $self->log('info', "$drive: removing block-dirty-bitmap '$bitmap'");
+        my $block_node_name =
+            PVE::QemuServer::BlockJob::get_mirror_source_node($self->{vmid}, "drive-$drive");
         mon_cmd(
             $self->{vmid}, 'block-dirty-bitmap-remove',
-            node => "drive-$drive",
+            node => "$block_node_name",
             name => $bitmap,
         );
     }
diff --git a/src/PVE/QemuServer/BlockJob.pm b/src/PVE/QemuServer/BlockJob.pm
index 921f046c..7be5a1e4 100644
--- a/src/PVE/QemuServer/BlockJob.pm
+++ b/src/PVE/QemuServer/BlockJob.pm
@@ -13,7 +13,9 @@ use PVE::Storage;
 use PVE::QemuServer::Agent qw(qga_check_running);
 use PVE::QemuServer::Blockdev;
 use PVE::QemuServer::Drive qw(checked_volume_format);
+use PVE::QemuServer::Machine;
 use PVE::QemuServer::Monitor qw(mon_cmd qmp_cmd vm_qmp_peer);
+use PVE::QemuServer::QMPHelpers;
 use PVE::QemuServer::RunState;
 
 # If the job was started with auto-dismiss=false, it's necessary to dismiss it manually. Using this
@@ -242,12 +244,44 @@ sub monitor {
     }
 }
 
+=pod
+
+=head3 get_mirror_source_node
+
+    my $node_name = get_mirror_source_node($vmid, $device_id);
+
+Return the node for the drive device C<$device_id> that should serve as the mirror source.
+
+=cut
+
+sub get_mirror_source_node {
+    my ($vmid, $device_id) = @_;
+
+    # Before QEMU binary 11.0.3, blockdev-mirror requires a root node.
+    if (!PVE::QemuServer::QMPHelpers::runs_at_least_qemu_version($vmid, 11, 0, 3)) {
+        return $device_id;
+    }
+
+    # The switch to -blockdev happened with machine version 10.0.
+    my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
+    if (!PVE::QemuServer::Machine::is_machine_version_at_least($machine_type, 10, 0)) {
+        return $device_id;
+    }
+
+    # Avoid throttle limits intended for the guest applying to the mirror job (bug #7299).
+    return PVE::QemuServer::Blockdev::get_node_name_below_throttle(
+        vm_qmp_peer($vmid), $device_id, 1,
+    );
+}
+
 my sub common_mirror_qmp_options {
-    my ($device_id, $qemu_target, $src_bitmap, $bwlimit) = @_;
+    my ($vmid, $device_id, $qemu_target, $src_bitmap, $bwlimit) = @_;
+
+    my $source_node_name = get_mirror_source_node($vmid, $device_id);
 
     my $opts = {
         timeout => 10,
-        device => "$device_id",
+        device => "$source_node_name",
         sync => "full",
         target => $qemu_target,
         'auto-dismiss' => JSON::false,
@@ -304,7 +338,7 @@ sub qemu_drive_mirror {
         $qemu_target = $is_zero_initialized ? "zeroinit:$dst_path" : $dst_path;
     }
 
-    my $opts = common_mirror_qmp_options($device_id, $qemu_target, $src_bitmap, $bwlimit);
+    my $opts = common_mirror_qmp_options($vmid, $device_id, $qemu_target, $src_bitmap, $bwlimit);
     $opts->{mode} = "existing";
     $opts->{format} = $format if $format;
 
@@ -498,7 +532,7 @@ sub blockdev_mirror {
     };
 
     my $qmp_opts = common_mirror_qmp_options(
-        $device_id, $target_node_name, $source->{bitmap}, $options->{bwlimit},
+        $vmid, $device_id, $target_node_name, $source->{bitmap}, $options->{bwlimit},
     );
 
     $qmp_opts->{'job-id'} = "$jobid";
diff --git a/src/test/MigrationTest/Shared.pm b/src/test/MigrationTest/Shared.pm
index a51e1692..c69a141a 100644
--- a/src/test/MigrationTest/Shared.pm
+++ b/src/test/MigrationTest/Shared.pm
@@ -159,6 +159,41 @@ $qemu_server_module->mock(
     },
 );
 
+our $qemu_server_blockdev_module = Test::MockModule->new("PVE::QemuServer::Blockdev");
+$qemu_server_blockdev_module->mock(
+    mon_cmd => sub {
+        my ($vmid, $command, %params) = @_;
+
+        if ($command eq 'query-block') {
+            my $vm_config = PVE::QemuConfig->load_config($vmid);
+            my $res = [];
+            PVE::QemuConfig->foreach_volume(
+                $vm_config,
+                sub {
+                    my ($ds, $drive) = @_;
+                    my $entry = {
+                        qdev => "$ds",
+                        inserted => {
+                            drv => 'throttle',
+                            'node-name' => "drive-$ds",
+                            children => [
+                                {
+                                    'node-name' => "f-$ds",
+                                    child => 'file',
+                                },
+                            ],
+                        },
+                    };
+                    push($res->@*, $entry);
+                },
+            );
+            return $res;
+        } else {
+            die "mon_cmd (mocked) - implement me: $command";
+        }
+    },
+);
+
 our $qemu_server_ovmf_module = Test::MockModule->new("PVE::QemuServer::OVMF");
 $qemu_server_ovmf_module->mock(
     get_efivars_size => sub {
diff --git a/src/test/run_qemu_migrate_tests.pl b/src/test/run_qemu_migrate_tests.pl
index 05eed1d9..9d6c495e 100755
--- a/src/test/run_qemu_migrate_tests.pl
+++ b/src/test/run_qemu_migrate_tests.pl
@@ -1367,8 +1367,8 @@ my $tests = [
         target_volids => local_volids_for_vm(105),
         expected_calls => {
             %{$replicated_expected_calls_online},
-            'block-dirty-bitmap-add-drive-scsi0' => 1,
-            'block-dirty-bitmap-add-drive-ide0' => 1,
+            'block-dirty-bitmap-add-f-scsi0' => 1,
+            'block-dirty-bitmap-add-f-ide0' => 1,
         },
         expected => {
             source_volids => local_volids_for_vm(105),
@@ -1416,8 +1416,8 @@ my $tests = [
         target_volids => local_volids_for_vm(105),
         expected_calls => {
             %{$replicated_expected_calls_online},
-            'block-dirty-bitmap-add-drive-scsi0' => 1,
-            'block-dirty-bitmap-add-drive-ide0' => 1,
+            'block-dirty-bitmap-add-f-scsi0' => 1,
+            'block-dirty-bitmap-add-f-ide0' => 1,
         },
         expected => {
             source_volids => local_volids_for_vm(105),
-- 
2.47.3





  parent reply	other threads:[~2026-07-24 14:33 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-24 14:32 [PATCH-SERIES qemu/qemu-server 0/9] mirror: fix regressions with blockdev and allow migration with 'ro' flag Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu 1/9] add patch to allow mirror job to use non-root block node Fiona Ebner
2026-07-24 14:32 ` Fiona Ebner [this message]
2026-07-24 14:32 ` [PATCH qemu-server 3/9] partially fix #7299: migrate: fix regression with guest IO limits applying to NBD export Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu-server 4/9] blockdev: attach: support explicitly attaching as writeable Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu-server 5/9] blockdev: implement reopen function Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu-server 6/9] partially fix #7836: allow mirror for drive with read-only flag when using blockdev Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu-server 7/9] qm: schema: declare 'nbdstop' command as being for internal use only Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu-server 8/9] migrate: log messages from remote nbdstop command Fiona Ebner
2026-07-24 14:32 ` [PATCH qemu-server 9/9] partially fix #7836: allow migration for drive with read-only flag when using blockdev Fiona Ebner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260724143240.211130-3-f.ebner@proxmox.com \
    --to=f.ebner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal