On 02/25/2015 10:44 PM, Peter Krempa wrote:
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.
---
Notes:
Version 2:
- dropped the ACPI naming
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 3922cd6..6f77a38 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;
}
@@ -4559,8 +4563,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;
@@ -4573,6 +4576,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) {
@@ -4770,6 +4783,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_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_DIMM &&
+ mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("only '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_DIMM) {
+ virBufferAsprintf(&buf, ",slot=%d",
mem->info.addr.dimm.slot);
+ virBufferAsprintf(&buf, ",base=%llu",
mem->info.addr.dimm.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,
@@ -8424,10 +8526,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 27f1831..0b13d56 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -1187,8 +1187,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;
@@ -2912,5 +2917,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);
+}
Because of linux limitation, the size of the memory module of linux
guests must be multiples of 128MiB,
so could we add this limitation by libvirt which knows type of guest it
installs/runs?
Regards,
Zhu
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 c02ad7a..155cac4 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1538,6 +1538,8 @@ mymain(void)
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);
+ 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);