This patch allow to hotplug memory dimm modules though a new option : dimm_memory
The dimm modules are generated from a map dimmid size dimm_memory dimm0 128 128 100.00 0 dimm1 128 256 50.00 1 dimm2 128 384 33.33 2 dimm3 128 512 25.00 3 dimm4 128 640 20.00 0 dimm5 128 768 16.67 1 dimm6 128 896 14.29 2 dimm7 128 1024 12.50 3 dimm8 128 1152 11.11 0 dimm9 128 1280 10.00 1 dimm10 128 1408 9.09 2 dimm11 128 1536 8.33 3 dimm12 128 1664 7.69 0 dimm13 128 1792 7.14 1 dimm14 128 1920 6.67 2 dimm15 128 2048 6.25 3 dimm16 128 2176 5.88 0 dimm17 128 2304 5.56 1 dimm18 128 2432 5.26 2 dimm19 128 2560 5.00 3 dimm20 128 2688 4.76 .... dimm250 16384 962560 1.70 2 dimm251 16384 978944 1.67 3 dimm252 16384 995328 1.65 0 dimm253 16384 1011712 1.62 1 dimm254 16384 1028096 1.59 2 dimm255 16384 1044480 1.57 3 max dimm_memory size is 1TB. If the dimm_memory value is not aligned on memory module, we align the dimm_memory on the next module. vmid.conf --------- memory: 1024 maxmemory: 8192 dimm_memory: 1028 total running memory is : memory + dimm_memory. dimm_memory is not seen by os at start, so need to have "memory" enough big to load kernel hotplug ---- qm set <vmid> -dimm_memory X (where X is bigger than current value) unplug (not yet implemented in qemu) ------ qm set <vmid> -delete dimm_memory qm set <vmid> -dimm_memory X (where X is lower than current value) 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 <aderum...@odiso.com> --- PVE/QemuServer.pm | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 2 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 7cfbc6f..e9edb0f 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -210,6 +210,19 @@ my $confdesc = { minimum => 16, default => 512, }, + dimm_memory => { + optional => 1, + type => 'integer', + description => "Amount of hotplugged RAM in MB", + minimum => 128, + maximum => 1044480, + }, + maxmemory => { + optional => 1, + type => 'integer', + description => "Maximum hotpluggale memory in MB.", + minimum => 128, + }, balloon => { optional => 1, type => 'integer', @@ -490,6 +503,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, @@ -2859,8 +2873,42 @@ 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; + + if($conf->{dimm_memory}) { + die "dimm_memory should be a multiple of 128!\n" if ($conf->{dimm_memory} % 128 != 0); + + $totalmemory += $conf->{dimm_memory}; + + my $dimm_id = 0; + my $current_size = 0; + my $dimm_size = 128; + for (my $j = 0; $j < 8; $j++) { + for (my $i = 0; $i < 32; $i++) { + my $name = "dimm${dimm_id}"; + $dimm_id++; + $current_size += $dimm_size; + my $numanode = $i % $sockets; + push @$cmd, "-object" , "memory-backend-ram,id=mem-$name,size=".int($dimm_size*1024*1024); + push @$cmd, "-device", "pc-dimm,id=$name,memdev=mem-$name,node=$numanode"; + $i = $j = 32 if $current_size >= $conf->{dimm_memory}; + #if dimm_memory is not aligned to dimm map + if($current_size >= $conf->{dimm_memory}) { + $conf->{dimm_memory} = $current_size; + update_config_nolock($vmid, $conf, 1); + } + } + $dimm_size *= 2; + } + } + + 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}) { @@ -3299,6 +3347,22 @@ sub qemu_devicedel { my $ret = vm_mon_cmd($vmid, "device_del", id => $deviceid); } +sub qemu_objectadd { + my($vmid, $objectid, $qomtype) = @_; + + vm_mon_cmd($vmid, "object-add", id => $objectid, "qom-type" => $qomtype); + + return 1; +} + +sub qemu_objectdel { + my($vmid, $objectid) = @_; + + vm_mon_cmd($vmid, "object-del", id => $objectid); + + return 1; +} + sub qemu_driveadd { my ($storecfg, $vmid, $device) = @_; @@ -3441,6 +3505,58 @@ sub qemu_cpu_hotplug { } } +sub qemu_memory_hotplug { + my ($vmid, $conf, $opt, $value) = @_; + + return if !check_running($vmid); + + die "maxmemory is not defined" if !$conf->{maxmemory}; + my $dimm_memory = $conf->{dimm_memory} ? $conf->{dimm_memory} : 0; + die "memory unplug is not yet available" if !$value || $value < $dimm_memory; + + die "dimm_memory should be a multiple of 128!\n" if ($value % 128 != 0); + + + my $totalmemory = $conf->{memory} + $value; + die "you cannot add more memory than maxmemory!\n" if $totalmemory > $conf->{'maxmemory'}; + + my $sockets = 1; + $sockets = $conf->{smp} if $conf->{smp}; + $sockets = $conf->{sockets} if $conf->{sockets}; + + my $dimm_id = 0; + my $current_size = 0; + my $dimm_size = 128; + for (my $j = 0; $j < 8; $j++) { + for (my $i = 0; $i < 32; $i++) { + my $name = "dimm${dimm_id}"; + $dimm_id++; + $current_size += $dimm_size; + next if $current_size <= $dimm_memory; + my $numanode = $i % $sockets; + + eval { vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) }; + if (my $err = $@) { + eval { qemu_objectdel($vmid, "mem-$name"); }; + die $err; + } + + eval { vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) }; + if (my $err = $@) { + eval { qemu_objectdel($vmid, "mem-$name"); }; + die $err; + } + #update conf after each succesful module hotplug + $conf->{$opt} = $current_size; + update_config_nolock($vmid, $conf, 1); + + return $current_size if $current_size >= $value; + } + $dimm_size *= 2; + } + +} + sub qemu_block_set_io_throttle { my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_; @@ -3688,6 +3804,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_memory(\d+)$/) { #dimms + die "skip\n" if !$conf->{maxmemory} && !$conf->{hotplug}; + qemu_memory_hotplug($vmid, $conf, $opt); } else { die "skip\n"; } @@ -3734,6 +3853,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_memory$/) { #dimms + die "skip\n" if !$conf->{maxmemory} && !$conf->{hotplug}; + $value = qemu_memory_hotplug($vmid, $conf, $opt, $value); } else { die "skip\n"; # skip non-hot-pluggable options } -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel