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@xxxxxxxxxx> > --- > 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list