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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.