From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 30F781FF0E2 for ; Thu, 30 Jul 2026 15:18:11 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 0450521542; Thu, 30 Jul 2026 15:17:53 +0200 (CEST) From: Fiona Ebner To: pve-devel@lists.proxmox.com Subject: [PATCH qemu-server 3/8] partially fix #7854: block job: monitor: handle completion faster to benefit migration Date: Thu, 30 Jul 2026 15:15:52 +0200 Message-ID: <20260730131613.157722-4-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260730131613.157722-1-f.ebner@proxmox.com> References: <20260730131613.157722-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1785417458168 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.160 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_LOW -0.7 Sender listed at https://www.dnswl.org/, low trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: TGSAXFCNIX6L37KL547R4DPHXXEYUZ6T X-Message-ID-Hash: TGSAXFCNIX6L37KL547R4DPHXXEYUZ6T X-MailFrom: f.ebner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Currently, the block job monitor always sleeps a full second between iterations. This causes a longer guest downtime for migration that does offline storage migration. Optimize this, by skipping sleep directly after issuing the completion commands and by sleeping only for 0.1 seconds when all jobs are ready. Except when a QMP completion command fails with 'cannot be completed', sleep for a full second again. >>From some quick testing with a VM with four disks, the time window between completing state migration and issuing resume on the target side shrank from ~2.5 to 1.6 seconds. A further optimization will be to avoid spawning qm via SSH for the nbdstop command, and instead re-using the tunnel. This requires some kind of capability check though, to know if the target side already supports it. Signed-off-by: Fiona Ebner --- src/PVE/QemuServer/BlockJob.pm | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/PVE/QemuServer/BlockJob.pm b/src/PVE/QemuServer/BlockJob.pm index 0a51a65b..3c8de3e4 100644 --- a/src/PVE/QemuServer/BlockJob.pm +++ b/src/PVE/QemuServer/BlockJob.pm @@ -5,6 +5,7 @@ use warnings; use JSON; use Storable qw(dclone); +use Time::HiRes qw(usleep); use PVE::Format qw(render_duration render_bytes); use PVE::RESTEnvironment qw(log_warn); @@ -110,6 +111,7 @@ sub monitor { } my $readycounter = 0; + my $wait_time = 1_000_000; # microseconds for my $job_id (sort keys %$jobs) { my $job = $running_jobs->{$job_id}; @@ -161,6 +163,8 @@ sub monitor { last if scalar(keys %$jobs) == 0; if ($readycounter == scalar(keys %$jobs)) { + $wait_time = 100_000; # everything is ready, query more often + if (!$ready_message_printed) { print "all '$op' jobs are ready\n"; $ready_message_printed = 1; @@ -202,6 +206,8 @@ sub monitor { for my $job_id (sort keys %$jobs) { next if $jobs->{$job_id}->{complete}; + my $got_completion_error; + # try to switch the disk if source and destination are on the same guest print "$job_id: Completing block job...\n"; @@ -225,6 +231,9 @@ sub monitor { if ($err && $err =~ m/cannot be completed/) { print "$job_id: block job cannot be completed, trying again.\n"; $err_complete++; + + $wait_time = 1_000_000; # give it some time + $got_completion_error = 1; } elsif ($err) { die "$job_id: block job cannot be completed - $err\n"; } else { @@ -233,11 +242,14 @@ sub monitor { print "$job_id: Completed successfully.\n"; $jobs->{$job_id}->{complete} = 1; + + # query right away, except if there was a completion error + $wait_time = 0 if !$got_completion_error; } } } } - sleep 1; + usleep($wait_time) if $wait_time; } }; my $err = $@; -- 2.47.3