diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index cc80efa73d01a18a9f721bffa63afd487e230790..2b1833dfd7bd7a810e939b306d9c84c09fc2af0b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -725,7 +725,7 @@
Note that due to alignment of the memory chunks added via memory
hotplug the full size allocation specified by this element may be
impossible to achieve.
- Since 1.2.14
+ Since 1.2.14 supported by the QEMU driver.
currentMemory
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 04e7bcc85ffffefd24e89952fd72cd4d6dd82ea8..3491f5ff644323a28f9b516fb83ba6ebca477b87 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -8492,13 +8492,35 @@ qemuBuildCommandLine(virConnectPtr conn,
if (qemuDomainAlignMemorySizes(def) < 0)
goto error;
- /* Set '-m MB' based on maxmem, because the lower 'memory' limit
- * is set post-startup using the balloon driver. If balloon driver
- * is not supported, then they're out of luck anyway. Update the
- * XML to reflect our rounding.
- */
virCommandAddArg(cmd, "-m");
- virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024);
+
+ if (def->mem.max_memory) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("memory hotplug isn't supported by this QEMU binary"));
+ goto error;
+ }
+
+ /* due to guest support, qemu would silently enable NUMA with one node
+ * once the memory hotplug backend is enabled. To avoid possible
+ * confusion we will enforce user originated numa configuration along
+ * with memory hotplug. */
+ if (virDomainNumaGetNodeCount(def->numa) == 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("At least one numa node has to be configured when "
+ "enabling memory hotplug"));
+ goto error;
+ }
+
+ /* Use the 'k' suffix to let qemu handle the units */
+ virCommandAddArgFormat(cmd, "size=%lluk,slots=%u,maxmem=%lluk",
+ virDomainDefGetMemoryInitial(def),
+ def->mem.memory_slots,
+ def->mem.max_memory);
+
+ } else {
+ virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024);
+ }
if (def->mem.nhugepages && !virDomainNumaGetNodeCount(def->numa)) {
const long system_page_size = virGetSystemPageSizeKB();
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 8baa0402be1f42e74c116738fa23b89c7ba7e919..aa0e779229508a60ae40e3df77b94545afad0b8d 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -1052,10 +1052,6 @@ qemuDomainDefPostParse(virDomainDefPtr def,
VIR_DOMAIN_INPUT_BUS_USB) < 0)
return -1;
- /* memory hotplug tunables are not supported by this driver */
- if (virDomainDefCheckUnsupportedMemoryHotplug(def) < 0)
- return -1;
-
return 0;
}
@@ -2906,5 +2902,9 @@ qemuDomainAlignMemorySizes(virDomainDefPtr def)
mem = virDomainDefGetMemoryInitial(def);
virDomainDefSetMemoryInitial(def, VIR_ROUND_UP(mem, 1024));
+ /* Align maximum memory size. QEMU requires rounding to next 4KiB block.
+ * We'll take the "traditional" path and round it to 1MiB*/
+ def->mem.max_memory = VIR_ROUND_UP(def->mem.max_memory, 1024);
+
return 0;
}
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nonuma.xml b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nonuma.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5c807ed09fc0aa78999af8480606893c80816750
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nonuma.xml
@@ -0,0 +1,22 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 1233456789
+ 219136
+ 219136
+ 1
+
+ hvm
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu
+
+
+
+
+
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug.args b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug.args
new file mode 100644
index 0000000000000000000000000000000000000000..6c2658686346765db9279aec9ab9b1fc6fea8865
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug.args
@@ -0,0 +1,6 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M pc \
+-m size=219136k,slots=16,maxmem=1099511627776k \
+-smp 2 -numa node,nodeid=0,cpus=0-1,mem=214 \
+-nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \
+-usb -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug.xml b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug.xml
new file mode 100644
index 0000000000000000000000000000000000000000..567a662f7089b9aaf4e032fb31cd889de2cdc796
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug.xml
@@ -0,0 +1,34 @@
+
+ QEMUGuest1
+ c7a5fdbd-edaf-9455-926a-d65c16db1809
+ 1099511627776
+ 219136
+ 219136
+ 2
+
+ hvm
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+ /usr/bin/qemu
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index fcf52187305432acfdb1ca6cb2ab3c3d5e71a646..387b3494a2a8ab36abf8eaeb043e69fd2155dfec 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1543,6 +1543,10 @@ mymain(void)
DO_TEST_PARSE_ERROR("shmem-msi-only", NONE);
DO_TEST("cpu-host-passthrough-features", QEMU_CAPS_KVM, QEMU_CAPS_CPU_HOST);
+ DO_TEST_FAILURE("memory-hotplug-nonuma", QEMU_CAPS_DEVICE_PC_DIMM);
+ DO_TEST_FAILURE("memory-hotplug", NONE);
+ DO_TEST("memory-hotplug", QEMU_CAPS_DEVICE_PC_DIMM, QEMU_CAPS_NUMA);
+
virObjectUnref(driver.config);
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 346a0251a67de08c308a932fc0cea8e5dedd221c..9f1fc8ff54d3fabdf4771ae9193e5477301b236d 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -428,6 +428,9 @@ mymain(void)
DO_TEST("smbios");
DO_TEST("aarch64-aavmf-virtio-mmio");
+ DO_TEST("memory-hotplug");
+ DO_TEST("memory-hotplug-nonuma");
+
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);