diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c0b361b771d53d686fde2f8703028ed54579bb9a..5dbe6509647ff66b051f68c728c064d05c483e2f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -9326,26 +9326,12 @@ qemuBuildCommandLine(virConnectPtr conn, qemuDomainAlignMemorySizes(def) < 0) goto error; + if (qemuDomainDefValidateMemoryHotplug(def, qemuCaps, NULL) < 0) + goto error; + virCommandAddArg(cmd, "-m"); if (virDomainDefHasMemoryHotplug(def)) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("memory hotplug isn't supported by this QEMU binary")); - goto error; - } - - /* due to guest support, qemu would silently enable NUMA with one node - * once the memory hotplug backend is enabled. To avoid possible - * confusion we will enforce user originated numa configuration along - * with memory hotplug. */ - if (virDomainNumaGetNodeCount(def->numa) == 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("At least one numa node has to be configured when " - "enabling memory hotplug")); - goto error; - } - /* Use the 'k' suffix to let qemu handle the units */ virCommandAddArgFormat(cmd, "size=%lluk,slots=%u,maxmem=%lluk", virDomainDefGetMemoryInitial(def), @@ -9408,12 +9394,6 @@ qemuBuildCommandLine(virConnectPtr conn, /* memory hotplug requires NUMA to be enabled - we already checked * that memory devices are present only when NUMA is */ - if (def->nmems > def->mem.memory_slots) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("memory device count '%zu' exceeds slots count '%u'"), - def->nmems, def->mem.memory_slots); - goto error; - } for (i = 0; i < def->nmems; i++) { char *backStr; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 3d2b7a0c9682270237d12ea0491a6a0fc97d4885..cb4cd0b9dd78283086fd0797d58149c886387317 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3558,6 +3558,84 @@ qemuDomainMachineIsS390CCW(const virDomainDef *def) } +/** + * qemuDomainDefValidateMemoryHotplug: + * @def: domain definition + * @qemuCaps: qemu capabilities object + * @mem: definition of memory device that is to be added to @def with hotplug, + * NULL in case of regular VM startup + * + * Validates that the domain definition and memory modules have valid + * configuration and are possibly able to accept @mem via hotplug if it's + * non-NULL. + * + * Returns 0 on success; -1 and a libvirt error on error. + */ +int +qemuDomainDefValidateMemoryHotplug(const virDomainDef *def, + virQEMUCapsPtr qemuCaps, + const virDomainMemoryDef *mem) +{ + unsigned int nmems = def->nmems; + unsigned long long hotplugSpace; + unsigned long long hotplugMemory = 0; + size_t i; + + hotplugSpace = def->mem.max_memory - virDomainDefGetMemoryInitial(def); + + if (mem) { + nmems++; + hotplugMemory = mem->size; + } + + if (!virDomainDefHasMemoryHotplug(def)) { + if (nmems) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot use/hotplug a memory device when domain " + "'maxMemory' is not defined")); + return -1; + } + + 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; + } + + /* due to guest support, qemu would silently enable NUMA with one node + * once the memory hotplug backend is enabled. To avoid possible + * confusion we will enforce user originated numa configuration along + * with memory hotplug. */ + if (virDomainNumaGetNodeCount(def->numa) == 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("At least one numa node has to be configured when " + "enabling memory hotplug")); + return -1; + } + + if (nmems > def->mem.memory_slots) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("memory device count '%u' exceeds slots count '%u'"), + nmems, def->mem.memory_slots); + return -1; + } + + for (i = 0; i < def->nmems; i++) + hotplugMemory += def->mems[i]->size; + + if (hotplugMemory > hotplugSpace) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("memory device total size exceeds hotplug space")); + return -1; + } + + return 0; +} + + /** * qemuDomainUpdateCurrentMemorySize: * diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 4be998c27d4ba392c3010a30c8969f2d8fb024f4..90b482d424c3b42970f0c16189837ef6fe4022f0 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -485,4 +485,8 @@ int qemuDomainUpdateCurrentMemorySize(virQEMUDriverPtr driver, unsigned long long qemuDomainGetMlockLimitBytes(virDomainDefPtr def); bool qemuDomainRequiresMlock(virDomainDefPtr def); +int qemuDomainDefValidateMemoryHotplug(const virDomainDef *def, + virQEMUCapsPtr qemuCaps, + const virDomainMemoryDef *mem); + #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 9a8bd9ee35f3bf28d9d3bcbd97daad069b35ba67..ccef8d5656d2f0b637b4168115422361ea781c64 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1767,11 +1767,10 @@ qemuDomainAttachMemory(virQEMUDriverPtr driver, int id; int ret = -1; - if (vm->def->nmems == vm->def->mem.memory_slots) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("no free memory device slot available")); + qemuDomainMemoryDeviceAlignSize(vm->def, mem); + + if (qemuDomainDefValidateMemoryHotplug(vm->def, priv->qemuCaps, mem) < 0) goto cleanup; - } if (virAsprintf(&mem->info.alias, "dimm%zu", vm->def->nmems) < 0) goto cleanup; @@ -1785,8 +1784,6 @@ qemuDomainAttachMemory(virQEMUDriverPtr driver, if (!(devstr = qemuBuildMemoryDeviceStr(mem, vm->def, priv->qemuCaps))) goto cleanup; - qemuDomainMemoryDeviceAlignSize(vm->def, mem); - if (qemuBuildMemoryBackendStr(mem->size, mem->pagesize, mem->targetNode, mem->sourceNodes, NULL, vm->def, priv->qemuCaps, cfg,