[pve-devel] [PATCH] memory hotplug patch v6
Alexandre Derumier
aderumier at odiso.com
Mon Jan 12 14:24:38 CET 2015
This patch allow to hotplug|define pc dimm devices memory
Minimum size is 128M
Maximum devices is 255
(tested under debian and windows).
vmid.conf
---------
dimm0: size=128[,numanode=0]
dimm1: size=512,numanode=1
dimm255: size=1024
memory: 1024
maxmemory: 8192
total running memory is : memory + dimmX.
dimmX devices are not seen by os at start, so need to have "memory" enough big to load kernel
numanode is optionnal, default is node=0.
If numa node don't exist, qemu simply allocate the dimm without error
hotplug
----
qm set <vmid> -dimm0 128,[numanode=0]
unplug (not yet implemented in qemu)
------
qm set <vmid> -delete dimm0
linux guest
-----------
-acpi hotplug module should be loaded in guest
-need a recent kernel. (tested with 3.10)
can be enable automaticaly, 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
-------------
windows guest need numa:1 (windows limitation)
tested with:
- windows 2012 standard
- windows 2008 enterprise/datacenter
Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
PVE/QemuServer.pm | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 141 insertions(+), 2 deletions(-)
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 087c508..b73c058 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -210,6 +210,12 @@ my $confdesc = {
minimum => 16,
default => 512,
},
+ maxmemory => {
+ optional => 1,
+ type => 'integer',
+ description => "Maximum memory in MB for hotplug.",
+ minimum => 128,
+ },
balloon => {
optional => 1,
type => 'integer',
@@ -490,6 +496,7 @@ my $MAX_HOSTPCI_DEVICES = 4;
my $MAX_SERIAL_PORTS = 4;
my $MAX_PARALLEL_PORTS = 3;
my $MAX_NUMA = 8;
+my $MAX_DIMMS = 255;
my $numadesc = {
optional => 1,
@@ -539,6 +546,18 @@ for (my $i = 0; $i < $MAX_NETS; $i++) {
$confdesc->{"net$i"} = $netdesc;
}
+my $dimmdesc = {
+ optional => 1,
+ type => 'string',
+ typetext => "<MB>[,numanode=<id>]",
+ description => "Hotpluggable memory DIMM modules"
+};
+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 = {
@@ -1316,6 +1335,25 @@ sub parse_numa {
return $res;
}
+sub parse_dimm {
+ my ($data) = @_;
+
+ my $res = {};
+
+ foreach my $kvp (split(/,/, $data)) {
+
+ if ($kvp =~ m/^(\d+)$/) {
+ $res->{size} = $1;
+ } elsif ($kvp =~ m/^numanode=(\d)$/) {
+ $res->{numanode} = $1;
+ } else {
+ return undef;
+ }
+ }
+
+ return $res;
+}
+
sub parse_hostpci {
my ($value) = @_;
@@ -1592,6 +1630,17 @@ sub verify_numa {
die "unable to parse numa options\n";
}
+PVE::JSONSchema::register_format('pve-qm-dimm', \&verify_dimm);
+sub verify_dimm {
+ my ($value, $noerr) = @_;
+
+ return $value if parse_dimm($value);
+
+ return undef if $noerr;
+
+ die "unable to parse dimm options\n";
+}
+
PVE::JSONSchema::register_format('pve-qm-net', \&verify_net);
sub verify_net {
my ($value, $noerr) = @_;
@@ -2850,8 +2899,27 @@ sub config_to_command {
# push @$cmd, '-cpu', "$cpu,enforce";
push @$cmd, '-cpu', $cpu;
- my $memory = $conf->{memory} || $defaults->{memory};
- push @$cmd, '-m', $memory;
+ my $memory = $conf->{memory} || $defaults->{memory};
+ my $totalmemory = $memory;
+ for (my $i = 0; $i < $MAX_DIMMS; $i++) {
+ next if !exists $conf->{"dimm$i"};
+ $conf->{"dimm$i"} =~ m/(\d+)/ or next;
+ my $dimm = parse_dimm($conf->{"dimm$i"});
+ my $dimmsize = $dimm->{size};
+ my $numanode = $dimm->{numanode} ? $dimm->{numanode} : 0;
+
+ die "dimm size should be a multiple of 128!\n" if ($dimmsize % 128 != 0);
+ 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,node=$numanode";
+ $totalmemory += $dimmsize;
+ }
+
+ if ($conf->{maxmemory}) {
+ die "Total memory is bigger than maxmemory\n" if $totalmemory > $conf->{maxmemory};
+ push @$cmd, '-m', "size=".$memory.",slots=$MAX_DIMMS,maxmem=".$conf->{maxmemory}."M";
+ } else {
+ push @$cmd, '-m', $memory;
+ }
if ($conf->{numa}) {
@@ -3461,6 +3529,58 @@ sub qemu_cpu_hotplug {
}
}
+sub qemu_memory_hotplug {
+ my ($vmid, $conf, $opt, $value) = @_;
+
+ return if !check_running($vmid);
+
+ my $dimm = parse_dimm($value);
+ my $size = $dimm->{size};
+ my $numanode = $dimm->{numanode} ? $dimm->{numanode} : 0;
+
+ die "maxmemory is not defined" if !$conf->{maxmemory};
+
+ die "dimm already exist" if $conf->{$opt};
+
+ die "dimm size should be a multiple of 128!\n" if ($size % 128 != 0);
+
+ my $defaults = load_defaults();
+ my $memory = $conf->{memory} || $defaults->{memory};
+ my $totalmemory = $memory;
+ for (my $i = 0; $i < $MAX_DIMMS; $i++) {
+ next if !exists $conf->{"dimm$i"};
+ $conf->{"dimm$i"} =~ m/(\d+)/ or next;
+ my $dimmsize = $1;
+ $totalmemory += $dimmsize;
+ }
+ $totalmemory += $size;
+
+ die "you cannot add more memory than maxmemory!\n" if $totalmemory > $conf->{'maxmemory'};
+
+ eval { vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem$opt", props => { size => int($size*1024*1024) } ) };
+ if (my $err = $@) {
+ eval { qemu_objectdel($vmid, "mem$opt"); };
+ die $err;
+ }
+
+ eval { vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$opt", memdev => "mem$opt", node => $numanode) };
+ if (my $err = $@) {
+ eval { qemu_objectdel($vmid, "mem$opt"); };
+ die $err;
+ }
+
+}
+
+sub qemu_memory_unplug {
+ my ($vmid, $conf, $opt) = @_;
+
+ return if !check_running($vmid);
+
+ die "memory unplug is not yet implemented";
+
+ vm_mon_cmd($vmid, "device_del", id => $opt);
+}
+
sub qemu_block_set_io_throttle {
my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_;
@@ -3708,6 +3828,9 @@ sub vmconfig_hotplug_pending {
die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
vm_deviceunplug($vmid, $conf, $opt);
vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}));
+ } elsif ($opt =~ m/^dimm(\d+)$/) { #dimms
+ die "skip\n" if !$conf->{maxmemory} && !$conf->{hotplug};
+ qemu_memory_unplug($vmid, $conf, $opt);
} else {
die "skip\n";
}
@@ -3754,6 +3877,9 @@ sub vmconfig_hotplug_pending {
} elsif (valid_drivename($opt)) {
# some changes can be done without hotplug
vmconfig_update_disk($storecfg, $conf, $vmid, $opt, $value, 1);
+ } elsif ($opt =~ m/^dimm(\d+)$/) { #dimms
+ die "skip\n" if !$conf->{maxmemory} && !$conf->{hotplug};
+ qemu_memory_hotplug($vmid, $conf, $opt, $value);
} else {
die "skip\n"; # skip non-hot-pluggable options
}
@@ -5964,4 +6090,17 @@ sub lspci {
return $devices;
}
+sub totalmem {
+ my ($conf, $defaults) = @_;
+
+ my $totalmem = 0;
+ $totalmem = $conf->{memory} || $defaults->{memory};
+
+ foreach my $opt (keys %$conf) {
+ next if $opt !~ m/^dimm(\d+)$/;
+ $totalmem += $conf->{$opt};
+ }
+ return $totalmem;
+}
+
1;
--
1.7.10.4
More information about the pve-devel
mailing list