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

Michal Privoznik mprivozn at redhat.com
Thu Feb 18 13:31:02 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 f4c08ef43d..5f9210a112 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -17377,6 +17377,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 14d238df4b..af38d8aa81 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3622,6 +3622,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 0e6ad1222f..ec1af101a9 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 b9bbdf8d48..1a121e9278 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 a66354426d..bfd5fb80d7 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 0476d606f5..217870c437 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4721,3 +4721,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 32dc96ee82..05df73cff8 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 f8c78d9093..07d19b5e6d 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -9462,3 +9462,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 31652d4207..9f264500f3 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