[PATCH v2 07/13] qemu: Wire up <memory/> live update

Michal Privoznik mprivozn at redhat.com
Tue Feb 9 11:26:12 UTC 2021


As advertised in one of previous commits, we want to be able to
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 at redhat.com>
---
 src/conf/domain_conf.c       |  36 ++++++++
 src/conf/domain_conf.h       |   4 +
 src/libvirt_private.syms     |   1 +
 src/qemu/qemu_driver.c       | 172 ++++++++++++++++++++++++++++++++++-
 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, 272 insertions(+), 1 deletion(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 52dd4ed1c4..a3313d61fe 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -17318,6 +17318,42 @@ virDomainMemoryFindInactiveByDef(virDomainDefPtr def,
 }
 
 
+/**
+ * virDomainMemoryFindByDeviceInfo:
+ * @def: domain defintion
+ * @info: device info to match
+ *
+ * For given domain definition @def find <memory/> device with
+ * matching address and matching device alias (if set in @info,
+ * otherwise ignored).
+ *
+ * Returns: device if found,
+ *          NULL otherwise.
+ */
+virDomainMemoryDefPtr
+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 (info->alias &&
+            STRNEQ_NULLABLE(tmp->info.alias, info->alias))
+            continue;
+
+        return tmp;
+    }
+
+    return NULL;
+}
+
+
 /**
  * virDomainMemoryInsert:
  *
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 29ca97b107..728e06f4ca 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3613,6 +3613,10 @@ int virDomainMemoryFindByDef(virDomainDefPtr def, virDomainMemoryDefPtr mem)
 int virDomainMemoryFindInactiveByDef(virDomainDefPtr def,
                                      virDomainMemoryDefPtr mem)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
+virDomainMemoryDefPtr
+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 d8d83c269d..41a1db7f63 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 c34af6b7d1..f28813ebc5 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7093,6 +7093,165 @@ 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;
+
+    oldDef = virDomainMemoryFindByDeviceInfo(vm->def, &dev->data.memory->info);
+    if (!oldDef) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("memory '%s' not found"), dev->data.memory->info.alias);
+        return -1;
+    }
+
+    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,
@@ -7134,6 +7293,18 @@ qemuDomainUpdateDeviceLive(virDomainObjPtr vm,
         ret = qemuDomainChangeNet(driver, vm, dev);
         break;
 
+    case VIR_DOMAIN_DEVICE_MEMORY:
+        oldDev.data.memory = virDomainMemoryFindByDeviceInfo(vm->def, &dev->data.memory->info);
+        if (oldDev.data.memory) {
+            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:
@@ -7149,7 +7320,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 dc2b46057c..ab8631e5ca 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -6714,3 +6714,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 14966d4096..4dcbcd197c 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4714,3 +4714,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 b0068f2a82..12a4e672bd 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1518,3 +1518,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 d3b2a2c7a5..2860cc8128 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -9463,3 +9463,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);
-- 
2.26.2




More information about the libvir-list mailing list