[libvirt] [PATCHv2 07/10] qemu: add support for memory devices

Zhu Guihua zhugh.fnst at cn.fujitsu.com
Thu Feb 26 09:58:36 UTC 2015


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);




More information about the libvir-list mailing list