]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer/Memory.pm
vma restore: bump timeout for reading header
[qemu-server.git] / PVE / QemuServer / Memory.pm
index d459a08dd81059f375df10a02b12b087f46e053e..f365f2d13a852e46e5e2a786a252f09e991b04b0 100644 (file)
@@ -3,15 +3,94 @@ package PVE::QemuServer::Memory;
 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);
@@ -63,12 +142,19 @@ my sub get_max_mem {
     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;
@@ -88,7 +174,7 @@ sub get_numa_guest_to_host_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;
@@ -127,16 +213,19 @@ sub foreach_dimm{
 }
 
 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);
 
@@ -151,7 +240,7 @@ sub qemu_memory_hotplug {
        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;
@@ -180,17 +269,18 @@ sub qemu_memory_hotplug {
                }
 
                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);
        });
 
@@ -209,7 +299,7 @@ sub qemu_memory_hotplug {
 
            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};
@@ -218,9 +308,10 @@ sub qemu_memory_hotplug {
            }
            $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);
        }
     }
@@ -245,9 +336,9 @@ sub qemu_memdevices_list {
 }
 
 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) {
@@ -281,7 +372,7 @@ sub config {
        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};
@@ -468,8 +559,7 @@ sub hugepages_topology {
 
     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;
@@ -484,7 +574,7 @@ sub hugepages_topology {
     #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;