mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 11:22:23 +00:00
qemu: Implement NVDIMM
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@redhat.com>
This commit is contained in:
parent
e21250dee8
commit
1bc173199e
@ -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;
|
||||
|
@ -3217,7 +3217,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;
|
||||
@ -3298,26 +3299,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)
|
||||
@ -3373,6 +3379,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
|
||||
@ -3402,8 +3409,7 @@ qemuBuildMemoryBackendStr(virJSONValuePtr *backendProps,
|
||||
|
||||
cleanup:
|
||||
virJSONValueFree(props);
|
||||
VIR_FREE(mem_path);
|
||||
|
||||
VIR_FREE(memPath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3490,6 +3496,7 @@ char *
|
||||
qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem)
|
||||
{
|
||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
const char *device;
|
||||
|
||||
if (!mem->info.alias) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
@ -3498,8 +3505,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);
|
||||
@ -3515,12 +3529,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;
|
||||
@ -7071,6 +7079,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
|
||||
@ -7107,6 +7116,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];
|
||||
@ -7227,6 +7245,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);
|
||||
}
|
||||
|
||||
|
@ -5935,6 +5935,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",
|
||||
@ -5967,11 +5968,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;
|
||||
@ -6002,6 +5998,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);
|
||||
@ -6025,12 +6023,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
|
||||
@ -6054,12 +6046,40 @@ 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;
|
||||
}
|
||||
|
||||
/* already existing devices don't need to be checked on hotplug */
|
||||
if (!mem &&
|
||||
qemuDomainDefValidateMemoryHotplugDevice(def->mems[i], def) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (hotplugMemory > hotplugSpace) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("memory device total size exceeds hotplug space"));
|
||||
|
@ -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
|
@ -2326,7 +2326,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);
|
||||
@ -2334,6 +2334,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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user