[PATCH 06/10] qemu: Wire up MEMORY_DEVICE_SIZE_CHANGE event

Michal Privoznik mprivozn at redhat.com
Fri Jan 22 12:50:28 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        | 24 ++++++++++++++++++--
 src/conf/domain_conf.h        |  7 ++++++
 src/libvirt_private.syms      |  1 +
 src/qemu/qemu_domain.c        |  3 +++
 src/qemu/qemu_domain.h        |  1 +
 src/qemu/qemu_driver.c        | 33 ++++++++++++++++++++++++++++
 src/qemu/qemu_monitor.c       | 24 ++++++++++++++++++++
 src/qemu/qemu_monitor.h       | 20 +++++++++++++++++
 src/qemu/qemu_monitor_json.c  | 24 ++++++++++++++++++++
 src/qemu/qemu_process.c       | 41 +++++++++++++++++++++++++++++++++++
 12 files changed, 188 insertions(+), 2 deletions(-)

diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 2938758ec2..3088da1243 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -7277,6 +7277,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>
@@ -7391,6 +7392,12 @@ Example: usage of the memory devices
      The total size of blocks exposed to the guest. Must respect ``block``
      granularity.
 
+   ``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 5bc120073e..6d5c983bc6 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -6115,6 +6115,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 b6fe5e4436..74c897b53e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -17320,6 +17320,21 @@ virDomainMemoryFindByDeviceInfo(virDomainDefPtr def,
 }
 
 
+ssize_t
+virDomainMemoryFindByDeviceAlias(virDomainDefPtr def,
+                                 const char *alias)
+{
+    size_t i;
+
+    for (i = 0; i < def->nmems; i++) {
+        if (STREQ_NULLABLE(def->mems[i]->info.alias, alias))
+            return i;
+    }
+
+    return -1;
+}
+
+
 /**
  * virDomainMemoryInsert:
  *
@@ -26611,7 +26626,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);
 
@@ -26633,6 +26649,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);
@@ -26665,7 +26685,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 ef52328a6f..5f4a455963 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2331,6 +2331,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 */
@@ -3613,6 +3616,10 @@ ssize_t virDomainMemoryFindByDeviceInfo(virDomainDefPtr dev,
                                         virDomainDeviceInfoPtr info)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 
+ssize_t 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 2e7f92bcfe..f3556cee82 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 233adc996f..783756b191 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -10771,6 +10771,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 154339ef8f..79ccccebcb 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 fadf0240fc..d64eb4d399 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4273,6 +4273,36 @@ processGuestCrashloadedEvent(virQEMUDriverPtr driver,
 }
 
 
+static void
+processMemoryDeviceSizeChange(virQEMUDriverPtr driver,
+                              virDomainObjPtr vm,
+                              qemuMonitorMemoryDeviceSizeChangePtr info)
+{
+    virDomainMemoryDefPtr mem = NULL;
+    ssize_t idx;
+
+    if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+        return;
+
+    if (!virDomainObjIsActive(vm)) {
+        VIR_DEBUG("Domain is not running");
+        goto endjob;
+    }
+
+    idx = virDomainMemoryFindByDeviceAlias(vm->def, info->devAlias);
+    if (idx < 0) {
+        VIR_DEBUG("Memory device '%s' not found", info->devAlias);
+        goto endjob;
+    }
+
+    mem = vm->def->mems[idx];
+    mem->actualsize = VIR_DIV_UP(info->size, 1024);
+
+ endjob:
+    qemuDomainObjEndJob(driver, vm);
+}
+
+
 static void qemuProcessEventHandler(void *data, void *opaque)
 {
     struct qemuProcessEvent *processEvent = data;
@@ -4322,6 +4352,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 6b3c1c2f5e..59f0265804 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)
@@ -4400,6 +4414,16 @@ qemuMonitorEventRdmaGidStatusFree(qemuMonitorRdmaGidStatusPtr info)
 }
 
 
+void
+qemuMonitorMemoryDeviceSizeChangeFree(qemuMonitorMemoryDeviceSizeChangePtr info)
+{
+    if (!info)
+        return;
+
+    VIR_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 443ddddf9b..00428c14d2 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 0ab4264522..b1dc527e8b 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 f87a3c0f60..37cc6a28e5 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1937,6 +1937,46 @@ 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;
+    }
+
+    ret = 0;
+ cleanup:
+    qemuMonitorMemoryDeviceSizeChangeFree(info);
+    virObjectUnlock(vm);
+    return ret;
+}
+
+
 static qemuMonitorCallbacks monitorCallbacks = {
     .eofNotify = qemuProcessHandleMonitorEOF,
     .errorNotify = qemuProcessHandleMonitorError,
@@ -1970,6 +2010,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