]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: add support for memory devices
authorPeter Krempa <pkrempa@redhat.com>
Fri, 9 Jan 2015 09:40:37 +0000 (10:40 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Mon, 23 Mar 2015 13:25:15 +0000 (14:25 +0100)
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
src/qemu/qemu_domain.c
src/qemu/qemu_domain.h
tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-dimm.args [new file with mode: 0644]
tests/qemuxml2argvtest.c
tests/qemuxml2xmltest.c

index 3491f5ff644323a28f9b516fb83ba6ebca477b87..f74e973d221385aa4daa3375aa02a577e5ef5a28 100644 (file)
@@ -1222,6 +1222,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;
 }
@@ -4612,8 +4616,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;
@@ -4623,9 +4626,19 @@ qemuBuildMemoryBackendStr(unsigned long long size,
     *backendProps = NULL;
     *backendType = NULL;
 
+    /* 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;
+    }
+
     if (!(props = virJSONValueNewObject()))
         return -1;
 
+    memAccess = virDomainNumaGetNodeMemoryAccessMode(def->numa, guestNode);
     mode = virDomainNumatuneGetMode(def->numa, guestNode);
 
     if (pagesize == 0 || pagesize != system_page_size) {
@@ -4824,6 +4837,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,
@@ -8602,10 +8704,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 ||
index a4bd638c85ec79dd1bd6c6a94c4e93cf43d01e90..655afb9c341dc81b7ada51c4f8d0c5a38a0a6cc8 100644 (file)
@@ -1200,8 +1200,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;
 
@@ -2958,5 +2963,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);
+}
index 75e82f31ea9bb4ba4188c4d4cf7d2c71e027dc51..28eefac986c79233d5792b4fb0857ce6863a3968 100644 (file)
@@ -423,5 +423,6 @@ bool qemuDomainDiskBlockJobIsActive(virDomainDiskDefPtr disk);
 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 (file)
index 0000000..7fbde33
--- /dev/null
@@ -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
index 387b3494a2a8ab36abf8eaeb043e69fd2155dfec..08f374e056f282dca0635ce97c4b557e558b6ebd 100644 (file)
@@ -1546,6 +1546,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);
index 9f1fc8ff54d3fabdf4771ae9193e5477301b236d..0f16d5ebc184e20a449c9ff7d7d258e9c6ff8a49 100644 (file)
@@ -430,6 +430,7 @@ mymain(void)
 
     DO_TEST("memory-hotplug");
     DO_TEST("memory-hotplug-nonuma");
+    DO_TEST("memory-hotplug-dimm");
 
     virObjectUnref(driver.caps);
     virObjectUnref(driver.xmlopt);