diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm index bdcc2e54..34fc46ee 100644 --- a/PVE/QemuMigrate.pm +++ b/PVE/QemuMigrate.pm @@ -1139,6 +1139,14 @@ sub phase2 { $self->log('info', "$drive: start migration to $nbd_uri"); PVE::QemuServer::qemu_drive_mirror($vmid, $drive, $nbd_uri, $vmid, undef, $self->{storage_migration_jobs}, 'skip', undef, $bwlimit, $bitmap); } + + if (PVE::QemuServer::Machine::runs_at_least_qemu_version($vmid, 8, 2)) { + $self->log('info', "switching mirror jobs to actively synced mode"); + PVE::QemuServer::qemu_drive_mirror_switch_to_active_mode( + $vmid, + $self->{storage_migration_jobs}, + ); + } } $self->log('info', "starting online/live migration on $migrate_uri"); diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 117d0b5e..b26da505 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -8186,6 +8186,57 @@ sub qemu_blockjobs_cancel { } } +# Callers should version guard this (only available with a binary >= QEMU 8.2) +sub qemu_drive_mirror_switch_to_active_mode { + my ($vmid, $jobs) = @_; + + my $switching = {}; + + for my $job (sort keys $jobs->%*) { + print "$job: switching to actively synced mode\n"; + + eval { + mon_cmd( + $vmid, + "block-job-change", + id => $job, + type => 'mirror', + 'copy-mode' => 'write-blocking', + ); + $switching->{$job} = 1; + }; + die "could not switch mirror job $job to active mode - $@\n" if $@; + } + + while (1) { + my $stats = mon_cmd($vmid, "query-block-jobs"); + + my $running_jobs = {}; + $running_jobs->{$_->{device}} = $_ for $stats->@*; + + for my $job (sort keys $switching->%*) { + die "$job: vanished while switching to active mode\n" if !$running_jobs->{$job}; + + my $info = $running_jobs->{$job}; + if ($info->{status} eq 'concluded') { + qemu_handle_concluded_blockjob($vmid, $job, $info); + # The 'concluded' state should occur here if and only if the job failed, so the + # 'die' below should be unreachable, but play it safe. + die "$job: expected job to have failed, but no error was set\n"; + } + + if ($info->{'actively-synced'}) { + print "$job: successfully switched to actively synced mode\n"; + delete $switching->{$job}; + } + } + + last if scalar(keys $switching->%*) == 0; + + sleep 1; + } +} + # Check for bug #4525: drive-mirror will open the target drive with the same aio setting as the # source, but some storages have problems with io_uring, sometimes even leading to crashes. my sub clone_disk_check_io_uring { diff --git a/test/MigrationTest/QemuMigrateMock.pm b/test/MigrationTest/QemuMigrateMock.pm index 1efabe24..f5b44424 100644 --- a/test/MigrationTest/QemuMigrateMock.pm +++ b/test/MigrationTest/QemuMigrateMock.pm @@ -152,6 +152,9 @@ $MigrationTest::Shared::qemu_server_module->mock( } return; }, + qemu_drive_mirror_switch_to_active_mode => sub { + return; + }, set_migration_caps => sub { return; }, @@ -185,6 +188,9 @@ $qemu_server_machine_module->mock( if !defined($vm_status->{runningmachine}); return $vm_status->{runningmachine}; }, + runs_at_least_qemu_version => sub { + return 1; + }, ); my $ssh_info_module = Test::MockModule->new("PVE::SSHInfo");