Add support to start qemu instance with 'pc-dimm' device. Thanks to the
refactors we are able to reuse the existing function to determine the
parameters.
---
src/qemu/qemu_command.c | 130 ++++++++++++++++++++-
src/qemu/qemu_domain.c | 26 ++++-
src/qemu/qemu_domain.h | 1 +
.../qemuxml2argv-memory-hotplug-dimm.args | 11 ++
tests/qemuxml2argvtest.c | 2 +
tests/qemuxml2xmltest.c | 1 +
6 files changed, 167 insertions(+), 4 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-dimm.args
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index d9666c3..2568a6d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1173,6 +1173,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr
qemuCaps)
if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0)
return -1;
}
+ for (i = 0; i < def->nmems; i++) {
+ if (virAsprintf(&def->mems[i]->info.alias, "dimm%zu", i) <
0)
+ return -1;
+ }
return 0;
}
@@ -4558,8 +4562,7 @@ qemuBuildMemoryBackendStr(unsigned long long size,
virDomainHugePagePtr hugepage = NULL;
virDomainNumatuneMemMode mode;
const long system_page_size = virGetSystemPageSizeKB();
- virNumaMemAccess memAccess = virDomainNumaGetNodeMemoryAccessMode(def->numa,
guestNode);
-
+ virNumaMemAccess memAccess = VIR_NUMA_MEM_ACCESS_DEFAULT;
size_t i;
char *mem_path = NULL;
virBitmapPtr nodemask = NULL;
@@ -4572,6 +4575,16 @@ qemuBuildMemoryBackendStr(unsigned long long size,
if (!(props = virJSONValueNewObject()))
return -1;
+ /* memory devices could provide a invalid guest node */
+ if (guestNode >= virDomainNumaGetNodeCount(def->numa)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("can't add memory backend for guest node '%d'
as "
+ "the guest has only '%zu' NUMA nodes
configured"),
+ guestNode, virDomainNumaGetNodeCount(def->numa));
+ return -1;
+ }
+
+ memAccess = virDomainNumaGetNodeMemoryAccessMode(def->numa, guestNode);
mode = virDomainNumatuneGetMode(def->numa, guestNode);
if (pagesize == 0 || pagesize != system_page_size) {
@@ -4769,6 +4782,95 @@ qemuBuildMemoryCellBackendStr(virDomainDefPtr def,
}
+static char *
+qemuBuildMemoryDimmBackendStr(virDomainMemoryDefPtr mem,
+ virDomainDefPtr def,
+ virQEMUCapsPtr qemuCaps,
+ virQEMUDriverConfigPtr cfg)
+{
+ virJSONValuePtr props = NULL;
+ char *alias = NULL;
+ const char *backendType;
+ char *ret = NULL;
+
+ if (!mem->info.alias) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("memory device alias is not assigned"));
+ return NULL;
+ }
+
+ if (virAsprintf(&alias, "mem%s", mem->info.alias) < 0)
+ goto cleanup;
+
+ if (qemuBuildMemoryBackendStr(mem->size, mem->pagesize,
+ mem->targetNode, mem->sourceNodes, NULL,
+ def, qemuCaps, cfg,
+ &backendType, &props, true) < 0)
+ goto cleanup;
+
+ ret = qemuBuildObjectCommandlineFromJSON(backendType, alias, props);
+
+ cleanup:
+ VIR_FREE(alias);
+ virJSONValueFree(props);
+
+ return ret;
+}
+
+
+static char *
+qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem,
+ virQEMUCapsPtr qemuCaps)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (!mem->info.alias) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing alias for memory device"));
+ return NULL;
+ }
+
+ switch ((virDomainMemoryModel) mem->model) {
+ case VIR_DOMAIN_MEMORY_MODEL_ACPI_DIMM:
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("this qemu doesn't support the pc-dimm
device"));
+ return NULL;
+ }
+
+ if (mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ACPI_DIMM &&
+ mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("only 'acpi-dimm' addresses are supported for
the "
+ "pc-dimm device"));
+ return NULL;
+ }
+
+ virBufferAsprintf(&buf, "pc-dimm,node=%d,memdev=mem%s,id=%s",
+ mem->targetNode, mem->info.alias, mem->info.alias);
+
+ if (mem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ACPI_DIMM) {
+ virBufferAsprintf(&buf, ",slot=%d",
mem->info.addr.acpiDimm.slot);
+ virBufferAsprintf(&buf, ",base=%llu",
mem->info.addr.acpiDimm.base);
+ }
+
+ break;
+
+ case VIR_DOMAIN_MEMORY_MODEL_NONE:
+ case VIR_DOMAIN_MEMORY_MODEL_LAST:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid memory device type"));
+ break;
+
+ }
+
+ if (virBufferCheckError(&buf) < 0)
+ return NULL;
+
+ return virBufferContentAndReset(&buf);
+}
+
+
char *
qemuBuildNicStr(virDomainNetDefPtr net,
const char *prefix,
@@ -8423,10 +8525,32 @@ qemuBuildCommandLine(virConnectPtr conn,
}
}
- if (virDomainNumaGetNodeCount(def->numa))
+ if (virDomainNumaGetNodeCount(def->numa)) {
if (qemuBuildNumaArgStr(cfg, def, cmd, qemuCaps, nodeset) < 0)
goto error;
+ /* memory hotplug requires NUMA to be enabled - we already checked
+ * that memory devices are present only when NUMA is */
+ for (i = 0; i < def->nmems; i++) {
+ char *backStr;
+ char *dimmStr;
+
+ if (!(backStr = qemuBuildMemoryDimmBackendStr(def->mems[i], def,
+ qemuCaps, cfg)))
+ goto error;
+
+ if (!(dimmStr = qemuBuildMemoryDeviceStr(def->mems[i], qemuCaps))) {
+ VIR_FREE(backStr);
+ goto error;
+ }
+
+ virCommandAddArgList(cmd, "-object", backStr, "-device",
dimmStr, NULL);
+
+ VIR_FREE(backStr);
+ VIR_FREE(dimmStr);
+ }
+ }
+
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_UUID))
virCommandAddArgList(cmd, "-uuid", uuid, NULL);
if (def->virtType == VIR_DOMAIN_VIRT_XEN ||
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index c433cc5..1806d5b 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -1182,8 +1182,13 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
}
}
- if (virDomainDeviceDefCheckUnsupportedMemoryDevice(dev) < 0)
+ if (dev->type == VIR_DOMAIN_DEVICE_MEMORY &&
+ def->mem.max_memory == 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("maxMemory has to be specified when using memory "
+ "devices "));
goto cleanup;
+ }
ret = 0;
@@ -2907,5 +2912,24 @@ qemuDomainAlignMemorySizes(virDomainDefPtr def)
* We'll take the "traditional" path and round it to 1MiB*/
def->mem.max_memory = VIR_ROUND_UP(def->mem.max_memory, 1024);
+ /* Align memory module sizes */
+ for (i = 0; i < def->nmems; i++)
+ qemuDomainMemoryDeviceAlignSize(def->mems[i]);
+
return 0;
}
+
+
+/**
+ * qemuDomainMemoryDeviceAlignSize:
+ * @mem: memory device definition object
+ *
+ * Aligns the size of the memory module as qemu enforces it. The size is updated
+ * inplace. Default rounding is now to 1 MiB (qemu requires rouding to page,
+ * size so this should be safe).
+ */
+void
+qemuDomainMemoryDeviceAlignSize(virDomainMemoryDefPtr mem)
+{
+ mem->size = VIR_ROUND_UP(mem->size, 1024);
+}
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 7c8a86e..c550c86 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -419,5 +419,6 @@ int qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
void qemuDomObjEndAPI(virDomainObjPtr *vm);
int qemuDomainAlignMemorySizes(virDomainDefPtr def);
+void qemuDomainMemoryDeviceAlignSize(virDomainMemoryDefPtr mem);
#endif /* __QEMU_DOMAIN_H__ */
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-dimm.args
b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-dimm.args
new file mode 100644
index 0000000..7fbde33
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-dimm.args
@@ -0,0 +1,11 @@
+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 \
+-object memory-backend-ram,id=memdimm0,size=536870912 \
+-device pc-dimm,node=0,memdev=memdimm0,id=dimm0 \
+-object memory-backend-ram,id=memdimm1,size=536870912,host-nodes=1-3,\
+policy=bind \
+-device pc-dimm,node=0,memdev=memdimm1,id=dimm1 \
+-nographic -nodefaults -monitor unix:/tmp/test-monitor,server,nowait \
+-no-acpi -boot c -usb -hda /dev/HostVG/QEMUGuest1 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 92b5fc5..0247945 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1536,6 +1536,8 @@ mymain(void)
DO_TEST_FAILURE("memory-hotplug-nonuma", QEMU_CAPS_DEVICE_PC_DIMM);
DO_TEST_FAILURE("memory-hotplug", 0);
DO_TEST("memory-hotplug", QEMU_CAPS_DEVICE_PC_DIMM, QEMU_CAPS_NUMA);
+ DO_TEST("memory-hotplug-dimm", QEMU_CAPS_DEVICE_PC_DIMM, QEMU_CAPS_NUMA,
+ QEMU_CAPS_DEVICE, QEMU_CAPS_OBJECT_MEMORY_RAM);
virObjectUnref(driver.config);
virObjectUnref(driver.caps);
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index be6a010..471721f 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -425,6 +425,7 @@ mymain(void)
DO_TEST("memory-hotplug");
DO_TEST("memory-hotplug-nonuma");
+ DO_TEST("memory-hotplug-dimm");
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);
--
2.2.2