From: Fiona Ebner <f.ebner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH qemu-server v2 9/9] partially fix #7836: allow migration for drive with read-only flag when using blockdev
Date: Wed, 11 Nov 2026 13:01:48 +0100 [thread overview]
Message-ID: <20261111120211.110737-10-f.ebner@proxmox.com> (raw)
In-Reply-To: <20261111120211.110737-1-f.ebner@proxmox.com>
A read-only block node cannot serve as a writeable block export for
the NBD mirror target. With -blockdev, the issue can be resolved by
re-opening the node below throttle as writeable, exporting it, and
then re-opening it as read-only after finishing the migration.
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---
Changes in v2:
* Improve error handling.
* Fix typo in comment.
* Terminate warning messages with newlines.
src/PVE/API2/Qemu.pm | 7 ++++--
src/PVE/CLI/qm.pm | 26 +++++++++++++++++++--
src/PVE/QemuServer.pm | 9 ++++++++
src/PVE/QemuServer/BlockJob.pm | 42 ++++++++++++++++++++++++++++++++++
4 files changed, 80 insertions(+), 4 deletions(-)
diff --git a/src/PVE/API2/Qemu.pm b/src/PVE/API2/Qemu.pm
index 7830d09b..15391ff0 100644
--- a/src/PVE/API2/Qemu.pm
+++ b/src/PVE/API2/Qemu.pm
@@ -48,7 +48,6 @@ use PVE::QemuServer::MetaInfo;
use PVE::QemuServer::Network;
use PVE::QemuServer::OVMF;
use PVE::QemuServer::PCI;
-use PVE::QemuServer::QMPHelpers;
use PVE::QemuServer::RNG;
use PVE::QemuServer::RunState;
use PVE::QemuServer::USB;
@@ -7145,7 +7144,11 @@ __PACKAGE__->register_method({
return;
},
'nbdstop' => sub {
- PVE::QemuServer::QMPHelpers::nbd_stop($state->{vmid});
+ PVE::QemuServer::BlockJob::nbd_stop_after_mirror(
+ $state->{storecfg},
+ $state->{vmid},
+ $state->{conf},
+ );
return;
},
'resume' => sub {
diff --git a/src/PVE/CLI/qm.pm b/src/PVE/CLI/qm.pm
index c4f9c64b..f3f02ef8 100755
--- a/src/PVE/CLI/qm.pm
+++ b/src/PVE/CLI/qm.pm
@@ -428,8 +428,30 @@ __PACKAGE__->register_method({
my $vmid = $param->{vmid};
- eval { PVE::QemuServer::QMPHelpers::nbd_stop($vmid) };
- warn $@ if $@;
+ my $need_nbd_stop = 1;
+
+ eval {
+ PVE::QemuConfig->lock_config(
+ $vmid,
+ sub {
+ my $storecfg = PVE::Storage::config();
+ my $conf = PVE::QemuConfig->load_config($vmid);
+ $need_nbd_stop = 0; # nbd_stop_after_mirror() handles it.
+ PVE::QemuServer::BlockJob::nbd_stop_after_mirror(
+ $storecfg, $vmid, $conf,
+ );
+ },
+ );
+ };
+ if (my $err = $@) {
+ warn $err;
+ if ($need_nbd_stop) {
+ # Fall back to just stopping the NBD server without checking disks.
+ warn "nbdstop: skipping disk check\n";
+ eval { PVE::QemuServer::QMPHelpers::nbd_stop($vmid) };
+ warn $@ if $@;
+ }
+ }
return;
},
diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index 1f7cc79e..17bf88b3 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -5819,6 +5819,10 @@ sub vm_start_nolock {
my $drivestr = $nbd->{$opt}->{drivestr};
my $volid = $nbd->{$opt}->{volid};
+ my $drive = parse_drive($opt, $drivestr);
+ die "$opt: internal error - unable to parse drive '$drivestr'\n" if !$drive;
+ my $drive_is_readonly = $drive->{ro};
+
my $top = $block_info->{$opt}->{inserted};
die "no block node found for drive '$opt'\n" if !$top;
my $block_node = $top->{'node-name'};
@@ -5830,6 +5834,11 @@ sub vm_start_nolock {
my $children = { map { $_->{child} => $_ } $top->{children}->@* };
$block_node = $children->{file}->{'node-name'}
or die "drive '$opt': throttle node without file child node name\n";
+
+ if ($drive_is_readonly) { # re-open the node below throttle as writeable
+ my $reopen_opts = { 'no-throttle' => 1, 'read-only' => 0 };
+ PVE::QemuServer::Blockdev::reopen($storecfg, $vmid, $drive, $reopen_opts);
+ }
}
mon_cmd(
diff --git a/src/PVE/QemuServer/BlockJob.pm b/src/PVE/QemuServer/BlockJob.pm
index bedcdc9d..fe87d976 100644
--- a/src/PVE/QemuServer/BlockJob.pm
+++ b/src/PVE/QemuServer/BlockJob.pm
@@ -614,4 +614,46 @@ sub mirror {
}
}
+sub nbd_stop_after_mirror {
+ my ($storecfg, $vmid, $conf) = @_;
+
+ my $exports = [];
+
+ eval {
+ my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
+ if (PVE::QemuServer::Machine::is_machine_version_at_least($machine_type, 10, 0)) {
+ $exports = mon_cmd($vmid, 'query-block-exports');
+ }
+ };
+ warn "stop nbd: unable to query block exports - $@" if $@;
+
+ PVE::QemuServer::QMPHelpers::nbd_stop($vmid);
+
+ for my $export ($exports->@*) {
+ my ($opt) = $export->{id} =~ m/^drive-(.*)$/;
+ if (!defined($opt)) {
+ warn "stop nbd: got unexpected export ID '$export->{id}'\n";
+ } elsif (!PVE::QemuServer::Drive::is_valid_drivename($opt)) {
+ warn "stop nbd: got unexpected export ID '$export->{id}' - not a drive key\n";
+ } else {
+ my $drive = PVE::QemuServer::Drive::parse_drive($opt, $conf->{$opt});
+ if (!$drive) {
+ warn "stop nbd: $opt: unable to parse drive '$conf->{$opt}'\n";
+ next;
+ }
+ if ($drive->{ro}) {
+ # The node below throttle was opened as writeable to be able to serve as a target
+ # NBD export for mirror. Re-open as read-only now. Failure is not too important,
+ # because the guest sees the drive as read-only anyways, because the top throttle
+ # node is.
+ my $reopen_opts = { 'no-throttle' => 1 };
+ eval { PVE::QemuServer::Blockdev::reopen($storecfg, $vmid, $drive, $reopen_opts); };
+ print "drive-$opt failed to re-apply read-only flag below throttle - $@\n" if $@;
+ }
+ }
+ }
+
+ return;
+}
+
1;
--
2.47.3
prev parent reply other threads:[~2026-11-11 12:03 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 12:01 [PATCH-SERIES qemu/qemu-server v2 0/9] mirror: fix regressions with blockdev and allow migration with 'ro' flag Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu v2 1/9] patches/extra: allow mirror job to use non-root block node Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 2/9] partially fix #7299: fix regression with guest IO limits applying to mirror job Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 3/9] partially fix #7299: migrate: fix regression with guest IO limits applying to NBD export Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 4/9] blockdev: attach: support explicitly attaching as writeable Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 5/9] blockdev: implement reopen function Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 6/9] partially fix #7836: allow mirror for drive with read-only flag when using blockdev Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 7/9] qm: schema: declare 'nbdstop' command as being for internal use only Fiona Ebner
2026-11-11 12:01 ` [PATCH qemu-server v2 8/9] migrate: log messages from remote nbdstop command Fiona Ebner
2026-11-11 12:01 ` Fiona Ebner [this message]
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=20261111120211.110737-10-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