From: Fiona Ebner <f.ebner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH qemu-server 9/9] partially fix #7836: allow migration for drive with read-only flag when using blockdev
Date: Fri, 24 Jul 2026 16:32:30 +0200 [thread overview]
Message-ID: <20260724143240.211130-10-f.ebner@proxmox.com> (raw)
In-Reply-To: <20260724143240.211130-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>
---
src/PVE/API2/Qemu.pm | 7 ++++--
src/PVE/CLI/qm.pm | 30 +++++++++++++++++++++++--
src/PVE/QemuServer.pm | 9 ++++++++
src/PVE/QemuServer/BlockJob.pm | 40 ++++++++++++++++++++++++++++++++++
4 files changed, 82 insertions(+), 4 deletions(-)
diff --git a/src/PVE/API2/Qemu.pm b/src/PVE/API2/Qemu.pm
index 28cbb9b0..7262c221 100644
--- a/src/PVE/API2/Qemu.pm
+++ b/src/PVE/API2/Qemu.pm
@@ -43,7 +43,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;
@@ -6957,7 +6956,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 9a53a4f2..af6b3170 100755
--- a/src/PVE/CLI/qm.pm
+++ b/src/PVE/CLI/qm.pm
@@ -428,8 +428,34 @@ __PACKAGE__->register_method({
my $vmid = $param->{vmid};
- eval { PVE::QemuServer::QMPHelpers::nbd_stop($vmid) };
- warn $@ if $@;
+ my $obtained_lock;
+
+ eval {
+ PVE::QemuConfig->lock_config(
+ $vmid,
+ sub {
+ $obtained_lock = 1;
+ my $storecfg = PVE::Storage::config();
+ my $conf = PVE::QemuConfig->load_config($vmid);
+ eval {
+ PVE::QemuServer::BlockJob::nbd_stop_after_mirror(
+ $storecfg, $vmid, $conf,
+ );
+ };
+ warn $@ if $@;
+ },
+ );
+ };
+ if (my $err = $@) {
+ if (!$obtained_lock) {
+ # Fall back to just stopping the NBD server without checking disks.
+ warn "nbdstop: failed to obtain lock, skipping disk check - $@\n";
+ eval { PVE::QemuServer::QMPHelpers::nbd_stop($vmid) };
+ warn $@ if $@;
+ } else {
+ die $err;
+ }
+ }
return;
},
diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index 6b03e170..7470a4a1 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -5929,6 +5929,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'};
@@ -5940,6 +5944,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 bce350da..1cf9149e 100644
--- a/src/PVE/QemuServer/BlockJob.pm
+++ b/src/PVE/QemuServer/BlockJob.pm
@@ -619,4 +619,44 @@ sub mirror {
}
}
+sub nbd_stop_after_mirror {
+ my ($storecfg, $vmid, $conf) = @_;
+
+ my $exports = [];
+
+ my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
+ if (PVE::QemuServer::Machine::is_machine_version_at_least($machine_type, 10, 0)) {
+ eval { $exports = mon_cmd($vmid, 'query-block-exports'); };
+ log_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}'");
+ } elsif (!PVE::QemuServer::Drive::is_valid_drivename($opt)) {
+ warn("stop nbd: got unexpected export ID '$export->{id}' - not a drive key");
+ } else {
+ my $drive = PVE::QemuServer::Drive::parse_drive($opt, $conf->{$opt});
+ if (!$drive) {
+ warn("stop nbd: $opt: unable to parse drive '$conf->{$opt}'");
+ 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 anways, 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-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 ` [PATCH qemu-server 2/9] partially fix #7299: fix regression with guest IO limits applying to mirror job Fiona Ebner
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 ` 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=20260724143240.211130-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