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(a)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_alias.c b/src/qemu/qemu_alias.c
index 4cccf231e..05e1293ef 100644
--- a/src/qemu/qemu_alias.c
+++ b/src/qemu/qemu_alias.c
@@ -352,17 +352,23 @@ qemuAssignDeviceMemoryAlias(virDomainDefPtr def,
size_t i;
int maxidx = 0;
int idx;
+ const char *prefix;
+
+ if (mem->model == VIR_DOMAIN_MEMORY_MODEL_DIMM)
+ prefix = "dimm";
+ else
+ prefix = "nvdimm";
if (oldAlias) {
for (i = 0; i < def->nmems; i++) {
- if ((idx = qemuDomainDeviceAliasIndex(&def->mems[i]->info,
"dimm")) >= maxidx)
+ if ((idx = qemuDomainDeviceAliasIndex(&def->mems[i]->info, prefix))
>= maxidx)
maxidx = idx + 1;
}
} else {
maxidx = mem->info.addr.dimm.slot;
}
- if (virAsprintf(&mem->info.alias, "dimm%d", maxidx) < 0)
+ if (virAsprintf(&mem->info.alias, "%s%d", prefix, maxidx) < 0)
return -1;
return 0;
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;
}
+ 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;
}
@@ -3391,6 +3397,7 @@ char *
qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
+ const char *device;
if (!mem->info.alias) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -3399,8 +3406,15 @@ qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem)
}
switch ((virDomainMemoryModel) mem->model) {
+ case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
case VIR_DOMAIN_MEMORY_MODEL_DIMM:
- virBufferAddLit(&buf, "pc-dimm,");
+
+ if (mem->model == VIR_DOMAIN_MEMORY_MODEL_DIMM)
+ device = "pc-dimm";
+ else
+ device = "nvdimm";
+
+ virBufferAsprintf(&buf, "%s,", device);
if (mem->targetNode >= 0)
virBufferAsprintf(&buf, "node=%d,", mem->targetNode);
@@ -3416,12 +3430,6 @@ qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem)
break;
- case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
- virReportError(VIR_ERR_NO_SUPPORT, "%s",
- _("nvdimm not supported yet"));
- return NULL;
- break;
-
case VIR_DOMAIN_MEMORY_MODEL_NONE:
case VIR_DOMAIN_MEMORY_MODEL_LAST:
break;
@@ -6972,6 +6980,7 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
bool obsoleteAccel = false;
+ size_t i;
int ret = -1;
/* This should *never* be NULL, since we always provide
@@ -7008,6 +7017,15 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
"with this QEMU binary"));
return -1;
}
+
+ for (i = 0; i < def->nmems; i++) {
+ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("nvdimm not is not available "
+ "with this QEMU binary"));
+ return -1;
+ }
+ }
} else {
virTristateSwitch vmport = def->features[VIR_DOMAIN_FEATURE_VMPORT];
virTristateSwitch smm = def->features[VIR_DOMAIN_FEATURE_SMM];
@@ -7128,6 +7146,18 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
}
}
+ for (i = 0; i < def->nmems; i++) {
+ if (def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_NVDIMM)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("nvdimm isn't supported by this QEMU
binary"));
+ goto cleanup;
+ }
+ virBufferAddLit(&buf, ",nvdimm=on");
+ break;
+ }
+ }
+
virCommandAddArgBuffer(cmd, &buf);
}
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;
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;
+ }
+
/* already existing devices don't need to be checked on hotplug */
if (!mem &&
qemuDomainDefValidateMemoryHotplugDevice(def->mems[i], def) < 0)
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nvdimm.args
b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nvdimm.args
new file mode 100644
index 000000000..907bcbeda
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-memory-hotplug-nvdimm.args
@@ -0,0 +1,26 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/home/test \
+USER=test \
+LOGNAME=test \
+QEMU_AUDIO_DRV=none \
+/usr/bin/qemu \
+-name QEMUGuest1 \
+-S \
+-machine pc,accel=tcg,nvdimm=on \
+-m size=1048576k,slots=16,maxmem=1099511627776k \
+-smp 2,sockets=2,cores=1,threads=1 \
+-numa node,nodeid=0,cpus=0-1,mem=1024 \
+-object memory-backend-file,id=memnvdimm0,prealloc=yes,mem-path=/tmp/nvdimm,\
+size=536870912 \
+-device nvdimm,node=0,memdev=memnvdimm0,id=nvdimm0,slot=0 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-nographic \
+-nodefaults \
+-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \
+-no-acpi \
+-boot c \
+-usb \
+-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \
+-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index d2d267fce..b6fdaaeb3 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -2329,7 +2329,7 @@ mymain(void)
DO_TEST_FAILURE("memory-align-fail", NONE);
DO_TEST_FAILURE("memory-hotplug-nonuma", QEMU_CAPS_DEVICE_PC_DIMM);
- DO_TEST_FAILURE("memory-hotplug", NONE);
+ DO_TEST("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_OBJECT_MEMORY_RAM, QEMU_CAPS_OBJECT_MEMORY_FILE);
@@ -2337,6 +2337,8 @@ mymain(void)
QEMU_CAPS_OBJECT_MEMORY_RAM, QEMU_CAPS_OBJECT_MEMORY_FILE);
DO_TEST("memory-hotplug-ppc64-nonuma", QEMU_CAPS_KVM,
QEMU_CAPS_DEVICE_PC_DIMM, QEMU_CAPS_NUMA,
QEMU_CAPS_OBJECT_MEMORY_RAM, QEMU_CAPS_OBJECT_MEMORY_FILE);
+ DO_TEST("memory-hotplug-nvdimm", QEMU_CAPS_MACHINE_OPT,
QEMU_CAPS_DEVICE_NVDIMM,
+ QEMU_CAPS_NUMA, QEMU_CAPS_OBJECT_MEMORY_RAM, QEMU_CAPS_OBJECT_MEMORY_FILE);
DO_TEST("machine-aeskeywrap-on-caps",
QEMU_CAPS_MACHINE_OPT, QEMU_CAPS_AES_KEY_WRAP,
--
2.11.0