Compare commits

...

6 commits

Author SHA1 Message Date
Tiago Sousa
503ae6d680 fix extend queue file path 2025-09-26 21:44:21 +01:00
Tiago Sousa
a775287ee5 blockdev: fix set write threshold arguments 2025-09-21 18:31:15 +01:00
Tiago Sousa
21768f8732 first functional version of extend lv 2025-09-20 18:49:23 +01:00
Tiago Sousa
1ef49d7295 blockdev: add query-blockstats qmp command 2025-09-13 18:54:06 +01:00
Tiago Sousa
1bcc5a6abf qmeventd: add block write threshold event handling
qemeventd: handle threshold event
2025-08-22 15:31:55 +01:00
Tiago Sousa
68c0855d8f blockdev: add set write threshold
blockdev: set write threshold
2025-08-22 15:31:12 +01:00
10 changed files with 291 additions and 1 deletions

View file

@ -5822,6 +5822,28 @@ sub vm_start_nolock {
warn $@ if $@;
}
# set write threshold for LVM thin provisioning disks
PVE::QemuConfig->foreach_volume(
$conf,
sub {
my ($ds, $drive) = @_;
return if PVE::QemuServer::drive_is_cdrom($drive, 1);
my $volid = $drive->{file};
if ( $volid ne 'none') {
my $extra_blockdev_options = {};
# $extra_blockdev_options->{'live-restore'} = $live_restore if $live_restore;
# extra protection for templates, but SATA and IDE don't support it..
$extra_blockdev_options->{'read-only'} = 1
if drive_is_read_only($conf, $drive);
my $drive_id = PVE::QemuServer::Drive::get_drive_id($drive);
PVE::QemuServer::Blockdev::set_write_threshold(
$storecfg, $vmid, $drive_id, $volid, $extra_blockdev_options,
);
}
},
);
#start nbd server for storage migration
if (my $nbd = $migrate_opts->{nbd}) {

View file

@ -17,6 +17,7 @@ use PVE::QemuServer::Drive qw(drive_is_cdrom);
use PVE::QemuServer::Helpers;
use PVE::QemuServer::Machine;
use PVE::QemuServer::Monitor qw(mon_cmd);
use PVE::SafeSyslog;
# gives ($host, $port, $export)
my $NBD_TCP_PATH_RE_3 = qr/nbd:(\S+):(\d+):exportname=(\S+)/;
@ -111,6 +112,21 @@ sub get_block_info {
return $block_info;
}
sub get_block_stats {
my ($vmid) = @_;
my $block_stats = {};
my $qmp_block_stats = mon_cmd($vmid, "query-blockstats");
for my $info ($qmp_block_stats->@*) {
my $qdev_id = $info->{qdev} or next;
my $drive_id = qdev_id_to_drive_id($qdev_id);
$block_stats->{$drive_id} = $info;
}
return $block_stats;
}
my sub get_node_name {
my ($type, $drive_id, $volid, $options) = @_;
@ -700,6 +716,22 @@ sub resize {
);
}
sub underlay_resize {
my ($storecfg, $vmid, $drive_id, $volid) = @_;
my $running = PVE::QemuServer::Helpers::vm_running_locally($vmid);
my $size = PVE::Storage::volume_underlay_resize($storecfg, $volid);
syslog("info", "got size $size KiB\n");
return if !$running;
my $block_info = get_block_info($vmid);
my $inserted = $block_info->{$drive_id}->{inserted}
or die "no block node inserted for drive '$drive_id'\n";
set_write_threshold($storecfg, $vmid, $drive_id, $volid);
}
my sub blockdev_change_medium {
my ($storecfg, $vmid, $qdev_id, $drive) = @_;
@ -820,6 +852,59 @@ sub set_io_throttle {
}
}
sub block_set_write_threshold {
my ($vmid, $nodename, $threshold) = @_;
print "set threshold $nodename $threshold\n";
PVE::QemuServer::mon_cmd(
$vmid,
"block-set-write-threshold",
'node-name' => $nodename,
'write-threshold' => int($threshold),
);
}
sub compute_write_threshold {
my ($storecfg, $volid) = @_;
my $lv_size = PVE::Storage::volume_underlay_size_info($storecfg, $volid, 5);
# FIX: change these vars to config inputs
my $chunksize = 1024 * 1024 * 1024; # 1 GiB
my $alert_chunk_percentage = 0.5; # alert when percetage of chunk used
my $write_threshold = $lv_size - $chunksize * (1 - $alert_chunk_percentage);
return $write_threshold;
}
sub set_write_threshold {
my ($storecfg, $vmid, $drive_id, $volid, $options) = @_;
my ($storeid) = PVE::Storage::parse_volume_id($volid);
my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
my $support_qemu_snapshots = PVE::Storage::volume_qemu_snapshot_method($storecfg, $volid);
# set write threshold is only supported for lvm storage using
# qcow2+external snapshots
return if $scfg->{type} ne 'lvm' || $support_qemu_snapshots ne 'mixed';
# return if drive is not set as thin
# return if !$drive->{thin};
print "setting threshold for $volid from $storeid\n";
my $snapshots = PVE::Storage::volume_snapshot_info($storecfg, $volid);
my $parentid = $snapshots->{'current'}->{parent};
# for now only set write_threshold for volumes that have snapshots
if ($parentid) {
my $nodename = get_node_name('file', $drive_id, $volid, $options);
my $write_threshold = compute_write_threshold($storecfg, $volid);
block_set_write_threshold($vmid, $nodename, $write_threshold);
}
}
sub blockdev_external_snapshot {
my ($storecfg, $vmid, $machine_version, $deviceid, $drive, $snap, $parent_snap) = @_;
@ -868,6 +953,9 @@ sub blockdev_external_snapshot {
node => $snap_fmt_blockdev->{'node-name'},
overlay => $new_fmt_blockdev->{'node-name'},
);
my $drive_id = PVE::QemuServer::Drive::get_drive_id($drive);
set_write_threshold($storecfg, $vmid, $drive_id, $volid);
}
sub blockdev_delete {

View file

@ -254,6 +254,13 @@ my %drivedesc_base = (
optional => 1,
default => 0,
},
thin => {
type => 'boolean',
description =>
'Controls whether a drive should be thin provisioned',
optional => 1,
default => 0,
},
);
my %iothread_fmt = (

8
src/qmeventd/docinfo.xml Normal file
View file

@ -0,0 +1,8 @@
<!-- DocBook document information file for Proxmox VE asciidoc docs -->
<authorgroup>
<corpauthor>Proxmox Server Solutions Gmbh
<ulink url="https://www.proxmox.com/"><citetitle>www.proxmox.com</citetitle></ulink>
</corpauthor>
</authorgroup>

View file

@ -0,0 +1,18 @@
Copyright and Disclaimer
------------------------
Copyright (C) 2007-2022 Proxmox Server Solutions GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with this program. If not, see
https://www.gnu.org/licenses/

BIN
src/qmeventd/qmeventd Executable file

Binary file not shown.

80
src/qmeventd/qmeventd.8 Normal file
View file

@ -0,0 +1,80 @@
'\" t
.\" Title: qmeventd
.\" Author: Proxmox Server Solutions Gmbh \m[blue]\fBwww.proxmox.com\fR\m[]
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
.\" Date: Fri Aug 15 05:33:35 PM WEST 2025
.\" Manual: Proxmox VE Documentation
.\" Source: \ \& 9.0.8
.\" Language: English
.\"
.TH "QMEVENTD" "8" "Fri Aug 15 05:33:35 PM WEST 2025" "\ \& 9\&.0\&.8" "Proxmox VE Documentation"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
qmeventd \- PVE QEMU Eventd Daemon
.SH "SYNOPSIS"
.sp
\fBqmeventd\fR [\-f] [\-v] PATH
.PP
\-v
.RS 4
Turn on verbose messages
.RE
.PP
\-f
.RS 4
Do not daemonize server
.RE
.PP
PATH
.RS 4
The path to listen on
.RE
.sp
This service is usually started and managed using systemd toolset\&. The service is called \fIqmeventd\fR\&.
.sp
.nf
systemctl start qmeventd
.fi
.sp
.nf
systemctl stop qmeventd
.fi
.sp
.nf
systemctl status qmeventd
.fi
.SH "DESCRIPTION"
.sp
qmeventd is a daemon that listens on PATH for incoming connections from a qemu qmp socket, and waits for SHUTDOWN events\&. When a client then disconnects, it executes /usr/sbin/qm cleanup\&. This makes it easy to clean up leftover tap devices, vgpus, etc\&.
.SH "COPYRIGHT AND DISCLAIMER"
.sp
Copyright \(co 2007\-2022 Proxmox Server Solutions GmbH
.sp
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version\&.
.sp
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. See the GNU Affero General Public License for more details\&.
.sp
You should have received a copy of the GNU Affero General Public License along with this program\&. If not, see \m[blue]\fBhttps://www\&.gnu\&.org/licenses/\fR\m[]
.SH "AUTHOR"
.PP
\fBProxmox Server Solutions Gmbh
\fR\fB\m[blue]\fBwww\&.proxmox\&.com\fR\m[]\fR\fB
\fR

View file

@ -0,0 +1,16 @@
*qmeventd* `[-f]` `[-v]` `PATH`
`-v`:: Turn on verbose messages
`-f`:: Do not daemonize server
`PATH`:: The path to listen on
This service is usually started and managed using systemd toolset. The
service is called 'qmeventd'.
systemctl start qmeventd
systemctl stop qmeventd
systemctl status qmeventd

View file

@ -0,0 +1,32 @@
ifdef::manvolnum[]
qmeventd(8)
===========
:pve-toplevel:
NAME
----
qmeventd - PVE QEMU Eventd Daemon
SYNOPSIS
--------
include::qmeventd.8-synopsis.adoc[]
DESCRIPTION
-----------
endif::manvolnum[]
ifndef::manvolnum[]
PVE QEMU Event Daemon
=====================
endif::manvolnum[]
`qmeventd` is a daemon that listens on PATH for incoming connections from
a qemu qmp socket, and waits for SHUTDOWN events. When a client then
disconnects, it executes `/usr/sbin/qm cleanup`. This makes it easy
to clean up leftover tap devices, vgpus, etc.
ifdef::manvolnum[]
include::pve-copyright.adoc[]
endif::manvolnum[]

View file

@ -43,7 +43,7 @@
#define DEFAULT_KILL_TIMEOUT 60
static int verbose = 0;
static int verbose = 1;
static int kill_timeout = DEFAULT_KILL_TIMEOUT;
static int epoll_fd = 0;
static const char *progname;
@ -209,6 +209,25 @@ void handle_qmp_event(struct Client *client, struct json_object *obj) {
// check if a backup is running and kill QEMU process if not
terminate_check(client);
} else if (!strcmp(json_object_get_string(event), "BLOCK_WRITE_THRESHOLD")) {
struct json_object *data;
struct json_object *nodename;
if (json_object_object_get_ex(obj, "data", &data) &&
json_object_object_get_ex(data, "node-name", &nodename)) {
// needs concurrency control
char extend_queue_path[] = "/etc/pve/extend-queue";
FILE *p_extend_queue = fopen(extend_queue_path, "a");
if (p_extend_queue == NULL) {
VERBOSE_PRINT(
"%s: Couldn't open extend queue file %s", client->qemu.vmid, extend_queue_path
);
} else {
const char *nodename_string = json_object_get_string(nodename);
fprintf(p_extend_queue, "%s: %s\n", client->qemu.vmid, nodename_string);
}
fclose(p_extend_queue);
}
}
}