[PATCH v1 23/26] qemu: Wire up MEMORY_DEVICE_SIZE_CHANGE event
Daniel Henrique Barboza
danielhb413 at gmail.com
Mon Nov 30 21:44:40 UTC 2020
On 11/27/20 12:03 PM, Michal Privoznik wrote:
> 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.
>
> TODO: Fix up total domain memory calculation.
I don't mind the 'TODO' here, but it would be good to clarify what
we can/can't expect while this isn't looked at.
E.g. I took the series for a spin in my x86 dev box (apparently pSeries
does no support virtio-pmem and virtio-mem, so here I am doing x86
work hehe) and I saw that 'virsh setmem --virtio' does not update the
'maxMemory' of the live XML. Is that the intended effect of this pending
'TODO' the commit is referring to?
Code LGTM.
Reviewed-by: Daniel Henrique Barboza <danielhb413 at gmail.com>
>
> 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 3990728939..ac87d03b33 100644
> --- a/docs/formatdomain.rst
> +++ b/docs/formatdomain.rst
> @@ -7196,6 +7196,7 @@ Example: usage of the memory devices
> <node>0</node>
> <block unit='KiB'>2048</block>
> <requested unit='KiB'>524288</requested>
> + <actual unit='KiB'>524288</requested>
> </target>
> </memory>
> <memory model='virtio' access='shared'>
> @@ -7322,6 +7323,12 @@ Example: usage of the memory devices
> granularity. This is valid for ``virtio`` model only and mutually
> exclusive with ``pmem``.
>
> + ``actual``
> + The active XML for a ``virtio`` 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 d478b639fa..3b12902e04 100644
> --- a/docs/schemas/domaincommon.rng
> +++ b/docs/schemas/domaincommon.rng
> @@ -6063,6 +6063,11 @@
> <ref name="scaledInteger"/>
> </element>
> </optional>
> + <optional>
> + <element name="actual">
> + <ref name="scaledInteger"/>
> + </element>
> + </optional>
> <optional>
> <element name="node">
> <ref name="unsignedInt"/>
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index 5db1fee16b..05f5d70cee 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -18804,6 +18804,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:
> *
> @@ -28124,7 +28139,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);
>
> @@ -28146,6 +28162,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);
> @@ -28180,7 +28200,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 31892c4941..633c07b59c 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -2342,6 +2342,9 @@ struct _virDomainMemoryDef {
> unsigned long long labelsize; /* kibibytes; valid only for NVDIMM */
> unsigned long long blocksize; /* kibibytes, valid for virtio-mem only */
> unsigned long long requestedsize; /* kibibytes, valid for virtio-mem only */
> + 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 */
> @@ -3584,6 +3587,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 1bed019aac..0fdec594ba 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -497,6 +497,7 @@ virDomainMemballoonModelTypeFromString;
> virDomainMemballoonModelTypeToString;
> virDomainMemoryDefFree;
> virDomainMemoryFindByDef;
> +virDomainMemoryFindByDeviceAlias;
> virDomainMemoryFindByDeviceInfo;
> virDomainMemoryFindInactiveByDef;
> virDomainMemoryInsert;
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index ab7938a355..fc994ec282 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -10554,6 +10554,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 010bae285d..376679da77 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 677f921920..0c5db18dff 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -4289,6 +4289,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 = info->size / 1024;
> +
> + endjob:
> + qemuDomainObjEndJob(driver, vm);
> +}
> +
> +
> static void qemuProcessEventHandler(void *data, void *opaque)
> {
> struct qemuProcessEvent *processEvent = data;
> @@ -4338,6 +4368,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 ace7c889d4..5f1cae9f48 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -1435,6 +1435,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)
> @@ -4456,6 +4470,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 c792c95c46..63c52ce6e8 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,
> @@ -511,6 +527,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 3d94181afb..0c050b27b7 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, },
> @@ -1335,6 +1337,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 e68df5abe7..64576377b8 100644
> --- a/src/qemu/qemu_process.c
> +++ b/src/qemu/qemu_process.c
> @@ -1932,6 +1932,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,
> @@ -1965,6 +2005,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
> .domainRdmaGidStatusChanged = qemuProcessHandleRdmaGidStatusChanged,
> .domainGuestCrashloaded = qemuProcessHandleGuestCrashloaded,
> .domainMemoryFailure = qemuProcessHandleMemoryFailure,
> + .domainMemoryDeviceSizeChange = qemuProcessHandleMemoryDeviceSizeChange,
> };
>
> static void
>
More information about the libvir-list
mailing list