From: Wim ten Have <wim.ten.have(a)oracle.com>
Add support for hot plugging memory into vNUMA partitioned KVM guests.
Hot plugging memory without a target <numa> node with result in evenly
balancing the added memory along all vNUMA nodes.
Signed-off-by: Wim ten Have <wim.ten.have(a)oracle.com>
---
src/qemu/qemu_driver.c | 59 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index e64afcb8efc9..8d1f0bf13cb7 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2348,9 +2348,12 @@ static int qemuDomainSetMemoryFlags(virDomainPtr dom, unsigned long
newmem,
}
if (persistentDef) {
- /* resizing memory with NUMA nodes specified doesn't work as there
- * is no way to change the individual node sizes with this API */
- if (virDomainNumaGetNodeCount(persistentDef->numa) > 0) {
+ /* Resizing memory with NUMA nodes specified doesn't work, as there
+ * is no way to change the individual node sizes with this API, but
+ * when vNUMA automatic partitioning is in effect resizing is possible.
+ */
+ if (!virDomainVnumaIsEnabled(persistentDef->numa) &&
+ virDomainNumaGetNodeCount(persistentDef->numa) > 0) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("initial memory size of a domain with NUMA "
"nodes cannot be modified with this API"));
@@ -2365,7 +2368,12 @@ static int qemuDomainSetMemoryFlags(virDomainPtr dom, unsigned long
newmem,
goto endjob;
}
- virDomainDefSetMemoryTotal(persistentDef, newmem);
+ if (virDomainDefSetNUMAMemoryTotal(persistentDef, newmem, driver->caps)
< 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to distribute newly configured "
+ "memory across NUMA nodes"));
+ goto endjob;
+ }
if (persistentDef->mem.cur_balloon > newmem)
persistentDef->mem.cur_balloon = newmem;
@@ -2378,6 +2386,18 @@ static int qemuDomainSetMemoryFlags(virDomainPtr dom, unsigned long
newmem,
/* resize the current memory */
unsigned long oldmax = 0;
+ if ((def &&
+ virDomainVnumaIsEnabled(def->numa) &&
+ virDomainNumaGetNodeCount(def->numa)) ||
+ (persistentDef &&
+ virDomainVnumaIsEnabled(persistentDef->numa) &&
+ virDomainNumaGetNodeCount(persistentDef->numa)) > 0) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("the current memory size of a domain with NUMA "
+ "nodes cannot be modified with this API"));
+ goto endjob;
+ }
+
if (def)
oldmax = virDomainDefGetMemoryTotal(def);
if (persistentDef) {
@@ -7820,6 +7840,7 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
{
int ret = -1;
const char *alias = NULL;
+ virDomainMemoryDefPtr mem;
switch ((virDomainDeviceType)dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
@@ -7895,8 +7916,34 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_MEMORY:
/* note that qemuDomainAttachMemory always consumes dev->data.memory
* and dispatches DeviceAdded event on success */
- ret = qemuDomainAttachMemory(driver, vm,
- dev->data.memory);
+
+ mem = dev->data.memory;
+ if (mem->targetNode >= 0) {
+ ret = qemuDomainAttachMemory(driver, vm,
+ dev->data.memory);
+ } else {
+ size_t i, ncells = virDomainNumaGetNodeCount(vm->def->numa);
+ unsigned long long memsizeCell = dev->data.memory->size / ncells;
+
+ for (i = 0; i < ncells; i++) {
+
+ if (VIR_ALLOC(mem) < 0) {
+ ret = -1;
+ break;
+ }
+
+ memcpy(mem, dev->data.memory, sizeof(virDomainMemoryDef));
+
+ if (dev->data.memory->sourceNodes)
+ virBitmapCopy(mem->sourceNodes,
dev->data.memory->sourceNodes);
+
+ mem->size = memsizeCell;
+ mem->targetNode = i;
+
+ ret = qemuDomainAttachMemory(driver, vm, mem);
+ }
+ virDomainMemoryDefFree(dev->data.memory);
+ }
dev->data.memory = NULL;
break;
--
2.21.0