Since QEMU 5.2 (commit-77b285f7f6), QEMU supports 'memory failure' event, posts event to monitor if hitting a hardware memory error. Several changes in this patch: Add a new event 'memory failure' for libvirt domain. Implement memory failure event handling for QEMU from QMP. Also implement virsh command callback functions. Test case: ~# virsh event stretch --event memory-failure event 'memory-failure' for domain stretch: recipient: guest action: inject flags: action required: 0 recursive: 0 events received: 1 Signed-off-by: zhenwei pi <pizhenwei@xxxxxxxxxxxxx> --- examples/c/misc/event-test.c | 17 ++++++++ include/libvirt/libvirt-domain.h | 84 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.c | 82 ++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 12 ++++++ src/libvirt_private.syms | 2 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 57 +++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 21 +++++++++- src/qemu/qemu_monitor.h | 39 +++++++++++++++++ src/qemu/qemu_monitor_json.c | 50 ++++++++++++++++++++++ src/qemu/qemu_process.c | 28 +++++++++++++ src/remote/remote_daemon_dispatch.c | 33 +++++++++++++++ src/remote/remote_driver.c | 35 ++++++++++++++++ src/remote/remote_protocol.x | 21 +++++++++- src/remote_protocol-structs | 12 ++++++ tools/virsh-domain.c | 37 ++++++++++++++++ 17 files changed, 530 insertions(+), 2 deletions(-) diff --git a/examples/c/misc/event-test.c b/examples/c/misc/event-test.c index 52caa8ffa8..b10946d569 100644 --- a/examples/c/misc/event-test.c +++ b/examples/c/misc/event-test.c @@ -964,6 +964,22 @@ myDomainEventBlockThresholdCallback(virConnectPtr conn G_GNUC_UNUSED, static int +myDomainEventMemoryFailureCallback(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags, + void *opaque G_GNUC_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) memory failure: recipient '%d', " + "aciont '%d', action_required '%d', recursive '%d'", + __func__, virDomainGetName(dom), virDomainGetID(dom), recipient, + action, flags->action_required, flags->recursive); + return 0; +} + + +static int myDomainEventMigrationIterationCallback(virConnectPtr conn G_GNUC_UNUSED, virDomainPtr dom, int iteration, @@ -1093,6 +1109,7 @@ struct domainEventData domainEvents[] = { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, myDomainEventMetadataChangeCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback), + DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE, myDomainEventMemoryFailureCallback), }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 77f9116675..a9170d9a7e 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3196,6 +3196,66 @@ typedef enum { } virDomainEventCrashedDetailType; /** + * virDomainMemoryFailureRecipientType: + * + * Recipient of a memory failure event. + */ +typedef enum { + /* memory failure at hypersivor memory address space */ + VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_HYPERVISOR = 0, + + /* memory failure at guest memory address space */ + VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_GUEST = 1, + +# ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST +# endif +} virDomainMemoryFailureRecipientType; + + +/** + * virDomainMemoryFailureActionType: + * + * Action of a memory failure event. + */ +typedef enum { + /* the memory failure could be ignored. This will only be the case for + * action-optional failures. */ + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_IGNORE = 0, + + /* memory failure occurred in guest memory, the guest enabled MCE handling + * mechanism, and hypervisor could inject the MCE into the guest + * successfully. */ + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_INJECT = 1, + + /* the failure is unrecoverable. This occurs for action-required failures + * if the recipient is the hypervisor; hypervisor will exit. */ + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_FATAL = 2, + + /* the failure is unrecoverable but confined to the guest. This occurs if + * the recipient is a guest which is not ready to handle memory failures. */ + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_RESET = 3, + +# ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST +# endif +} virDomainMemoryFailureActionType; + + +typedef struct _virDomainMemoryFailureFlags virDomainMemoryFailureFlags; +typedef virDomainMemoryFailureFlags *virDomainMemoryFailureFlagsPtr; +struct _virDomainMemoryFailureFlags { + /* whether a memory failure event is action-required or action-optional + * (e.g. a failure during memory scrub). */ + int action_required; + + /* whether the failure occurred while the previous failure was still in + * progress. */ + int recursive; +}; + + +/** * virConnectDomainEventCallback: * @conn: virConnect connection * @dom: The domain on which the event occurred @@ -4565,6 +4625,29 @@ typedef void (*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventMemoryFailureCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @recipient: the recipient of hardware memory failure + * @action: the action of hardware memory failure + * @flags: the flags of hardware memory failure + * @opaque: application specified data + * + * The callback occurs when the hypervisor handles the hardware memory + * corrupted event. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventMemoryFailureCallback)(virConnectPtr conn, + virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags, + void *opaque); + + +/** * VIR_DOMAIN_EVENT_CALLBACK: * * Used to cast the event specific callback into the generic one @@ -4606,6 +4689,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED = 22, /* virConnectDomainEventDeviceRemovalFailedCallback */ VIR_DOMAIN_EVENT_ID_METADATA_CHANGE = 23, /* virConnectDomainEventMetadataChangeCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD = 24, /* virConnectDomainEventBlockThresholdCallback */ + VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE = 25, /* virConnectDomainEventMemoryFailureCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index a8bd9f1595..20c5590835 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -57,6 +57,7 @@ static virClassPtr virDomainEventJobCompletedClass; static virClassPtr virDomainEventDeviceRemovalFailedClass; static virClassPtr virDomainEventMetadataChangeClass; static virClassPtr virDomainEventBlockThresholdClass; +static virClassPtr virDomainEventMemoryFailureClass; static void virDomainEventDispose(void *obj); static void virDomainEventLifecycleDispose(void *obj); @@ -79,6 +80,7 @@ static void virDomainEventJobCompletedDispose(void *obj); static void virDomainEventDeviceRemovalFailedDispose(void *obj); static void virDomainEventMetadataChangeDispose(void *obj); static void virDomainEventBlockThresholdDispose(void *obj); +static void virDomainEventMemoryFailureDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -287,6 +289,16 @@ struct _virDomainEventBlockThreshold { typedef struct _virDomainEventBlockThreshold virDomainEventBlockThreshold; typedef virDomainEventBlockThreshold *virDomainEventBlockThresholdPtr; +struct _virDomainEventMemoryFailure { + virDomainEvent parent; + + virDomainMemoryFailureRecipientType recipient; + virDomainMemoryFailureActionType action; + virDomainMemoryFailureFlags flags; +}; +typedef struct _virDomainEventMemoryFailure virDomainEventMemoryFailure; +typedef virDomainEventMemoryFailure *virDomainEventMemoryFailurePtr; + static int virDomainEventsOnceInit(void) @@ -333,6 +345,8 @@ virDomainEventsOnceInit(void) return -1; if (!VIR_CLASS_NEW(virDomainEventBlockThreshold, virDomainEventClass)) return -1; + if (!VIR_CLASS_NEW(virDomainEventMemoryFailure, virDomainEventClass)) + return -1; return 0; } @@ -542,6 +556,14 @@ virDomainEventBlockThresholdDispose(void *obj) } +static void +virDomainEventMemoryFailureDispose(void *obj) +{ + virDomainEventMemoryFailurePtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + static void * virDomainEventNew(virClassPtr klass, int eventID, @@ -1619,6 +1641,53 @@ virDomainEventBlockThresholdNewFromDom(virDomainPtr dom, } +static virObjectEventPtr +virDomainEventMemoryFailureNew(int id, + const char *name, + unsigned char *uuid, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags) +{ + virDomainEventMemoryFailurePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventMemoryFailureClass, + VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE, + id, name, uuid))) + return NULL; + + ev->recipient = recipient; + ev->action = action; + ev->flags.action_required = flags->action_required; + ev->flags.recursive = flags->recursive; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags) +{ + return virDomainEventMemoryFailureNew(obj->def->id, obj->def->name, + obj->def->uuid, recipient, action, + flags); +} + +virObjectEventPtr +virDomainEventMemoryFailureNewFromDom(virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags) +{ + return virDomainEventMemoryFailureNew(dom->id, dom->name, dom->uuid, + recipient, action, flags); +} + static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, @@ -1902,6 +1971,19 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); goto cleanup; } + case VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE: + { + virDomainEventMemoryFailurePtr memoryFailureEvent; + + memoryFailureEvent = (virDomainEventMemoryFailurePtr)event; + ((virConnectDomainEventMemoryFailureCallback)cb)(conn, dom, + memoryFailureEvent->recipient, + memoryFailureEvent->action, + &memoryFailureEvent->flags, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_LAST: break; } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index d1cfb81d62..5b317e8d30 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -255,6 +255,18 @@ virDomainEventBlockThresholdNewFromDom(virDomainPtr dom, unsigned long long threshold, unsigned long long excess); +virObjectEventPtr +virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags); + +virObjectEventPtr +virDomainEventMemoryFailureNewFromDom(virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags); + int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 152083d220..927de5001a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -704,6 +704,8 @@ virDomainEventLifecycleNew; virDomainEventLifecycleNewFromDef; virDomainEventLifecycleNewFromDom; virDomainEventLifecycleNewFromObj; +virDomainEventMemoryFailureNewFromDom; +virDomainEventMemoryFailureNewFromObj; virDomainEventMetadataChangeNewFromDom; virDomainEventMetadataChangeNewFromObj; virDomainEventMigrationIterationNewFromDom; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9623123d3c..5b5316fadd 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -10551,6 +10551,7 @@ qemuProcessEventFree(struct qemuProcessEvent *event) case QEMU_PROCESS_EVENT_BLOCK_JOB: case QEMU_PROCESS_EVENT_MONITOR_EOF: case QEMU_PROCESS_EVENT_GUEST_CRASHLOADED: + case QEMU_PROCESS_EVENT_MEMORY_FAILURE: VIR_FREE(event->data); break; case QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE: diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 9bf32e16c9..51d5963f25 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_FAILURE, QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8ef812cd94..aecd947836 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4292,6 +4292,59 @@ processGuestCrashloadedEvent(virQEMUDriverPtr driver, } +static void +processMemoryFailureEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuMonitorEventMemoryFailurePtr mfp) +{ + virObjectEventPtr event = NULL; + virDomainMemoryFailureRecipientType recipient; + virDomainMemoryFailureActionType action; + virDomainMemoryFailureFlags flags; + + switch (mfp->recipient) { + case QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_HYPERVISOR: + recipient = VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_HYPERVISOR; + break; + case QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_GUEST: + recipient = VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_GUEST; + break; + case QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_LAST: + default: + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("requested unknown memory failure recipient")); + return; + } + + switch (mfp->action) { + case QEMU_MONITOR_MEMORY_FAILURE_ACTION_IGNORE: + action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_IGNORE; + break; + case QEMU_MONITOR_MEMORY_FAILURE_ACTION_INJECT: + action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_INJECT; + break; + case QEMU_MONITOR_MEMORY_FAILURE_ACTION_FATAL: + action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_FATAL; + break; + case QEMU_MONITOR_MEMORY_FAILURE_ACTION_RESET: + action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_RESET; + break; + case QEMU_MONITOR_MEMORY_FAILURE_ACTION_LAST: + default: + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("requested unknown memory failure action")); + return; + } + + flags.action_required = mfp->action_required; + flags.recursive = mfp->recursive; + event = virDomainEventMemoryFailureNewFromObj(vm, recipient, action, + &flags); + + virObjectEventStateQueue(driver->domainEventState, event); +} + + static void qemuProcessEventHandler(void *data, void *opaque) { struct qemuProcessEvent *processEvent = data; @@ -4341,6 +4394,10 @@ static void qemuProcessEventHandler(void *data, void *opaque) case QEMU_PROCESS_EVENT_GUEST_CRASHLOADED: processGuestCrashloadedEvent(driver, vm); break; + case QEMU_PROCESS_EVENT_MEMORY_FAILURE: + processMemoryFailureEvent(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 8c991fefbb..189b789bb8 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -159,7 +159,6 @@ static int qemuMonitorOnceInit(void) VIR_ONCE_GLOBAL_INIT(qemuMonitor); - VIR_ENUM_IMPL(qemuMonitorMigrationStatus, QEMU_MONITOR_MIGRATION_STATUS_LAST, "inactive", "setup", @@ -197,6 +196,14 @@ VIR_ENUM_IMPL(qemuMonitorDumpStatus, "none", "active", "completed", "failed", ); +VIR_ENUM_IMPL(qemuMonitorMemoryFailureRecipient, + QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_LAST, + "hypervisor", "guest"); + +VIR_ENUM_IMPL(qemuMonitorMemoryFailureAction, + QEMU_MONITOR_MEMORY_FAILURE_ACTION_LAST, + "ignore", "inject", + "fatal", "reset"); #if DEBUG_RAW_IO static char * @@ -1428,6 +1435,18 @@ qemuMonitorEmitSpiceMigrated(qemuMonitorPtr mon) int +qemuMonitorEmitMemoryFailure(qemuMonitorPtr mon, + qemuMonitorEventMemoryFailurePtr mfp) +{ + int ret = -1; + + QEMU_MONITOR_CALLBACK(mon, ret, domainMemoryFailure, mon->vm, mfp); + + return ret; +} + + +int qemuMonitorEmitMigrationStatus(qemuMonitorPtr mon, int status) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index a744c8975b..17ba006a2f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -340,6 +340,40 @@ typedef int (*qemuMonitorDomainGuestCrashloadedCallback)(qemuMonitorPtr mon, virDomainObjPtr vm, void *opaque); +typedef enum { + QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_HYPERVISOR, + QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_GUEST, + + QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_LAST +} qemuMonitorMemoryFailureRecipient; + +VIR_ENUM_DECL(qemuMonitorMemoryFailureRecipient); + +typedef enum { + QEMU_MONITOR_MEMORY_FAILURE_ACTION_IGNORE, + QEMU_MONITOR_MEMORY_FAILURE_ACTION_INJECT, + QEMU_MONITOR_MEMORY_FAILURE_ACTION_FATAL, + QEMU_MONITOR_MEMORY_FAILURE_ACTION_RESET, + + QEMU_MONITOR_MEMORY_FAILURE_ACTION_LAST +} qemuMonitorMemoryFailureAction; + +VIR_ENUM_DECL(qemuMonitorMemoryFailureAction); + +typedef struct _qemuMonitorEventMemoryFailure qemuMonitorEventMemoryFailure; +typedef qemuMonitorEventMemoryFailure *qemuMonitorEventMemoryFailurePtr; +struct _qemuMonitorEventMemoryFailure { + qemuMonitorMemoryFailureRecipient recipient; + qemuMonitorMemoryFailureAction action; + bool action_required; + bool recursive; +}; + +typedef int (*qemuMonitorDomainMemoryFailureCallback)(qemuMonitorPtr mon, + virDomainObjPtr vm, + qemuMonitorEventMemoryFailurePtr mfp, + void *opaque); + typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; struct _qemuMonitorCallbacks { @@ -376,6 +410,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainPRManagerStatusChangedCallback domainPRManagerStatusChanged; qemuMonitorDomainRdmaGidStatusChangedCallback domainRdmaGidStatusChanged; qemuMonitorDomainGuestCrashloadedCallback domainGuestCrashloaded; + qemuMonitorDomainMemoryFailureCallback domainMemoryFailure; }; qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, @@ -475,6 +510,10 @@ int qemuMonitorEmitSerialChange(qemuMonitorPtr mon, const char *devAlias, bool connected); int qemuMonitorEmitSpiceMigrated(qemuMonitorPtr mon); + +int qemuMonitorEmitMemoryFailure(qemuMonitorPtr mon, + qemuMonitorEventMemoryFailurePtr mfp); + int qemuMonitorEmitMigrationStatus(qemuMonitorPtr mon, int status); int qemuMonitorEmitMigrationPass(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 26ac499fc5..8e2659dc21 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -112,6 +112,7 @@ static void qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValue static void qemuMonitorJSONHandleDumpCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleMemoryFailure(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -132,6 +133,7 @@ static qemuEventHandler eventHandlers[] = { { "GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, { "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, }, + { "MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure, }, { "MIGRATION", qemuMonitorJSONHandleMigrationStatus, }, { "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, }, { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, }, @@ -1336,6 +1338,54 @@ qemuMonitorJSONHandleSpiceMigrated(qemuMonitorPtr mon, static void +qemuMonitorJSONHandleMemoryFailure(qemuMonitorPtr mon, + virJSONValuePtr data) +{ + virJSONValuePtr flagsjson = virJSONValueObjectGetObject(data, "flags"); + const char *str; + int recipient; + int action; + bool ar = false; + bool recursive = false; + qemuMonitorEventMemoryFailurePtr mfp; + + if (!(str = virJSONValueObjectGetString(data, "recipient"))) { + VIR_WARN("missing recipient in memory failure event"); + return; + } + + recipient = qemuMonitorMemoryFailureRecipientTypeFromString(str); + if (recipient == -1) { + VIR_WARN("unknown recipient '%s' in memory_failure event", str); + return; + } + + if (!(str = virJSONValueObjectGetString(data, "action"))) { + VIR_WARN("missing action in memory failure event"); + return; + } + + action = qemuMonitorMemoryFailureActionTypeFromString(str); + if (action == -1) { + VIR_WARN("unknown action '%s' in memory_failure event", str); + return; + } + + if (flagsjson) { + virJSONValueObjectGetBoolean(flagsjson, "action-required", &ar); + virJSONValueObjectGetBoolean(flagsjson, "recursive", &recursive); + } + + mfp = g_new0(qemuMonitorEventMemoryFailure, 1); + mfp->recipient = recipient; + mfp->action = action; + mfp->action_required = ar; + mfp->recursive = recursive; + qemuMonitorEmitMemoryFailure(mon, mfp); +} + + +static void qemuMonitorJSONHandleMigrationStatus(qemuMonitorPtr mon, virJSONValuePtr data) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 6b5de29fdb..abcbab0f06 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1878,6 +1878,33 @@ qemuProcessHandleGuestCrashloaded(qemuMonitorPtr mon G_GNUC_UNUSED, } +static int +qemuProcessHandleMemoryFailure(qemuMonitorPtr mon G_GNUC_UNUSED, + virDomainObjPtr vm, + qemuMonitorEventMemoryFailurePtr mfp, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + struct qemuProcessEvent *processEvent; + + virObjectLock(vm); + processEvent = g_new0(struct qemuProcessEvent, 1); + + processEvent->eventType = QEMU_PROCESS_EVENT_MEMORY_FAILURE; + processEvent->data = mfp; + processEvent->vm = virObjectRef(vm); + + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + virObjectUnref(vm); + qemuProcessEventFree(processEvent); + } + + virObjectUnlock(vm); + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuProcessHandleMonitorEOF, .errorNotify = qemuProcessHandleMonitorError, @@ -1910,6 +1937,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainPRManagerStatusChanged = qemuProcessHandlePRManagerStatusChanged, .domainRdmaGidStatusChanged = qemuProcessHandleRdmaGidStatusChanged, .domainGuestCrashloaded = qemuProcessHandleGuestCrashloaded, + .domainMemoryFailure = qemuProcessHandleMemoryFailure, }; static void diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index 32ebcd8f36..45a8ab3c05 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -1302,6 +1302,38 @@ remoteRelayDomainEventBlockThreshold(virConnectPtr conn, } +static int +remoteRelayDomainEventMemoryFailure(virConnectPtr conn, + virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_memory_failure_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + data.recipient = recipient; + data.action = action; + data.flags.action_required = flags->action_required; + data.flags.recursive = flags->recursive; + make_nonnull_domain(&data.dom, dom); + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE, + (xdrproc_t)xdr_remote_domain_event_memory_failure_msg, &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -1328,6 +1360,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMemoryFailure), }; G_STATIC_ASSERT(G_N_ELEMENTS(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d318224605..5b29161a22 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -405,6 +405,11 @@ remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog, void *evdata, void *opaque); static void +remoteDomainBuildEventMemoryFailure(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static void remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog G_GNUC_UNUSED, virNetClientPtr client G_GNUC_UNUSED, void *evdata, void *opaque); @@ -615,6 +620,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventBlockThreshold, sizeof(remote_domain_event_block_threshold_msg), (xdrproc_t)xdr_remote_domain_event_block_threshold_msg }, + { REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE, + remoteDomainBuildEventMemoryFailure, + sizeof(remote_domain_event_memory_failure_msg), + (xdrproc_t)xdr_remote_domain_event_memory_failure_msg }, }; static void @@ -5440,6 +5449,32 @@ remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog G_GNUC_UNUSED, } +static void +remoteDomainBuildEventMemoryFailure(virNetClientProgramPtr prog G_GNUC_UNUSED, + virNetClientPtr client G_GNUC_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_memory_failure_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virDomainMemoryFailureFlags flags; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + flags.action_required = msg->flags.action_required; + flags.recursive = msg->flags.recursive; + event = virDomainEventMemoryFailureNewFromDom(dom, msg->recipient, + msg->action, &flags); + + virObjectUnref(dom); + + virObjectEventStateQueueRemote(priv->eventState, event, msg->callbackID); +} + + static int remoteStreamSend(virStreamPtr st, const char *data, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f4d6147676..a3fda24807 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3469,6 +3469,19 @@ struct remote_domain_event_callback_metadata_change_msg { remote_string nsuri; }; +struct remote_domain_event_memory_failure_flags { + int action_required; + int recursive; +}; + +struct remote_domain_event_memory_failure_msg { + int callbackID; + remote_nonnull_domain dom; + int recipient; + int action; + remote_domain_event_memory_failure_flags flags; +}; + struct remote_connect_secret_event_register_any_args { int eventID; remote_secret secret; @@ -6668,5 +6681,11 @@ enum remote_procedure { * @priority: high * @acl: domain:read */ - REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422 + REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE = 423 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index bae0f0b545..1b74fb330d 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2862,6 +2862,17 @@ struct remote_domain_event_callback_metadata_change_msg { int type; remote_string nsuri; }; +struct remote_domain_event_memory_failure_flags { + int action_required; + int recursive; +}; +struct remote_domain_event_memory_failure_msg { + int callbackID; + remote_nonnull_domain dom; + int recipient; + int action; + remote_domain_event_memory_failure_flags flags; +}; struct remote_connect_secret_event_register_any_args { int eventID; remote_secret secret; @@ -3558,4 +3569,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_AGENT_SET_RESPONSE_TIMEOUT = 420, REMOTE_PROC_DOMAIN_BACKUP_BEGIN = 421, REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422, + REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE = 423, }; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 8f11393197..7c6b19a54b 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -13590,6 +13590,41 @@ virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED, } +VIR_ENUM_DECL(virshEventMemoryFailureRecipientType); +VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType, + VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST, + N_("hypervisor"), + N_("guest")); + +VIR_ENUM_DECL(virshEventMemoryFailureActionType); +VIR_ENUM_IMPL(virshEventMemoryFailureActionType, + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST, + N_("ignore"), + N_("inject"), + N_("fatal"), + N_("reset")); + +static void +virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + virDomainMemoryFailureFlagsPtr flags, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'memory-failure' for domain %s:\n" + "recipient: %s\naction: %s\nflags:\n" + "\taction required: %d\n\trecursive: %d\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)), + UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action)), + !!(flags->action_required), !!(flags->recursive)); + virshEventPrint(opaque, &buf); +} + + virshDomainEventCallback virshDomainEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, @@ -13639,6 +13674,8 @@ virshDomainEventCallback virshDomainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, { "block-threshold", VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), }, + { "memory-failure", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), }, }; G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks)); -- 2.11.0