[PATCH v3 1/2] libvirt: support memory failure event

zhenwei pi posted 2 patches 5 years, 3 months ago
[PATCH v3 1/2] libvirt: support memory failure event
Posted by zhenwei pi 5 years, 3 months ago
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@bytedance.com>
---
 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

Re: [PATCH v3 1/2] libvirt: support memory failure event
Posted by Michal Privoznik 5 years, 3 months ago
On 10/14/20 12:37 PM, zhenwei pi wrote:
> 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@bytedance.com>
> ---
>   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,

While this works for now, it's not as future proof as it could be. We 
try to avoid enums in public APIs because if we ever add a new member 
into the enum its size might change and thus break the ABI. Use 'int' 
instead. That is also the reason why we use int on the RPC level. Then 
we merely document in the comment what enum to expect for each argument.

> +                                                           unsigned int flags,
> +                                                           void *opaque);


Therefore, I suggest this to be squashed in (no need to resend, I can 
fix locally, just want you to confirm you're okay with the change):

diff --git a/examples/c/misc/event-test.c b/examples/c/misc/event-test.c
index 1651efe019..f164e825e1 100644
--- a/examples/c/misc/event-test.c
+++ b/examples/c/misc/event-test.c
@@ -966,8 +966,8 @@ myDomainEventBlockThresholdCallback(virConnectPtr 
conn G_GNUC_UNUSED,
  static int
  myDomainEventMemoryFailureCallback(virConnectPtr conn G_GNUC_UNUSED,
                                     virDomainPtr dom,
-                                   virDomainMemoryFailureRecipientType 
recipient,
-                                   virDomainMemoryFailureActionType action,
+                                   int recipient,
+                                   int action,
                                     unsigned int flags,
                                     void *opaque G_GNUC_UNUSED)
  {
diff --git a/include/libvirt/libvirt-domain.h 
b/include/libvirt/libvirt-domain.h
index 5138843a56..b3310729bf 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -4627,7 +4627,9 @@ typedef void 
(*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn,
   * @conn: connection object
   * @dom: domain on which the event occurred
   * @recipient: the recipient of hardware memory failure
+ *             (virDomainMemoryFailureRecipientType)
   * @action: the action of hardware memory failure
+ *          (virDomainMemoryFailureActionType)
   * @flags: the flags of hardware memory failure
   * @opaque: application specified data
   *
@@ -4639,8 +4641,8 @@ typedef void 
(*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn,
   */
  typedef void 
(*virConnectDomainEventMemoryFailureCallback)(virConnectPtr conn,
 
virDomainPtr dom,
- 
virDomainMemoryFailureRecipientType recipient,
- 
virDomainMemoryFailureActionType action,
+                                                           int recipient,
+                                                           int action,
                                                             unsigned 
int flags,
                                                             void *opaque);

diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c
index 4a6051a6ab..0fde3481ed 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -292,8 +292,8 @@ typedef virDomainEventBlockThreshold 
*virDomainEventBlockThresholdPtr;
  struct _virDomainEventMemoryFailure {
      virDomainEvent parent;

-    virDomainMemoryFailureRecipientType recipient;
-    virDomainMemoryFailureActionType action;
+    int recipient;
+    int action;
      unsigned int flags;
  };
  typedef struct _virDomainEventMemoryFailure virDomainEventMemoryFailure;
@@ -1644,8 +1644,8 @@ static virObjectEventPtr
  virDomainEventMemoryFailureNew(int id,
                                 const char *name,
                                 unsigned char *uuid,
-                               virDomainMemoryFailureRecipientType 
recipient,
-                               virDomainMemoryFailureActionType action,
+                               int recipient,
+                               int action,
                                 unsigned int flags)
  {
      virDomainEventMemoryFailurePtr ev;
@@ -1667,8 +1667,8 @@ virDomainEventMemoryFailureNew(int id,

  virObjectEventPtr
  virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj,
- 
virDomainMemoryFailureRecipientType recipient,
-                                      virDomainMemoryFailureActionType 
action,
+                                      int recipient,
+                                      int action,
                                        unsigned int flags)
  {
      return virDomainEventMemoryFailureNew(obj->def->id, obj->def->name,
@@ -1678,8 +1678,8 @@ 
virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj,

  virObjectEventPtr
  virDomainEventMemoryFailureNewFromDom(virDomainPtr dom,
- 
virDomainMemoryFailureRecipientType recipient,
-                                      virDomainMemoryFailureActionType 
action,
+                                      int recipient,
+                                      int action,
                                        unsigned int flags)
  {
      return virDomainEventMemoryFailureNew(dom->id, dom->name, dom->uuid,
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h
index 1d001e164e..13a1c56ce1 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -257,14 +257,14 @@ 
virDomainEventBlockThresholdNewFromDom(virDomainPtr dom,

  virObjectEventPtr
  virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj,
- 
virDomainMemoryFailureRecipientType recipient,
-                                      virDomainMemoryFailureActionType 
action,
+                                      int recipient,
+                                      int action,
                                        unsigned int flags);

  virObjectEventPtr
  virDomainEventMemoryFailureNewFromDom(virDomainPtr dom,
- 
virDomainMemoryFailureRecipientType recipient,
-                                      virDomainMemoryFailureActionType 
action,
+                                      int recipient,
+                                      int action,
                                        unsigned int flags);

  int
diff --git a/src/remote/remote_daemon_dispatch.c 
b/src/remote/remote_daemon_dispatch.c
index 078467f8da..eb5f6ebb0c 100644
--- a/src/remote/remote_daemon_dispatch.c
+++ b/src/remote/remote_daemon_dispatch.c
@@ -1305,8 +1305,8 @@ remoteRelayDomainEventBlockThreshold(virConnectPtr 
conn,
  static int
  remoteRelayDomainEventMemoryFailure(virConnectPtr conn,
                                      virDomainPtr dom,
-                                    virDomainMemoryFailureRecipientType 
recipient,
-                                    virDomainMemoryFailureActionType 
action,
+                                    int recipient,
+                                    int action,
                                      unsigned int flags,
                                      void *opaque)
  {
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 4040f0d1e7..ef347585e8 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -13608,8 +13608,8 @@ VIR_ENUM_IMPL(virshEventMemoryFailureActionType,
  static void
  virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,
                               virDomainPtr dom,
-                             virDomainMemoryFailureRecipientType recipient,
-                             virDomainMemoryFailureActionType action,
+                             int recipient,
+                             int action,
                               unsigned int flags,
                               void *opaque)
  {



Michal