[PATCH v2 08/13] qemu: Wire up MEMORY_DEVICE_SIZE_CHANGE event

Michal Privoznik mprivozn at redhat.com
Thu Feb 18 13:31:03 UTC 2021


As advertised in previous commit, this event is delivered to us
when virtio-mem module changes the allocation inside the guest.
It comes with one attribute - size - which holds the new size of
the virtio-mem (well, allocated size), in bytes.
Mind you, this is not necessarily the same number as 'requested
size'. It almost certainly will be when sizing the memory up, but
it might not be when sizing the memory down - the guest kernel
might be unable to free some blocks.

This actual size is reported in the domain XML as an output
element only.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 docs/formatdomain.rst         |  7 ++++++
 docs/schemas/domaincommon.rng |  5 +++++
 src/conf/domain_conf.c        | 26 ++++++++++++++++++++--
 src/conf/domain_conf.h        |  8 +++++++
 src/libvirt_private.syms      |  1 +
 src/qemu/qemu_domain.c        |  3 +++
 src/qemu/qemu_domain.h        |  1 +
 src/qemu/qemu_driver.c        | 31 ++++++++++++++++++++++++++
 src/qemu/qemu_monitor.c       | 24 ++++++++++++++++++++
 src/qemu/qemu_monitor.h       | 20 +++++++++++++++++
 src/qemu/qemu_monitor_json.c  | 24 ++++++++++++++++++++
 src/qemu/qemu_process.c       | 42 +++++++++++++++++++++++++++++++++++
 12 files changed, 190 insertions(+), 2 deletions(-)

diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 05d359a32d..ccce01fd3b 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -7396,6 +7396,7 @@ Example: usage of the memory devices
          <node>0</node>
          <block unit='KiB'>2048</block>
          <requested unit='KiB'>1048576</requested>
+         <actual unit='KiB'>524288</requested>
        </target>
      </memory>
    </devices>
@@ -7512,6 +7513,12 @@ Example: usage of the memory devices
      The total size exposed to the guest. Must respect ``block`` granularity
      and be smaller or equal to ``size``.
 
+   ``actual``
+     Active XML for ``virtio-mem`` model may contain ``actual`` element that
+     reflects the actual size of the corresponding virtio memory device. The
+     element is formatted into live XML and never parsed, i.e. it is
+     output-only element.
+
 :anchor:`<a id="elementsIommu"/>`
 
 IOMMU devices
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 1f672756bd..fc7569ba8f 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -6151,6 +6151,11 @@
             <ref name="scaledInteger"/>
           </element>
         </optional>
+        <optional>
+          <element name="actual">
+            <ref name="scaledInteger"/>
+          </element>
+        </optional>
         <optional>
           <element name="label">
             <element name="size">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5f9210a112..aaeb50f2a2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -17413,6 +17413,23 @@ virDomainMemoryFindByDeviceInfo(virDomainDefPtr def,
 }
 
 
+virDomainMemoryDefPtr
+virDomainMemoryFindByDeviceAlias(virDomainDefPtr def,
+                                 const char *alias)
+{
+    size_t i;
+
+    for (i = 0; i < def->nmems; i++) {
+        virDomainMemoryDefPtr tmp = def->mems[i];
+
+        if (STREQ_NULLABLE(tmp->info.alias, alias))
+            return tmp;
+    }
+
+    return NULL;
+}
+
+
 /**
  * virDomainMemoryInsert:
  *
@@ -26746,7 +26763,8 @@ virDomainMemorySourceDefFormat(virBufferPtr buf,
 
 static void
 virDomainMemoryTargetDefFormat(virBufferPtr buf,
-                               virDomainMemoryDefPtr def)
+                               virDomainMemoryDefPtr def,
+                               unsigned int flags)
 {
     g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
 
@@ -26768,6 +26786,10 @@ virDomainMemoryTargetDefFormat(virBufferPtr buf,
 
         virBufferAsprintf(&childBuf, "<requested unit='KiB'>%llu</requested>\n",
                           def->requestedsize);
+        if (!(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) {
+            virBufferAsprintf(&childBuf, "<actual unit='KiB'>%llu</actual>\n",
+                              def->actualsize);
+        }
     }
 
     virXMLFormatElement(buf, "target", NULL, &childBuf);
@@ -26800,7 +26822,7 @@ virDomainMemoryDefFormat(virBufferPtr buf,
     if (virDomainMemorySourceDefFormat(buf, def) < 0)
         return -1;
 
-    virDomainMemoryTargetDefFormat(buf, def);
+    virDomainMemoryTargetDefFormat(buf, def, flags);
 
     virDomainDeviceInfoFormat(buf, &def->info, flags);
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index af38d8aa81..8a43c9655f 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2335,6 +2335,9 @@ struct _virDomainMemoryDef {
     unsigned long long labelsize; /* kibibytes; valid only for NVDIMM */
     unsigned long long blocksize; /* kibibytes; valid only for VIRTIO_MEM */
     unsigned long long requestedsize; /* kibibytes; valid only for VIRTIO_MEM */
+    unsigned long long actualsize; /* kibibytes, valid for VIRTIO_MEM and
+                                      active domain only, only to report never
+                                      parse */
     bool readonly; /* valid only for NVDIMM */
 
     /* required for QEMU NVDIMM ppc64 support */
@@ -3627,6 +3630,11 @@ virDomainMemoryFindByDeviceInfo(virDomainDefPtr dev,
                                 virDomainDeviceInfoPtr info)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 
+virDomainMemoryDefPtr
+virDomainMemoryFindByDeviceAlias(virDomainDefPtr def,
+                                 const char *alias)
+    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;
 bool virDomainShmemDefEquals(virDomainShmemDefPtr src, virDomainShmemDefPtr dst)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ec1af101a9..4f362e3c54 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -494,6 +494,7 @@ virDomainMemballoonModelTypeFromString;
 virDomainMemballoonModelTypeToString;
 virDomainMemoryDefFree;
 virDomainMemoryFindByDef;
+virDomainMemoryFindByDeviceAlias;
 virDomainMemoryFindByDeviceInfo;
 virDomainMemoryFindInactiveByDef;
 virDomainMemoryInsert;
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 26b9b5583e..7de9f8c050 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -10880,6 +10880,9 @@ qemuProcessEventFree(struct qemuProcessEvent *event)
     case QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE:
         virObjectUnref(event->data);
         break;
+    case QEMU_PROCESS_EVENT_MEMORY_DEVICE_SIZE_CHANGE:
+        qemuMonitorMemoryDeviceSizeChangeFree(event->data);
+        break;
     case QEMU_PROCESS_EVENT_PR_DISCONNECT:
     case QEMU_PROCESS_EVENT_LAST:
         break;
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 9a7d997d65..91d6d6ef80 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -441,6 +441,7 @@ typedef enum {
     QEMU_PROCESS_EVENT_PR_DISCONNECT,
     QEMU_PROCESS_EVENT_RDMA_GID_STATUS_CHANGED,
     QEMU_PROCESS_EVENT_GUEST_CRASHLOADED,
+    QEMU_PROCESS_EVENT_MEMORY_DEVICE_SIZE_CHANGE,
 
     QEMU_PROCESS_EVENT_LAST
 } qemuProcessEventType;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 1a121e9278..995faf2abc 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4279,6 +4279,34 @@ processGuestCrashloadedEvent(virQEMUDriverPtr driver,
 }
 
 
+static void
+processMemoryDeviceSizeChange(virQEMUDriverPtr driver,
+                              virDomainObjPtr vm,
+                              qemuMonitorMemoryDeviceSizeChangePtr info)
+{
+    virDomainMemoryDefPtr mem = NULL;
+
+    if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+        return;
+
+    if (!virDomainObjIsActive(vm)) {
+        VIR_DEBUG("Domain is not running");
+        goto endjob;
+    }
+
+    mem = virDomainMemoryFindByDeviceAlias(vm->def, info->devAlias);
+    if (!mem) {
+        VIR_DEBUG("Memory device '%s' not found", info->devAlias);
+        goto endjob;
+    }
+
+    mem->actualsize = VIR_DIV_UP(info->size, 1024);
+
+ endjob:
+    qemuDomainObjEndJob(driver, vm);
+}
+
+
 static void qemuProcessEventHandler(void *data, void *opaque)
 {
     struct qemuProcessEvent *processEvent = data;
@@ -4328,6 +4356,9 @@ static void qemuProcessEventHandler(void *data, void *opaque)
     case QEMU_PROCESS_EVENT_GUEST_CRASHLOADED:
         processGuestCrashloadedEvent(driver, vm);
         break;
+    case QEMU_PROCESS_EVENT_MEMORY_DEVICE_SIZE_CHANGE:
+        processMemoryDeviceSizeChange(driver, vm, processEvent->data);
+        break;
     case QEMU_PROCESS_EVENT_LAST:
         break;
     }
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 217870c437..a7b7a78a55 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1437,6 +1437,20 @@ qemuMonitorEmitSpiceMigrated(qemuMonitorPtr mon)
 }
 
 
+int
+qemuMonitorEmitMemoryDeviceSizeChange(qemuMonitorPtr mon,
+                                      const char *devAlias,
+                                      unsigned long long size)
+{
+    int ret = -1;
+    VIR_DEBUG("mon=%p, devAlias='%s', size=%llu", mon, devAlias, size);
+
+    QEMU_MONITOR_CALLBACK(mon, ret, domainMemoryDeviceSizeChange, mon->vm, devAlias, size);
+
+    return ret;
+}
+
+
 int
 qemuMonitorEmitMemoryFailure(qemuMonitorPtr mon,
                              qemuMonitorEventMemoryFailurePtr mfp)
@@ -4408,6 +4422,16 @@ qemuMonitorEventRdmaGidStatusFree(qemuMonitorRdmaGidStatusPtr info)
 }
 
 
+void
+qemuMonitorMemoryDeviceSizeChangeFree(qemuMonitorMemoryDeviceSizeChangePtr info)
+{
+    if (!info)
+        return;
+
+    g_free(info->devAlias);
+}
+
+
 int
 qemuMonitorSetWatchdogAction(qemuMonitorPtr mon,
                              const char *action)
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 05df73cff8..53f2aa9600 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -108,6 +108,14 @@ struct _qemuMonitorRdmaGidStatus {
 };
 
 
+typedef struct _qemuMonitorMemoryDeviceSizeChange qemuMonitorMemoryDeviceSizeChange;
+typedef qemuMonitorMemoryDeviceSizeChange *qemuMonitorMemoryDeviceSizeChangePtr;
+struct _qemuMonitorMemoryDeviceSizeChange {
+    char *devAlias;
+    unsigned long long size;
+};
+
+
 typedef enum {
     QEMU_MONITOR_JOB_TYPE_UNKNOWN, /* internal value, not exposed by qemu */
     QEMU_MONITOR_JOB_TYPE_COMMIT,
@@ -153,6 +161,7 @@ struct _qemuMonitorJobInfo {
 char *qemuMonitorGuestPanicEventInfoFormatMsg(qemuMonitorEventPanicInfoPtr info);
 void qemuMonitorEventPanicInfoFree(qemuMonitorEventPanicInfoPtr info);
 void qemuMonitorEventRdmaGidStatusFree(qemuMonitorRdmaGidStatusPtr info);
+void qemuMonitorMemoryDeviceSizeChangeFree(qemuMonitorMemoryDeviceSizeChangePtr info);
 
 typedef void (*qemuMonitorDestroyCallback)(qemuMonitorPtr mon,
                                            virDomainObjPtr vm,
@@ -374,6 +383,12 @@ typedef int (*qemuMonitorDomainMemoryFailureCallback)(qemuMonitorPtr mon,
                                                       qemuMonitorEventMemoryFailurePtr mfp,
                                                       void *opaque);
 
+typedef int (*qemuMonitorDomainMemoryDeviceSizeChange)(qemuMonitorPtr mon,
+                                                       virDomainObjPtr vm,
+                                                       const char *alias,
+                                                       unsigned long long size,
+                                                       void *opaque);
+
 typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks;
 typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr;
 struct _qemuMonitorCallbacks {
@@ -411,6 +426,7 @@ struct _qemuMonitorCallbacks {
     qemuMonitorDomainRdmaGidStatusChangedCallback domainRdmaGidStatusChanged;
     qemuMonitorDomainGuestCrashloadedCallback domainGuestCrashloaded;
     qemuMonitorDomainMemoryFailureCallback domainMemoryFailure;
+    qemuMonitorDomainMemoryDeviceSizeChange domainMemoryDeviceSizeChange;
 };
 
 qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm,
@@ -507,6 +523,10 @@ int qemuMonitorEmitSerialChange(qemuMonitorPtr mon,
                                 bool connected);
 int qemuMonitorEmitSpiceMigrated(qemuMonitorPtr mon);
 
+int qemuMonitorEmitMemoryDeviceSizeChange(qemuMonitorPtr mon,
+                                          const char *devAlias,
+                                          unsigned long long size);
+
 int qemuMonitorEmitMemoryFailure(qemuMonitorPtr mon,
                                  qemuMonitorEventMemoryFailurePtr mfp);
 
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 07d19b5e6d..9ce7ba52ba 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -113,6 +113,7 @@ static void qemuMonitorJSONHandleDumpCompleted(qemuMonitorPtr mon, virJSONValueP
 static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleMemoryFailure(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitorPtr mon, virJSONValuePtr data);
 
 typedef struct {
     const char *type;
@@ -133,6 +134,7 @@ static qemuEventHandler eventHandlers[] = {
     { "GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded, },
     { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
     { "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, },
+    { "MEMORY_DEVICE_SIZE_CHANGE", qemuMonitorJSONHandleMemoryDeviceSizeChange, },
     { "MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure, },
     { "MIGRATION", qemuMonitorJSONHandleMigrationStatus, },
     { "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, },
@@ -1333,6 +1335,28 @@ qemuMonitorJSONHandleSpiceMigrated(qemuMonitorPtr mon,
 }
 
 
+static void
+qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitorPtr mon,
+                                            virJSONValuePtr data)
+{
+    const char *name;
+    unsigned long long size;
+
+    if (!(name = virJSONValueObjectGetString(data, "id"))) {
+        VIR_WARN("missing device alias in MEMORY_DEVICE_SIZE_CHANGE event");
+        return;
+    }
+
+    if (virJSONValueObjectGetNumberUlong(data, "size", &size) < 0) {
+        VIR_WARN("missing new size for '%s' in MEMORY_DEVICE_SIZE_CHANGE event", name);
+        return;
+    }
+
+
+    qemuMonitorEmitMemoryDeviceSizeChange(mon, name, size);
+}
+
+
 static void
 qemuMonitorJSONHandleMemoryFailure(qemuMonitorPtr mon,
                                    virJSONValuePtr data)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index ebf42259b5..4ab085841a 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1937,6 +1937,47 @@ qemuProcessHandleMemoryFailure(qemuMonitorPtr mon G_GNUC_UNUSED,
 }
 
 
+static int
+qemuProcessHandleMemoryDeviceSizeChange(qemuMonitorPtr mon G_GNUC_UNUSED,
+                                        virDomainObjPtr vm,
+                                        const char *devAlias,
+                                        unsigned long long size,
+                                        void *opaque)
+{
+    virQEMUDriverPtr driver = opaque;
+    struct qemuProcessEvent *processEvent = NULL;
+    qemuMonitorMemoryDeviceSizeChangePtr info = NULL;
+    int ret = -1;
+
+    virObjectLock(vm);
+
+    VIR_DEBUG("Memory device '%s' changed size to '%llu' in domain '%s'",
+              devAlias, size, vm->def->name);
+
+    info = g_new0(qemuMonitorMemoryDeviceSizeChange, 1);
+    info->devAlias = g_strdup(devAlias);
+    info->size = size;
+
+    processEvent = g_new0(struct qemuProcessEvent, 1);
+    processEvent->eventType = QEMU_PROCESS_EVENT_MEMORY_DEVICE_SIZE_CHANGE;
+    processEvent->vm = virObjectRef(vm);
+    processEvent->data = g_steal_pointer(&info);
+
+    if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
+        qemuProcessEventFree(processEvent);
+        virObjectUnref(vm);
+        goto cleanup;
+    }
+
+    processEvent = NULL;
+    ret = 0;
+ cleanup:
+    qemuProcessEventFree(processEvent);
+    virObjectUnlock(vm);
+    return ret;
+}
+
+
 static qemuMonitorCallbacks monitorCallbacks = {
     .eofNotify = qemuProcessHandleMonitorEOF,
     .errorNotify = qemuProcessHandleMonitorError,
@@ -1970,6 +2011,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
     .domainRdmaGidStatusChanged = qemuProcessHandleRdmaGidStatusChanged,
     .domainGuestCrashloaded = qemuProcessHandleGuestCrashloaded,
     .domainMemoryFailure = qemuProcessHandleMemoryFailure,
+    .domainMemoryDeviceSizeChange = qemuProcessHandleMemoryDeviceSizeChange,
 };
 
 static void
-- 
2.26.2




More information about the libvir-list mailing list