[pve-devel] [PATCH 2/2] add memory/dimm hotplug incl. migration support
Alexandre Derumier
aderumier at odiso.com
Wed Jul 16 16:07:31 CEST 2014
From: Stefan Priebe <s.priebe at profihost.ag>
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 | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 96 insertions(+), 4 deletions(-)
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 b94e976..1ffb4b7 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -483,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'];
@@ -520,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 = {
@@ -2769,12 +2784,24 @@ sub config_to_command {
push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
});
- $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} && $conf->{maxmemory}*1024 > $conf->{memory}) {
- push @$cmd, '-m', "size=".$conf->{memory}.",slots=10,maxmem=".$conf->{maxmemory}."G";
+ 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";
} else {
- push @$cmd, '-m', $conf->{memory};
+ push @$cmd, '-m', $memory;
}
for (my $i = 0; $i < $MAX_NETS; $i++) {
@@ -3133,6 +3160,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) = @_;
@@ -3341,6 +3418,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