use strict;
use warnings;
+use PVE::JSONSchema qw(parse_property_string);
use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach);
use PVE::Exception qw(raise raise_param_exc);
-use PVE::QemuServer;
+use PVE::QemuServer::Helpers qw(parse_number_sets);
use PVE::QemuServer::Monitor qw(mon_cmd);
+use PVE::QemuServer::QMPHelpers qw(qemu_devicedel qemu_objectdel);
+
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(
+get_current_memory
+);
+
+our $MAX_NUMA = 8;
+
+my $numa_fmt = {
+ cpus => {
+ type => "string",
+ pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
+ description => "CPUs accessing this NUMA node.",
+ format_description => "id[-id];...",
+ },
+ memory => {
+ type => "number",
+ description => "Amount of memory this NUMA node provides.",
+ optional => 1,
+ },
+ hostnodes => {
+ type => "string",
+ pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
+ description => "Host NUMA nodes to use.",
+ format_description => "id[-id];...",
+ optional => 1,
+ },
+ policy => {
+ type => 'string',
+ enum => [qw(preferred bind interleave)],
+ description => "NUMA allocation policy.",
+ optional => 1,
+ },
+};
+PVE::JSONSchema::register_format('pve-qm-numanode', $numa_fmt);
+our $numadesc = {
+ optional => 1,
+ type => 'string', format => $numa_fmt,
+ description => "NUMA topology.",
+};
+PVE::JSONSchema::register_standard_option("pve-qm-numanode", $numadesc);
+
+sub parse_numa {
+ my ($data) = @_;
+
+ my $res = parse_property_string($numa_fmt, $data);
+ $res->{cpus} = parse_number_sets($res->{cpus}) if defined($res->{cpus});
+ $res->{hostnodes} = parse_number_sets($res->{hostnodes}) if defined($res->{hostnodes});
+ return $res;
+}
-my $MAX_NUMA = 8;
my $STATICMEM = 1024;
+our $memory_fmt = {
+ current => {
+ description => "Current amount of online RAM for the VM in MiB. This is the maximum available memory when"
+ ." you use the balloon device.",
+ type => 'integer',
+ default_key => 1,
+ minimum => 16,
+ default => 512,
+ },
+};
+
+sub print_memory {
+ my $memory = shift;
+
+ return PVE::JSONSchema::print_property_string($memory, $memory_fmt);
+}
+
+sub parse_memory {
+ my ($value) = @_;
+
+ return { current => $memory_fmt->{current}->{default} } if !defined($value);
+
+ my $res = PVE::JSONSchema::parse_property_string($memory_fmt, $value);
+
+ return $res;
+}
+
my $_host_bits;
sub get_host_phys_address_bits {
return $_host_bits if defined($_host_bits);
return $bits_to_max_mem > 4*1024*1024 ? 4*1024*1024 : $bits_to_max_mem;
}
+sub get_current_memory {
+ my ($value) = @_;
+
+ my $memory = parse_memory($value);
+ return $memory->{current};
+}
+
sub get_numa_node_list {
my ($conf) = @_;
my @numa_map;
for (my $i = 0; $i < $MAX_NUMA; $i++) {
my $entry = $conf->{"numa$i"} or next;
- my $numa = PVE::QemuServer::parse_numa($entry) or next;
+ my $numa = parse_numa($entry) or next;
push @numa_map, $i;
}
return @numa_map if @numa_map;
my $map = {};
for (my $i = 0; $i < $MAX_NUMA; $i++) {
my $entry = $conf->{"numa$i"} or next;
- my $numa = PVE::QemuServer::parse_numa($entry) or next;
+ my $numa = parse_numa($entry) or next;
$map->{$i} = print_numa_hostnodes($numa->{hostnodes});
}
return $map if %$map;
}
sub qemu_memory_hotplug {
- my ($vmid, $conf, $defaults, $value) = @_;
+ my ($vmid, $conf, $value) = @_;
- return $value if !PVE::QemuServer::check_running($vmid);
+ return $value if !PVE::QemuServer::Helpers::vm_running_locally($vmid);
- my $sockets = $conf->{sockets} || 1;
+ my $oldmem = parse_memory($conf->{memory});
+ my $newmem = parse_memory($value);
- my $memory = $conf->{memory} || $defaults->{memory};
- $value = $defaults->{memory} if !$value;
- return $value if $value == $memory;
+ return $value if $newmem->{current} == $oldmem->{current};
+ my $memory = $oldmem->{current};
+ $value = $newmem->{current};
+
+ my $sockets = $conf->{sockets} || 1;
my $static_memory = $STATICMEM;
$static_memory = $static_memory * $sockets if ($conf->{hugepages} && $conf->{hugepages} == 1024);
foreach_dimm($conf, $vmid, $value, $static_memory, sub {
my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
- return if $current_size <= $conf->{memory};
+ return if $current_size <= get_current_memory($conf->{memory});
if ($conf->{hugepages}) {
$numa_hostmap = get_numa_guest_to_host_map($conf) if !$numa_hostmap;
}
if (my $err = $@) {
- eval { PVE::QemuServer::qemu_objectdel($vmid, "mem-$name"); };
+ eval { qemu_objectdel($vmid, "mem-$name"); };
die $err;
}
eval { mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) };
if (my $err = $@) {
- eval { PVE::QemuServer::qemu_objectdel($vmid, "mem-$name"); };
+ eval { qemu_objectdel($vmid, "mem-$name"); };
die $err;
}
#update conf after each succesful module hotplug
- $conf->{memory} = $current_size;
+ $newmem->{current} = $current_size;
+ $conf->{memory} = print_memory($newmem);
PVE::QemuConfig->write_config($vmid, $conf);
});
my $retry = 0;
while (1) {
- eval { PVE::QemuServer::qemu_devicedel($vmid, $name) };
+ eval { qemu_devicedel($vmid, $name) };
sleep 3;
my $dimm_list = qemu_memdevices_list($vmid, 'dimm');
last if !$dimm_list->{$name};
}
$current_size -= $dimm_size;
#update conf after each succesful module unplug
- $conf->{memory} = $current_size;
+ $newmem->{current} = $current_size;
+ $conf->{memory} = print_memory($newmem);
- eval { PVE::QemuServer::qemu_objectdel($vmid, "mem-$name"); };
+ eval { qemu_objectdel($vmid, "mem-$name"); };
PVE::QemuConfig->write_config($vmid, $conf);
}
}
}
sub config {
- my ($conf, $vmid, $sockets, $cores, $defaults, $hotplug, $cmd) = @_;
+ my ($conf, $vmid, $sockets, $cores, $hotplug, $cmd) = @_;
- my $memory = $conf->{memory} || $defaults->{memory};
+ my $memory = get_current_memory($conf->{memory});
my $static_memory = 0;
if ($hotplug) {
my $numa_totalmemory = undef;
for (my $i = 0; $i < $MAX_NUMA; $i++) {
next if !$conf->{"numa$i"};
- my $numa = PVE::QemuServer::parse_numa($conf->{"numa$i"});
+ my $numa = parse_numa($conf->{"numa$i"});
next if !$numa;
# memory
die "missing NUMA node$i memory value\n" if !$numa->{memory};
return if !$conf->{numa};
- my $defaults = PVE::QemuServer::load_defaults();
- my $memory = $conf->{memory} || $defaults->{memory};
+ my $memory = get_current_memory($conf->{memory});
my $static_memory = 0;
my $sockets = $conf->{sockets} || 1;
my $numa_custom_topology = undef;
#custom numa topology
for (my $i = 0; $i < $MAX_NUMA; $i++) {
next if !$conf->{"numa$i"};
- my $numa = PVE::QemuServer::parse_numa($conf->{"numa$i"});
+ my $numa = parse_numa($conf->{"numa$i"});
next if !$numa;
$numa_custom_topology = 1;