]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer/Memory.pm
Disable memory hotplugging for custom NUMA topologies
[qemu-server.git] / PVE / QemuServer / Memory.pm
index 0e4c83086fa70dc6146a4b0a408ac8a99fed69ac..ae9598b34ffc2391f43dbe07d29ff4c3d07829a5 100644 (file)
@@ -2,10 +2,13 @@ package PVE::QemuServer::Memory;
 
 use strict;
 use warnings;
-use PVE::QemuServer;
+
 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::Monitor qw(mon_cmd);
+
 my $MAX_NUMA = 8;
 my $MAX_MEM = 4194304;
 my $STATICMEM = 1024;
@@ -34,7 +37,7 @@ sub get_numa_guest_to_host_map {
     }
     return $map if %$map;
     my $sockets = $conf->{sockets} || 1;
-    return map { $_ => $_ } (0..($sockets-1));
+    return {map { $_ => $_ } (0..($sockets-1))};
 }
 
 sub foreach_dimm{
@@ -139,7 +142,7 @@ sub qemu_memory_hotplug {
                        my $hugepages_host_topology = hugepages_host_topology();
                        hugepages_allocate($hugepages_topology, $hugepages_host_topology);
 
-                       eval { PVE::QemuServer::vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-file", id => "mem-$name", props => {
+                       eval { mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-file", id => "mem-$name", props => {
                                             size => int($dimm_size*1024*1024), 'mem-path' => $path, share => JSON::true, prealloc => JSON::true } ); };
                        if (my $err = $@) {
                            hugepages_reset($hugepages_host_topology);
@@ -151,7 +154,7 @@ sub qemu_memory_hotplug {
                    eval { hugepages_update_locked($code); };
 
                } else {
-                   eval { PVE::QemuServer::vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) };
+                   eval { mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) };
                }
 
                if (my $err = $@) {
@@ -159,7 +162,7 @@ sub qemu_memory_hotplug {
                    die $err;
                }
 
-               eval { PVE::QemuServer::vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) };
+               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"); };
                    die $err;
@@ -199,7 +202,7 @@ sub qemu_memory_hotplug {
 sub qemu_dimm_list {
     my ($vmid) = @_;
 
-    my $dimmarray = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "query-memory-devices");
+    my $dimmarray = mon_cmd($vmid, "query-memory-devices");
     my $dimms = {};
 
     foreach my $dimm (@$dimmarray) {
@@ -215,13 +218,19 @@ sub qemu_dimm_list {
 
 sub config {
     my ($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd) = @_;
-    
+
     my $memory = $conf->{memory} || $defaults->{memory};
     my $static_memory = 0;
 
     if ($hotplug_features->{memory}) {
        die "NUMA needs to be enabled for memory hotplug\n" if !$conf->{numa};
        die "Total memory is bigger than ${MAX_MEM}MB\n" if $memory > $MAX_MEM;
+
+       for (my $i = 0; $i < $MAX_NUMA; $i++) {
+           die "cannot enable memory hotplugging with custom NUMA topology\n"
+               if $conf->{"numa$i"};
+       }
+
        my $sockets = 1;
        $sockets = $conf->{sockets} if $conf->{sockets};
 
@@ -358,7 +367,7 @@ sub hugepages_mount {
    my $mountdata = PVE::ProcFSTools::parse_proc_mounts();
 
    foreach my $size (qw(2048 1048576)) {
-       return if (! -d "/sys/kernel/mm/hugepages/hugepages-${size}kB");
+       next if (! -d "/sys/kernel/mm/hugepages/hugepages-${size}kB");
 
        my $path = "/run/hugepages/kvm/${size}kB";
 
@@ -391,30 +400,36 @@ sub hugepages_nr {
 }
 
 sub hugepages_size {
-   my ($conf, $size) = @_;
+    my ($conf, $size) = @_;
+    die "hugepages option is not enabled" if !$conf->{hugepages};
+    die "memory size '$size' is not a positive even integer; cannot use for hugepages\n"
+       if $size <= 0 || $size & 1;
 
-   die "hugepages option is not enabled" if !$conf->{hugepages};
+    my $page_chunk = sub { -d  "/sys/kernel/mm/hugepages/hugepages-". ($_[0] * 1024) ."kB" };
+    die "your system doesn't support hugepages\n" if !$page_chunk->(2) && !$page_chunk->(1024);
 
-   if ($conf->{hugepages} eq 'any') {
+    if ($conf->{hugepages} eq 'any') {
 
-       #try to use 1GB if available && memory size is matching
-       if (-d "/sys/kernel/mm/hugepages/hugepages-1048576kB" && ($size % 1024 == 0)) {
+       # try to use 1GB if available && memory size is matching
+       if ($page_chunk->(1024) && ($size & 1023) == 0) {
            return 1024;
-       } else {
+       } elsif ($page_chunk->(2)) {
            return 2;
+       } else {
+           die "host only supports 1024 GB hugepages, but requested size '$size' is not a multiple of 1024 MB\n"
        }
+    } else {
 
-   } else {
-
-       my $hugepagesize = $conf->{hugepages} * 1024 . "kB";
+       my $hugepagesize = $conf->{hugepages};
 
-       if (! -d "/sys/kernel/mm/hugepages/hugepages-$hugepagesize") {
-               die "your system doesn't support hugepages of $hugepagesize";
+       if (!$page_chunk->($hugepagesize)) {
+           die "your system doesn't support hugepages of $hugepagesize MB\n";
+       } elsif (($size % $hugepagesize) != 0) {
+           die "Memory size $size is not a multiple of the requested hugepages size $hugepagesize\n";
        }
-       die "Memory size $size is not a multiple of the requested hugepages size $hugepagesize" if ($size % $conf->{hugepages}) != 0;
-       return $conf->{hugepages};
-   }
 
+       return $hugepagesize
+    }
 }
 
 sub hugepages_topology {
@@ -538,6 +553,29 @@ sub hugepages_allocate {
 
 }
 
+sub hugepages_default_nr_hugepages {
+    my ($size) = @_;
+
+    my $cmdline = PVE::Tools::file_read_firstline("/proc/cmdline");
+    my $args = PVE::Tools::split_args($cmdline);
+
+    my $parsed_size = 2; # default is 2M
+
+    foreach my $arg (@$args) {
+       if ($arg eq "hugepagesz=2M") {
+           $parsed_size = 2;
+       } elsif ($arg eq "hugepagesz=1G") {
+           $parsed_size = 1024;
+       } elsif ($arg =~ m/^hugepages=(\d+)?$/) {
+           if ($parsed_size == $size) {
+               return $1;
+           }
+       }
+    }
+
+    return 0;
+}
+
 sub hugepages_pre_deallocate {
     my ($hugepages_topology) = @_;
 
@@ -545,8 +583,8 @@ sub hugepages_pre_deallocate {
 
        my $hugepages_size = $size * 1024;
        my $path = "/sys/kernel/mm/hugepages/hugepages-${hugepages_size}kB/";
-       my $hugepages_nr = PVE::Tools::file_read_firstline($path."nr_hugepages");
-       PVE::ProcFSTools::write_proc_entry($path."nr_hugepages", 0);
+       my $hugepages_nr = hugepages_default_nr_hugepages($size);
+       PVE::ProcFSTools::write_proc_entry($path."nr_hugepages", $hugepages_nr);
     }
 }