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(a)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