Introduce memory failure event. Libvirt should monitor domain's event, then posts it to uplayer. According to the hardware memory corrupted message, a cloud scheduler could migrate domain to another health physical server. Several changes in this patch: public API: include/* src/conf/* src/remote/* src/remote_protocol-structs client: examples/c/misc/event-test.c tools/virsh-domain.c With this patch, each driver could implement its own method to run this new event. Signed-off-by: zhenwei pi <pizhenwei@xxxxxxxxxxxxx> --- include/libvirt/libvirt-domain.h | 82 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.c | 80 ++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 12 ++++++ src/libvirt_private.syms | 2 + src/remote/remote_daemon_dispatch.c | 32 +++++++++++++++ src/remote/remote_driver.c | 32 +++++++++++++++ src/remote/remote_protocol.x | 16 +++++++- src/remote_protocol-structs | 8 ++++ examples/c/misc/event-test.c | 16 ++++++++ tools/virsh-domain.c | 40 ++++++++++++++++++ 10 files changed, 319 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 77f9116675..5138843a56 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3196,6 +3196,64 @@ 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 enum { + /* whether a memory failure event is action-required or action-optional + * (e.g. a failure during memory scrub). */ + VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED = (1 << 0), + + /* whether the failure occurred while the previous failure was still in + * progress. */ + VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE = (1 << 1), +} virDomainMemoryFailureFlags; + + +/** * virConnectDomainEventCallback: * @conn: virConnect connection * @dom: The domain on which the event occurred @@ -4565,6 +4623,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, + unsigned int flags, + void *opaque); + + +/** * VIR_DOMAIN_EVENT_CALLBACK: * * Used to cast the event specific callback into the generic one @@ -4606,6 +4687,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..4a6051a6ab 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,15 @@ struct _virDomainEventBlockThreshold { typedef struct _virDomainEventBlockThreshold virDomainEventBlockThreshold; typedef virDomainEventBlockThreshold *virDomainEventBlockThresholdPtr; +struct _virDomainEventMemoryFailure { + virDomainEvent parent; + + virDomainMemoryFailureRecipientType recipient; + virDomainMemoryFailureActionType action; + unsigned int flags; +}; +typedef struct _virDomainEventMemoryFailure virDomainEventMemoryFailure; +typedef virDomainEventMemoryFailure *virDomainEventMemoryFailurePtr; static int virDomainEventsOnceInit(void) @@ -333,6 +344,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 +555,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 +1640,52 @@ virDomainEventBlockThresholdNewFromDom(virDomainPtr dom, } +static virObjectEventPtr +virDomainEventMemoryFailureNew(int id, + const char *name, + unsigned char *uuid, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + unsigned int 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 = flags; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + unsigned int flags) +{ + return virDomainEventMemoryFailureNew(obj->def->id, obj->def->name, + obj->def->uuid, recipient, action, + flags); +} + +virObjectEventPtr +virDomainEventMemoryFailureNewFromDom(virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + unsigned int flags) +{ + return virDomainEventMemoryFailureNew(dom->id, dom->name, dom->uuid, + recipient, action, flags); +} + static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, @@ -1902,6 +1969,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..1d001e164e 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, + unsigned int flags); + +virObjectEventPtr +virDomainEventMemoryFailureNewFromDom(virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + unsigned int flags); + int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 52e9c6313f..ca43d1c199 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -705,6 +705,8 @@ virDomainEventLifecycleNew; virDomainEventLifecycleNewFromDef; virDomainEventLifecycleNewFromDom; virDomainEventLifecycleNewFromObj; +virDomainEventMemoryFailureNewFromDom; +virDomainEventMemoryFailureNewFromObj; virDomainEventMetadataChangeNewFromDom; virDomainEventMetadataChangeNewFromObj; virDomainEventMigrationIterationNewFromDom; diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index 32ebcd8f36..078467f8da 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -1302,6 +1302,37 @@ remoteRelayDomainEventBlockThreshold(virConnectPtr conn, } +static int +remoteRelayDomainEventMemoryFailure(virConnectPtr conn, + virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + unsigned int 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 = flags; + 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 +1359,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..9cd2fd36ae 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,29 @@ 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; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventMemoryFailureNewFromDom(dom, msg->recipient, + msg->action, msg->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..5e5e781e76 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3469,6 +3469,14 @@ struct remote_domain_event_callback_metadata_change_msg { remote_string nsuri; }; +struct remote_domain_event_memory_failure_msg { + int callbackID; + remote_nonnull_domain dom; + int recipient; + int action; + unsigned int flags; +}; + struct remote_connect_secret_event_register_any_args { int eventID; remote_secret secret; @@ -6668,5 +6676,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..c2ae411885 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2862,6 +2862,13 @@ struct remote_domain_event_callback_metadata_change_msg { int type; remote_string nsuri; }; +struct remote_domain_event_memory_failure_msg { + int callbackID; + remote_nonnull_domain dom; + int recipient; + int action; + u_int flags; +}; struct remote_connect_secret_event_register_any_args { int eventID; remote_secret secret; @@ -3558,4 +3565,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/examples/c/misc/event-test.c b/examples/c/misc/event-test.c index 52caa8ffa8..1651efe019 100644 --- a/examples/c/misc/event-test.c +++ b/examples/c/misc/event-test.c @@ -964,6 +964,21 @@ myDomainEventBlockThresholdCallback(virConnectPtr conn G_GNUC_UNUSED, static int +myDomainEventMemoryFailureCallback(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + virDomainMemoryFailureRecipientType recipient, + virDomainMemoryFailureActionType action, + unsigned int flags, + void *opaque G_GNUC_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) memory failure: recipient '%d', " + "aciont '%d', flags '%d'", __func__, virDomainGetName(dom), + virDomainGetID(dom), recipient, action, flags); + return 0; +} + + +static int myDomainEventMigrationIterationCallback(virConnectPtr conn G_GNUC_UNUSED, virDomainPtr dom, int iteration, @@ -1093,6 +1108,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/tools/virsh-domain.c b/tools/virsh-domain.c index 8f11393197..2bfb33e528 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -13590,6 +13590,44 @@ 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, + unsigned int flags, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'memory-failure' for domain %s:\n" + "recipient: %s\naction: %s\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)), + UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action))); + virBufferAsprintf(&buf, _("flags:\n" + "\taction required: %d\n\trecursive: %d\n"), + !!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED), + !!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE)); + + virshEventPrint(opaque, &buf); +} + + virshDomainEventCallback virshDomainEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, @@ -13639,6 +13677,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