[pve-devel] [PATCH] memory hotplug patch v3

Alexandre Derumier aderumier at odiso.com
Thu Jul 17 10:02:17 CEST 2014


From: Stefan Priebe <s.priebe at profihost.ag>

This patch allow to hotplug memory, by step of minimum 128M dimm, up to 255 dimm
(tested under debian and windows).

vmid.conf
---------
memory: 1024
maxmemory: 8192

test
----
qm set <vmid> -memory 2048

linux guest
-----------
-acpi hotplug module should be loaded in guest
-need a recent kernel. (tested with 3.10)

can be enable automaticly, adding:

/lib/udev/rules.d/80-hotplug-cpu-mem.rules
SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", \
 ATTR{online}="1"

SUBSYSTEM=="memory", ACTION=="add", TEST=="state", ATTR{state}=="offline", \
 ATTR{state}="online"

windows guest
-------------
works out of the box:

- windows 2012 standard
- windows 2008 enterprise/datacenter

Signed-off-by: Stefan Priebe <s.priebe at profihost.ag>
Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 PVE/API2/Qemu.pm  |    3 ++
 PVE/QemuServer.pm |  105 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index d03e28a..60fe01a 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -1008,6 +1008,9 @@ my $update_vm_api  = sub {
 		    if($opt eq 'cores' && $conf->{maxcpus}){
 			PVE::QemuServer::qemu_cpu_hotplug($vmid, $conf, $param->{$opt});
 		    }
+		    elsif ($opt eq 'memory' && $running){
+			PVE::QemuServer::qemu_memory_hotplug($vmid, $conf, $param->{$opt});
+		    }
 
 		    $conf->{$opt} = $param->{$opt};
 		    PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index abed0c1..953f8d2 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -307,6 +307,12 @@ EODESC
 	minimum => 1,
 	default => 1,
     },
+    maxmemory => {
+	optional => 1,
+	type => 'integer',
+	description => "Maximum memory for hotplug.",
+	minimum => 1,
+    },
     acpi => {
 	optional => 1,
 	type => 'boolean',
@@ -477,6 +483,7 @@ my $MAX_UNUSED_DISKS = 8;
 my $MAX_HOSTPCI_DEVICES = 2;
 my $MAX_SERIAL_PORTS = 4;
 my $MAX_PARALLEL_PORTS = 3;
+my $MAX_DIMMS = 255;
 
 my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000',  'pcnet',  'virtio',
 		      'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3'];
@@ -514,6 +521,20 @@ for (my $i = 0; $i < $MAX_NETS; $i++)  {
     $confdesc->{"net$i"} = $netdesc;
 }
 
+my $dimmdesc = {
+    optional => 1,
+    type => 'string',
+    typetext => "size=<BYTES>,start=<startadress>",
+    description => <<EODESCR,
+Later / Hotplugged memory DIMM modules
+EODESCR
+};
+PVE::JSONSchema::register_standard_option("pve-qm-dimm", $dimmdesc);
+
+for (my $i = 0; $i < $MAX_DIMMS; $i++)  {
+    $confdesc->{"dimm$i"} = $dimmdesc;
+}
+
 my $drivename_hash;
 
 my $idedesc = {
@@ -2763,7 +2784,27 @@ sub config_to_command {
 	push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
     });
 
-    push @$cmd, '-m', $conf->{memory} || $defaults->{memory};
+    my $memory = $conf->{memory} || $defaults->{memory};
+
+    for (my $i = 0; $i < $MAX_DIMMS; $i++)  {
+	next if !exists $conf->{"dimm$i"};
+	$conf->{"dimm$i"} =~ m/size=(\d+)/ or next;
+	my $dimmsize = $1;
+	$conf->{"dimm$i"} =~ m/addr=(\d+)/ or next;
+	my $addr = $1;
+	$memory -= $dimmsize;
+	push @$cmd, "-object" , "memory-backend-ram,id=mem$i,size=".int(${dimmsize}*1024*1024);
+	push @$cmd, "-device", "pc-dimm,id=dimm$i,memdev=mem$i,addr=$addr";
+    }
+
+    if ($conf->{maxmemory}) {
+        die "Memory is bigger than maxmemory\n" if $conf->{memory} > $conf->{maxmemory};
+	push @$cmd, '-m', "size=".$memory.",slots=$MAX_DIMMS,maxmem=".$conf->{maxmemory}."M";
+	push @$cmd, "-numa", "node" if ($conf->{ostype} && $conf->{ostype} =~ m/^w/);
+
+    } else {
+	push @$cmd, '-m', $memory;
+    }
 
     for (my $i = 0; $i < $MAX_NETS; $i++) {
          next if !$conf->{"net$i"};
@@ -3121,6 +3162,56 @@ sub qemu_cpu_hotplug {
     }
 }
 
+sub qemu_memory_hotplug {
+    my ($vmid, $conf, $memory) = @_;
+
+    return if !check_running($vmid);
+    return if !$conf->{maxmemory};
+
+    die "you cannot add more memory than maxmemory!\n" if $memory > $conf->{'maxmemory'};
+
+    # delta of memory must be a multiple of 1024
+    my $toadd = $memory - $conf->{memory};
+
+    die "memory hot remove is not supported!\n" if $toadd <= 0;
+
+    die "memory hot add should be a multiple of 128!\n" if ($toadd % 128 != 0);
+
+    # check for free dimms
+    my $i = 0;
+    while (defined $conf->{"dimm$i"} && $i < $MAX_DIMMS) {$i++;}
+
+    die "no free dimm slot found!\n" if ($i >= $MAX_DIMMS);
+
+    eval {
+	# size is in BYTES
+	vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem$i", props => { size => int($toadd*1024*1024) } )
+    };
+    my $err = $@;
+
+    $conf->{"dimm$i"} = "size=$toadd";
+
+    # slot is consumed even on failure and also if device_add fails
+    update_config_nolock($vmid, $conf, 1);
+
+    if (!$err) {
+	eval {
+	    vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "dimm$i", memdev => "mem$i");
+
+	    my $cmd = { execute => 'qom-get', arguments => { path => "/machine/peripheral/dimm$i", property => "addr" } };
+	    my $addr = vm_qmp_command($vmid, $cmd);
+
+	    $conf->{"dimm$i"} .= ",addr=$addr";
+
+	    # slot is consumed even on failure and also if device_add fails
+	    update_config_nolock($vmid, $conf, 1);
+	};
+	$err = $@;
+    }
+
+    die "Memory hot add failed!: $err\n" if $err;
+}
+
 sub qemu_block_set_io_throttle {
     my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_;
 
@@ -3329,6 +3420,18 @@ sub vm_start {
 	# set environment variable useful inside network script
 	$ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
 
+	# fresh VM start ignore DIMMs / delete them
+	if (!$migratedfrom) {
+	    my $found = 0;
+	    for (my $i = 0; $i < $MAX_DIMMS; $i++)  {
+		next if !exists $conf->{"dimm$i"};
+		delete($conf->{"dimm$i"});
+		$found = 1;
+	    }
+            update_config_nolock($vmid, $conf, 1);
+            $conf = load_config($vmid, $migratedfrom);	    
+	}
+
 	my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
 	my $migrate_port = 0;
-- 
1.7.10.4




More information about the pve-devel mailing list