change 'requested-size' attribute of virtio-mem on the fly. This
commit does exactly that. Changing anything else is checked for
and forbidden.
Once guest has changed the allocation, QEMU emits an event which
we will use to track the allocation. In the next commit.
Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx>
---
src/conf/domain_conf.c | 23 +++++
src/conf/domain_conf.h | 3 +
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 175 ++++++++++++++++++++++++++++++++++-
src/qemu/qemu_hotplug.c | 18 ++++
src/qemu/qemu_hotplug.h | 5 +
src/qemu/qemu_monitor.c | 13 +++
src/qemu/qemu_monitor.h | 4 +
src/qemu/qemu_monitor_json.c | 15 +++
src/qemu/qemu_monitor_json.h | 5 +
10 files changed, 261 insertions(+), 1 deletion(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f8c5a40b24..b6fe5e4436 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -17297,6 +17297,29 @@ virDomainMemoryFindInactiveByDef(virDomainDefPtr def,
}
+ssize_t
+virDomainMemoryFindByDeviceInfo(virDomainDefPtr def,
+ virDomainDeviceInfoPtr info)
+{
+ size_t i;
+
+ for (i = 0; i < def->nmems; i++) {
+ virDomainMemoryDefPtr tmp = def->mems[i];
+
+ if (!virDomainDeviceInfoAddressIsEqual(&tmp->info, info))
+ continue;
+
+ /* alias, if present */
+ if (STRNEQ_NULLABLE(tmp->info.alias, info->alias))
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+
/**
* virDomainMemoryInsert:
*
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 5d89ecfe9d..ef52328a6f 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3609,6 +3609,9 @@ int virDomainMemoryFindByDef(virDomainDefPtr def, virDomainMemoryDefPtr mem)
int virDomainMemoryFindInactiveByDef(virDomainDefPtr def,
virDomainMemoryDefPtr mem)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
+ssize_t virDomainMemoryFindByDeviceInfo(virDomainDefPtr dev,
+ virDomainDeviceInfoPtr info)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
int virDomainShmemDefInsert(virDomainDefPtr def, virDomainShmemDefPtr shmem)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 962d82680e..2e7f92bcfe 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -494,6 +494,7 @@ virDomainMemballoonModelTypeFromString;
virDomainMemballoonModelTypeToString;
virDomainMemoryDefFree;
virDomainMemoryFindByDef;
+virDomainMemoryFindByDeviceInfo;
virDomainMemoryFindInactiveByDef;
virDomainMemoryInsert;
virDomainMemoryModelTypeToString;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index ed966cf7e3..fadf0240fc 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7087,6 +7087,168 @@ qemuDomainChangeDiskLive(virDomainObjPtr vm,
return 0;
}
+
+static bool
+qemuDomainChangeMemoryLiveValidateChange(const virDomainMemoryDef *oldDef,
+ const virDomainMemoryDef *newDef)
+{
+ /* The only thing that is allowed to change is 'requestedsize' for virtio
+ * model. */
+ if (oldDef->model != newDef->model) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory model from '%s' to '%s'"),
+ virDomainMemoryModelTypeToString(oldDef->model),
+ virDomainMemoryModelTypeToString(newDef->model));
+ return false;
+ }
+
+ if (oldDef->access != newDef->access) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory access from '%s' to '%s'"),
+ virDomainMemoryAccessTypeToString(oldDef->access),
+ virDomainMemoryAccessTypeToString(newDef->access));
+ return false;
+ }
+
+ if (oldDef->discard != newDef->discard) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory discard from '%s' to '%s'"),
+ virTristateBoolTypeToString(oldDef->discard),
+ virTristateBoolTypeToString(newDef->discard));
+ return false;
+ }
+
+ if (!virBitmapEqual(oldDef->sourceNodes,
+ newDef->sourceNodes)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("cannot modify memory source nodes"));
+ return false;
+ }
+
+ if (oldDef->pagesize != newDef->pagesize) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory pagesize from '%llu' to '%llu'"),
+ oldDef->pagesize,
+ newDef->pagesize);
+ return false;
+ }
+
+ if (STRNEQ_NULLABLE(oldDef->nvdimmPath, newDef->nvdimmPath)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory path from '%s' to '%s'"),
+ NULLSTR(oldDef->nvdimmPath),
+ NULLSTR(newDef->nvdimmPath));
+ return false;
+ }
+
+ if (oldDef->alignsize != newDef->alignsize) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory align size from '%llu' to '%llu'"),
+ oldDef->alignsize, newDef->alignsize);
+ return false;
+ }
+
+ if (oldDef->nvdimmPmem != newDef->nvdimmPmem) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory pmem from '%d' to '%d'"),
+ oldDef->nvdimmPmem, newDef->nvdimmPmem);
+ return false;
+ }
+
+ if (oldDef->targetNode != newDef->targetNode) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory targetNode from '%d' to '%d'"),
+ oldDef->targetNode, newDef->targetNode);
+ return false;
+ }
+
+ if (oldDef->size != newDef->size) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory size from '%llu' to '%llu'"),
+ oldDef->size, newDef->size);
+ return false;
+ }
+
+ if (oldDef->labelsize != newDef->labelsize) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory label size from '%llu' to '%llu'"),
+ oldDef->labelsize, newDef->labelsize);
+ return false;
+ }
+ if (oldDef->blocksize != newDef->blocksize) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory block size from '%llu' to '%llu'"),
+ oldDef->blocksize, newDef->blocksize);
+ return false;
+ }
+
+ /* requestedsize can change */
+
+ if (oldDef->readonly != newDef->readonly) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("cannot modify memory pmem flag"));
+ return false;
+ }
+
+ if ((oldDef->uuid || newDef->uuid) &&
+ !(oldDef->uuid && newDef->uuid &&
+ memcmp(oldDef->uuid, newDef->uuid, VIR_UUID_BUFLEN) == 0)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("cannot modify memory UUID"));
+ return false;
+ }
+
+ switch (oldDef->model) {
+ case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM:
+ break;
+
+ case VIR_DOMAIN_MEMORY_MODEL_NONE:
+ case VIR_DOMAIN_MEMORY_MODEL_DIMM:
+ case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
+ case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
+ case VIR_DOMAIN_MEMORY_MODEL_LAST:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot modify memory of model '%s'"),
+ virDomainMemoryModelTypeToString(oldDef->model));
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+
+static int
+qemuDomainChangeMemoryLive(virQEMUDriverPtr driver G_GNUC_UNUSED,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev)
+{
+ virDomainMemoryDefPtr newDef = dev->data.memory;
+ virDomainMemoryDefPtr oldDef = NULL;
+ ssize_t idx;
+
+ idx = virDomainMemoryFindByDeviceInfo(vm->def, &dev->data.memory->info);
+ if (idx < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("memory '%s' not found"), dev->data.memory->info.alias);
+ return -1;
+ }
+
+ oldDef = vm->def->mems[idx];
+
+ if (!qemuDomainChangeMemoryLiveValidateChange(oldDef, newDef))
+ return -1;
+
+ if (qemuDomainChangeMemoryRequestedSize(driver, vm,
+ newDef->info.alias,
+ newDef->requestedsize) < 0)
+ return -1;
+
+ oldDef->requestedsize = newDef->requestedsize;
+ return 0;
+}
+
+
static int
qemuDomainUpdateDeviceLive(virDomainObjPtr vm,
virDomainDeviceDefPtr dev,
@@ -7128,6 +7290,18 @@ qemuDomainUpdateDeviceLive(virDomainObjPtr vm,
ret = qemuDomainChangeNet(driver, vm, dev);
break;
+ case VIR_DOMAIN_DEVICE_MEMORY:
+ if ((idx = virDomainMemoryFindByDeviceInfo(vm->def, &dev->data.memory->info)) >= 0) {
+ oldDev.data.memory = vm->def->mems[idx];
+ if (virDomainDefCompatibleDevice(vm->def, dev, &oldDev,
+ VIR_DOMAIN_DEVICE_ACTION_UPDATE,
+ true) < 0)
+ return -1;
+ }
+
+ ret = qemuDomainChangeMemoryLive(driver, vm, dev);
+ break;
+
case VIR_DOMAIN_DEVICE_FS:
case VIR_DOMAIN_DEVICE_INPUT:
case VIR_DOMAIN_DEVICE_SOUND:
@@ -7143,7 +7317,6 @@ qemuDomainUpdateDeviceLive(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_HOSTDEV:
case VIR_DOMAIN_DEVICE_CONTROLLER:
case VIR_DOMAIN_DEVICE_REDIRDEV:
- case VIR_DOMAIN_DEVICE_MEMORY:
case VIR_DOMAIN_DEVICE_CHR:
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_TPM:
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 882e5d2384..88213a53f7 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -6702,3 +6702,21 @@ qemuDomainSetVcpuInternal(virQEMUDriverPtr driver,
virBitmapFree(livevcpus);
return ret;
}
+
+
+int
+qemuDomainChangeMemoryRequestedSize(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ const char *alias,
+ unsigned long long requestedsize)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int rc;
+
+ qemuDomainObjEnterMonitor(driver, vm);
+ rc = qemuMonitorChangeMemoryRequestedSize(priv->mon, alias, requestedsize);
+ if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ return -1;
+
+ return rc;
+}
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
index 6287c5b5e8..9e551a1f82 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -160,3 +160,8 @@ int qemuHotplugAttachDBusVMState(virQEMUDriverPtr driver,
int qemuHotplugRemoveDBusVMState(virQEMUDriverPtr driver,
virDomainObjPtr vm,
qemuDomainAsyncJob asyncJob);
+
+int qemuDomainChangeMemoryRequestedSize(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ const char *alias,
+ unsigned long long requestedsize);
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 09b8617097..6b3c1c2f5e 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4713,3 +4713,16 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions,
return qemuMonitorJSONTransactionBackup(actions, device, jobname, target,
bitmap, syncmode);
}
+
+
+int
+qemuMonitorChangeMemoryRequestedSize(qemuMonitorPtr mon,
+ const char *alias,
+ unsigned long long requestedsize)
+{
+ VIR_DEBUG("alias=%s requestedsize=%llu", alias, requestedsize);
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONChangeMemoryRequestedSize(mon, alias, requestedsize);
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index a07617ec28..443ddddf9b 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1516,3 +1516,7 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions,
const char *target,
const char *bitmap,
qemuMonitorTransactionBackupSyncMode syncmode);
+
+int qemuMonitorChangeMemoryRequestedSize(qemuMonitorPtr mon,
+ const char *alias,
+ unsigned long long requestedsize);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 8a75a2734e..0ab4264522 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -9452,3 +9452,18 @@ qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon,
return virJSONValueGetBoolean(virJSONValueObjectGet(reply, "return"),
migratable);
}
+
+
+int
+qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitorPtr mon,
+ const char *alias,
+ unsigned long long requestedsize)
+{
+ g_autofree char *path = g_strdup_printf("/machine/peripheral/%s", alias);
+ qemuMonitorJSONObjectProperty prop = {
+ .type = QEMU_MONITOR_OBJECT_PROPERTY_ULONG,
+ .val.ul = requestedsize * 1024, /* monitor needs bytes */
+ };
+
+ return qemuMonitorJSONSetObjectProperty(mon, path, "requested-size", &prop);
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index ba1531fee8..53af2b4022 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -705,3 +705,8 @@ int qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon,
int
qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon,
bool *migratable);
+
+int
+qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitorPtr mon,
+ const char *alias,
+ unsigned long long requestedsize);