[libvirt] [PATCH v3 07/17] qemu: Implement NVDIMM

John Ferlan jferlan at redhat.com
Tue Mar 14 13:36:15 UTC 2017



On 03/09/2017 11:06 AM, Michal Privoznik wrote:
> So, majority of the code is just ready as-is. Well, with one
> slight change: differentiate between dimm and nvdimm in places
> like device alias generation, generating the command line and so
> on.
> 
> Speaking of the command line, we also need to append 'nvdimm=on'
> to the '-machine' argument so that the nvdimm feature is
> advertised in the ACPI tables properly.
> 
> Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
> ---
>  src/qemu/qemu_alias.c                              | 10 ++-
>  src/qemu/qemu_command.c                            | 76 +++++++++++++++-------
>  src/qemu/qemu_domain.c                             | 42 ++++++++----
>  .../qemuxml2argv-memory-hotplug-nvdimm.args        | 26 ++++++++
>  tests/qemuxml2argvtest.c                           |  4 +-
>  5 files changed, 121 insertions(+), 37 deletions(-)
>  create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nvdimm.args
> 

[...]

> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
> index 07178f839..a66ce6645 100644
> --- a/src/qemu/qemu_command.c
> +++ b/src/qemu/qemu_command.c
> @@ -3118,7 +3118,8 @@ qemuBuildMemoryBackendStr(virJSONValuePtr *backendProps,

>      const long system_page_size = virGetSystemPageSizeKB();
>      virDomainMemoryAccess memAccess = VIR_DOMAIN_MEMORY_ACCESS_DEFAULT;
>      size_t i;
> -    char *mem_path = NULL;
> +    char *memPath = NULL;
> +    bool prealloc = false;
>      virBitmapPtr nodemask = NULL;
>      int ret = -1;
>      virJSONValuePtr props = NULL;
> @@ -3199,26 +3200,31 @@ qemuBuildMemoryBackendStr(virJSONValuePtr *backendProps,
>      if (!(props = virJSONValueNewObject()))
>          return -1;
>  
> -    if (pagesize || def->mem.source == VIR_DOMAIN_MEMORY_SOURCE_FILE) {
> +    if (pagesize || mem->nvdimmPath ||
> +        def->mem.source == VIR_DOMAIN_MEMORY_SOURCE_FILE) {
>          *backendType = "memory-backend-file";
>  
> -        if (def->mem.source == VIR_DOMAIN_MEMORY_SOURCE_FILE) {
> -            /* we can have both pagesize and mem source, then check mem source first */
> -            if (virJSONValueObjectAdd(props,
> -                                      "s:mem-path", cfg->memoryBackingDir,
> -                                      NULL) < 0)
> +        if (mem->nvdimmPath) {
> +            if (VIR_STRDUP(memPath, mem->nvdimmPath) < 0)
> +                goto cleanup;
> +            prealloc = true;
> +        } else if (def->mem.source == VIR_DOMAIN_MEMORY_SOURCE_FILE) {
> +            /* We can have both pagesize and mem source,
> +             * then check mem source first. */
> +            if (VIR_STRDUP(memPath, cfg->memoryBackingDir) < 0)
>                  goto cleanup;
>          } else {
> -            if (qemuGetDomainHupageMemPath(def, cfg, pagesize, &mem_path) < 0)
> -                goto cleanup;
> -
> -            if (virJSONValueObjectAdd(props,
> -                                      "b:prealloc", true,
> -                                      "s:mem-path", mem_path,
> -                                      NULL) < 0)
> +            if (qemuGetDomainHupageMemPath(def, cfg, pagesize, &memPath) < 0)
>                  goto cleanup;
> +            prealloc = true;
>          }
>  

FWIW: In my v2 review I alluded to a double comma thing (e.g. code that
needs virQEMUBuildBufferEscapeComma)... This is what I was thinking
about, but IIRC it's only something for certain command line options and
not JSON object building...



> +        if (virJSONValueObjectAdd(props,
> +                                  "B:prealloc", prealloc,
> +                                  "s:mem-path", memPath,
> +                                  NULL) < 0)
> +            goto cleanup;
> +
>          switch (memAccess) {
>          case VIR_DOMAIN_MEMORY_ACCESS_SHARED:
>              if (virJSONValueObjectAdd(props, "b:share", true, NULL) < 0)
> @@ -3274,6 +3280,7 @@ qemuBuildMemoryBackendStr(virJSONValuePtr *backendProps,
>  
>      /* If none of the following is requested... */
>      if (!needHugepage && !mem->sourceNodes && !nodeSpecified &&
> +        !mem->nvdimmPath &&
>          memAccess == VIR_DOMAIN_MEMORY_ACCESS_DEFAULT &&
>          def->mem.source != VIR_DOMAIN_MEMORY_SOURCE_FILE && !force) {
>          /* report back that using the new backend is not necessary
> @@ -3303,8 +3310,7 @@ qemuBuildMemoryBackendStr(virJSONValuePtr *backendProps,
>  
>   cleanup:
>      virJSONValueFree(props);
> -    VIR_FREE(mem_path);
> -
> +    VIR_FREE(memPath);
>      return ret;
>  }

[...]

> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index 66c0e5911..495242981 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -5919,6 +5919,7 @@ qemuDomainDefValidateMemoryHotplugDevice(const virDomainMemoryDef *mem,
>  {
>      switch ((virDomainMemoryModel) mem->model) {
>      case VIR_DOMAIN_MEMORY_MODEL_DIMM:
> +    case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
>          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",
> @@ -5951,11 +5952,6 @@ qemuDomainDefValidateMemoryHotplugDevice(const virDomainMemoryDef *mem,
>          }
>          break;
>  
> -    case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
> -        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> -                       _("nvdimm hotplug not supported yet"));
> -        return -1;
> -
>      case VIR_DOMAIN_MEMORY_MODEL_NONE:
>      case VIR_DOMAIN_MEMORY_MODEL_LAST:
>          return -1;
> @@ -5986,6 +5982,8 @@ qemuDomainDefValidateMemoryHotplug(const virDomainDef *def,
>      unsigned int nmems = def->nmems;
>      unsigned long long hotplugSpace;
>      unsigned long long hotplugMemory = 0;
> +    bool needPCDimmCap = false;
> +    bool needNvdimmCap = false;

needNVDimm could be used.... although I see no reason for these bool's
the way the rest is written.

>      size_t i;
>  
>      hotplugSpace = def->mem.max_memory - virDomainDefGetMemoryInitial(def);
> @@ -6009,12 +6007,6 @@ qemuDomainDefValidateMemoryHotplug(const virDomainDef *def,
>          return 0;
>      }
>  
> -    if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
> -        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> -                       _("memory hotplug isn't supported by this QEMU binary"));
> -        return -1;
> -    }
> -
>      if (!ARCH_IS_PPC64(def->os.arch)) {
>          /* due to guest support, qemu would silently enable NUMA with one node
>           * once the memory hotplug backend is enabled. To avoid possible
> @@ -6038,6 +6030,34 @@ qemuDomainDefValidateMemoryHotplug(const virDomainDef *def,
>      for (i = 0; i < def->nmems; i++) {
>          hotplugMemory += def->mems[i]->size;
>  
> +        switch ((virDomainMemoryModel) def->mems[i]->model) {
> +        case VIR_DOMAIN_MEMORY_MODEL_DIMM:
> +            needPCDimmCap = true;
> +            break;
> +
> +        case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
> +            needNvdimmCap = true;
> +            break;
> +
> +        case VIR_DOMAIN_MEMORY_MODEL_NONE:
> +        case VIR_DOMAIN_MEMORY_MODEL_LAST:
> +            break;
> +        }
> +
> +        if (needPCDimmCap &&
> +            !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                           _("memory hotplug isn't supported by this QEMU binary"));
> +            return -1;
> +        }
> +
> +        if (needNvdimmCap &&
> +            !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_NVDIMM)) {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                           _("nvdimm isn't supported by this QEMU binary"));
> +            return -1;
> +        }
> +

Still inefficient as virQEMUCapsGet will get called each time through
the for loop as soon as need*Cap = true...  Perhaps even moreso since
one or the other will be called both times for a single pass if there
both types of *Dimm defs are in the XML (once one or the other is seen).

I think the bool's should be turned into 'checkedPCDimmCap' and
'checkedNVDimmCap' though and that would be less inefficient.

e.g. within the case:

case VIR_DOMAIN_MEMORY_MODEL_DIMM:
    if (!checkedPCDimmCap) {
        if (!virQEMUCapsGet())
            failure
        checkedPCDimmCap = true;
    }

>          /* already existing devices don't need to be checked on hotplug */
>          if (!mem &&
>              qemuDomainDefValidateMemoryHotplugDevice(def->mems[i], def) < 0)

ACK - I think it would be good to not make a CapsGet on every pass
through though

John




More information about the libvir-list mailing list