[libvirt] [PATCH] Add support for SUSPEND_DISK event

This patch adds support for SUSPEND_DISK event; both lifecycle and separated. The support is added for QEMU, machines are changed to PMSUSPENDED, but as QEMU sends SHUTDOWN afterwards, the state changes to shut-off. This and much more needs to be done in order for libvirt to work with transient devices, wake-ups etc. This patch is not aiming for that functionality. --- daemon/remote.c | 25 +++++++++++ examples/domain-events/events-c/event-test.c | 22 +++++++++- examples/domain-events/events-python/event-test.py | 3 +- include/libvirt/libvirt.h.in | 29 +++++++++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 50 ++++++++++++++++++++++ src/conf/domain_event.c | 32 +++++++++++++- src/conf/domain_event.h | 4 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_monitor.c | 10 +++++ src/qemu/qemu_monitor.h | 3 ++ src/qemu/qemu_monitor_json.c | 9 ++++ src/qemu/qemu_process.c | 47 ++++++++++++++++++++ src/remote/remote_driver.c | 31 ++++++++++++++ src/remote/remote_protocol.x | 7 ++- src/remote_protocol-structs | 4 ++ 16 files changed, 282 insertions(+), 5 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e7fe128..58017e5 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -608,6 +608,30 @@ remoteRelayDomainEventBalloonChange(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int remoteRelayDomainEventSuspendDisk(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int reason ATTRIBUTE_UNUSED, + void *opaque) { + virNetServerClientPtr client = opaque; + remote_domain_event_suspend_disk_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain %s %d system suspend-disk", dom->name, dom->id); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_domain(&data.dom, dom); + + remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_SUSPEND_DISK, + (xdrproc_t)xdr_remote_domain_event_suspend_disk_msg, &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -623,6 +647,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMWakeup), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspend), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBalloonChange), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventSuspendDisk), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index cde60fb..856eb4f 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -201,6 +201,9 @@ static const char *eventDetailToString(int event, int detail) { case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: ret = "Memory"; break; + case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK: + ret = "Disk"; + break; } break; } @@ -402,6 +405,16 @@ static int myDomainEventPMSuspendCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventSuspendDiskCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int reason ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) system suspend-disk\n", + __func__, virDomainGetName(dom), virDomainGetID(dom)); + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -440,6 +453,7 @@ int main(int argc, char **argv) int callback11ret = -1; int callback12ret = -1; int callback13ret = -1; + int callback14ret = -1; struct sigaction action_stop; memset(&action_stop, 0, sizeof(action_stop)); @@ -533,6 +547,11 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventBalloonChangeCallback), strdup("callback balloonchange"), myFreeFunc); + callback14ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_SUSPEND_DISK, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventSuspendDiskCallback), + strdup("suspend-disk"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && (callback3ret != -1) && @@ -544,7 +563,8 @@ int main(int argc, char **argv) (callback10ret != -1) && (callback11ret != -1) && (callback12ret != -1) && - (callback13ret != -1)) { + (callback13ret != -1) && + (callback14ret != -1)) { if (virConnectSetKeepAlive(dconn, 5, 3) < 0) { virErrorPtr err = virGetLastError(); fprintf(stderr, "Failed to start keepalive protocol: %s\n", diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 27e74c4..bf11fb9 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -449,7 +449,7 @@ def detailToString(event, detail): ( "Unpaused", "Migrated", "Snapshot" ), ( "Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", "Failed", "Snapshot"), ( "Finished", ), - ( "Memory", ) + ( "Memory", "Disk" ) ) return eventStrings[event][detail] @@ -554,6 +554,7 @@ def main(): vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_PMWAKEUP, myDomainEventPMWakeupCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_PMSUSPEND, myDomainEventPMSuspendCallback, None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, myDomainEventBalloonChangeCallback, None) + vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_SUSPEND_DISK, myDomainEventSuspendDiskCallback, None) vc.setKeepAlive(5, 3) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index a4e8ca9..422819c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -225,6 +225,14 @@ typedef enum { #endif } virDomainPMSuspendedReason; +typedef enum { + VIR_DOMAIN_SUSPENDED_DISK_UNKNOWN = 0, + +#ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_SUSPENDED_DISK_LAST +#endif +} virDomainSuspendedDiskReason; + /** * virDomainControlState: * @@ -3114,6 +3122,7 @@ typedef enum { */ typedef enum { VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY = 0, /* Guest was PM suspended to memory */ + VIR_DOMAIN_EVENT_PMSUSPENDED_DISK = 1, /* Guest was PM suspended to disk */ #ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_PMSUSPENDED_LAST @@ -4164,6 +4173,25 @@ typedef void (*virConnectDomainEventBalloonChangeCallback)(virConnectPtr conn, void *opaque); /** + * virConnectDomainEventSuspendDiskCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @reason: reason why the callback was called, unused currently, + * always passes 0 + * @opaque: application specified data + * + * This callback occurs when the guest is suspended to disk. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_SUSPEND_DISK with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventSuspendDiskCallback)(virConnectPtr conn, + virDomainPtr dom, + int reason, + void *opaque); + + +/** * VIR_DOMAIN_EVENT_CALLBACK: * * Used to cast the event specific callback into the generic one @@ -4187,6 +4215,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_PMWAKEUP = 11, /* virConnectDomainEventPMWakeupCallback */ VIR_DOMAIN_EVENT_ID_PMSUSPEND = 12, /* virConnectDomainEventPMSuspendCallback */ VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE = 13, /* virConnectDomainEventBalloonChangeCallback */ + VIR_DOMAIN_EVENT_ID_SUSPEND_DISK = 14, /* virConnectDomainEventSuspendDiskCallback */ #ifdef VIR_ENUM_SENTINELS /* diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 6bec66d..3136841 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -170,6 +170,15 @@ cb(self, virDomain(self, _obj=dom), actual, opaque) return 0 + def _dispatchDomainEventSuspendDiskCallback(self, dom, reason, cbData): + """Dispatches event to python user domain suspend-disk event callbacks + """ + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, virDomain(self, _obj=dom), reason, opaque) + return 0; + def domainEventDeregisterAny(self, callbackID): """Removes a Domain Event Callback. De-registering for a domain callback will disable delivery of this event type """ diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 81099b1..bc73ad0 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -5812,6 +5812,53 @@ libvirt_virConnectDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_ return ret; } +static int +libvirt_virConnectDomainEventSuspendDiskCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int reason, + void *opaque) +{ + PyObject *pyobj_cbData = (PyObject*)opaque; + PyObject *pyobj_dom; + PyObject *pyobj_ret; + PyObject *pyobj_conn; + PyObject *dictKey; + int ret = -1; + + LIBVIRT_ENSURE_THREAD_STATE; + /* Create a python instance of this virDomainPtr */ + virDomainRef(dom); + + pyobj_dom = libvirt_virDomainPtrWrap(dom); + Py_INCREF(pyobj_cbData); + + dictKey = libvirt_constcharPtrWrap("conn"); + pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey); + Py_DECREF(dictKey); + + /* Call the Callback Dispatcher */ + pyobj_ret = PyObject_CallMethod(pyobj_conn, + (char*)"_dispatchDomainEventSuspendDiskCallback", + (char*)"OiO", + pyobj_dom, + reason, + pyobj_cbData); + + Py_DECREF(pyobj_cbData); + Py_DECREF(pyobj_dom); + + if(!pyobj_ret) { + DEBUG("%s - ret:%p\n", __FUNCTION__, pyobj_ret); + PyErr_Print(); + } else { + Py_DECREF(pyobj_ret); + ret = 0; + } + + LIBVIRT_RELEASE_THREAD_STATE; + return ret; +} + static PyObject * libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) @@ -5884,6 +5931,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBalloonChangeCallback); break; + case VIR_DOMAIN_EVENT_ID_SUSPEND_DISK: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventSuspendDiskCallback); + break; } if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 7da4a1e..c7010c8 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -1,7 +1,7 @@ /* * domain_event.c: domain event queue processing helpers * - * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010-2012 Red Hat, Inc. * Copyright (C) 2008 VirtualIron * * This library is free software; you can redistribute it and/or @@ -1107,10 +1107,10 @@ virDomainEventPMSuspendNew(int id, const char *name, virDomainEventPtr ev = virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_PMSUSPEND, id, name, uuid); - return ev; } + virDomainEventPtr virDomainEventPMSuspendNewFromObj(virDomainObjPtr obj) { @@ -1125,6 +1125,30 @@ virDomainEventPMSuspendNewFromDom(virDomainPtr dom) return virDomainEventPMSuspendNew(dom->id, dom->name, dom->uuid); } +static virDomainEventPtr +virDomainEventSuspendDiskNew(int id, const char *name, + unsigned char *uuid) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_SUSPEND_DISK, + id, name, uuid); + return ev; +} + +virDomainEventPtr +virDomainEventSuspendDiskNewFromObj(virDomainObjPtr obj) +{ + return virDomainEventSuspendDiskNew(obj->def->id, + obj->def->name, + obj->def->uuid); +} + +virDomainEventPtr +virDomainEventSuspendDiskNewFromDom(virDomainPtr dom) +{ + return virDomainEventSuspendDiskNew(dom->id, dom->name, dom->uuid); +} + virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, unsigned long long actual) { @@ -1294,6 +1318,10 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_SUSPEND_DISK: + ((virConnectDomainEventSuspendDiskCallback)cb)(conn, dom, 0, cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 995b655..173eba8 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -1,6 +1,7 @@ /* * domain_event.h: domain event queue processing helpers * + * Copyright (C) 2012 Red Hat, Inc. * Copyright (C) 2008 VirtualIron * * This library is free software; you can redistribute it and/or @@ -128,6 +129,9 @@ virDomainEventPtr virDomainEventPMSuspendNewFromDom(virDomainPtr dom); virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, unsigned long long actual); virDomainEventPtr virDomainEventBalloonChangeNewFromObj(virDomainObjPtr obj, unsigned long long actual); +virDomainEventPtr virDomainEventSuspendDiskNewFromObj(virDomainObjPtr obj); +virDomainEventPtr virDomainEventSuspendDiskNewFromDom(virDomainPtr dom); + void virDomainEventFree(virDomainEventPtr event); void virDomainEventStateFree(virDomainEventStatePtr state); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fe31bbe..c1f08e0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -585,6 +585,8 @@ virDomainEventStateRegisterID; virDomainEventStateFree; virDomainEventStateNew; virDomainEventStateQueue; +virDomainEventSuspendDiskNewFromDom; +virDomainEventSuspendDiskNewFromObj; virDomainEventTrayChangeNewFromDom; virDomainEventTrayChangeNewFromObj; virDomainEventWatchdogNewFromDom; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 85b0bc2..b0fa682 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1111,6 +1111,16 @@ int qemuMonitorEmitPMSuspend(qemuMonitorPtr mon) return ret; } +int qemuMonitorEmitSuspendDisk(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, ret, domainSuspendDisk, mon->vm); + + return ret; +} + int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, const char *diskAlias, int type, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 54b3a99..f4dc94f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -136,6 +136,8 @@ struct _qemuMonitorCallbacks { int (*domainBalloonChange)(qemuMonitorPtr mon, virDomainObjPtr vm, unsigned long long actual); + int (*domainSuspendDisk)(qemuMonitorPtr mon, + virDomainObjPtr vm); }; char *qemuMonitorEscapeArg(const char *in); @@ -213,6 +215,7 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, int status); int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, unsigned long long actual); +int qemuMonitorEmitSuspendDisk(qemuMonitorPtr mon); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index bd52ce4..d0fd23a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -70,6 +70,7 @@ static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr d static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -91,6 +92,7 @@ static qemuEventHandler eventHandlers[] = { { "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, }, { "STOP", qemuMonitorJSONHandleStop, }, { "SUSPEND", qemuMonitorJSONHandlePMSuspend, }, + { "SUSPEND_DISK", qemuMonitorJSONHandleSuspendDisk, }, { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, @@ -891,6 +893,13 @@ qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, qemuMonitorEmitBalloonChange(mon, actual); } +static void +qemuMonitorJSONHandleSuspendDisk(qemuMonitorPtr mon, + virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitSuspendDisk(mon); +} + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f8a2bfd..02beb5d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1178,6 +1178,52 @@ qemuProcessHandleBalloonChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, return 0; } +static int +qemuProcessHandleSuspendDisk(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event = NULL; + virDomainEventPtr lifecycleEvent = NULL; + + virDomainObjLock(vm); + event = virDomainEventSuspendDiskNewFromObj(vm); + + if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) { + qemuDomainObjPrivatePtr priv = vm->privateData; + VIR_DEBUG("Transitioned guest %s to pmsuspended state due to " + "QMP suspend_disk event", vm->def->name); + + virDomainObjSetState(vm, VIR_DOMAIN_PMSUSPENDED, + VIR_DOMAIN_PMSUSPENDED_UNKNOWN); + lifecycleEvent = + virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_PMSUSPENDED, + VIR_DOMAIN_EVENT_PMSUSPENDED_DISK); + + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) { + VIR_WARN("Unable to save status on vm %s after suspend event", + vm->def->name); + } + + if (priv->agent) + qemuAgentNotifyEvent(priv->agent, QEMU_AGENT_EVENT_SUSPEND); + } + + virDomainObjUnlock(vm); + + if (event || lifecycleEvent) { + qemuDriverLock(driver); + if (event) + qemuDomainEventQueue(driver, event); + if (lifecycleEvent) + qemuDomainEventQueue(driver, lifecycleEvent); + qemuDriverUnlock(driver); + } + + return 0; +} + static qemuMonitorCallbacks monitorCallbacks = { .destroy = qemuProcessHandleMonitorDestroy, @@ -1196,6 +1242,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainPMWakeup = qemuProcessHandlePMWakeup, .domainPMSuspend = qemuProcessHandlePMSuspend, .domainBalloonChange = qemuProcessHandleBalloonChange, + .domainSuspendDisk = qemuProcessHandleSuspendDisk, }; static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index fc4c696..741c159 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -253,6 +253,10 @@ static void remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventSuspendDisk(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, @@ -311,6 +315,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventBalloonChange, sizeof(remote_domain_event_balloon_change_msg), (xdrproc_t)xdr_remote_domain_event_balloon_change_msg }, + { REMOTE_PROC_DOMAIN_EVENT_SUSPEND_DISK, + remoteDomainBuildEventSuspendDisk, + sizeof(remote_domain_event_suspend_disk_msg), + (xdrproc_t)xdr_remote_domain_event_suspend_disk_msg }, }; enum virDrvOpenRemoteFlags { @@ -4575,6 +4583,29 @@ remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED } +static void +remoteDomainBuildEventSuspendDisk(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_suspend_disk_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventSuspendDiskNewFromDom(dom); + + virDomainFree(dom); + + remoteDomainEventQueue(priv, event); +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen(virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index b0b530c..94ea947 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2259,6 +2259,10 @@ struct remote_domain_event_balloon_change_msg { unsigned hyper actual; }; +struct remote_domain_event_suspend_disk_msg { + remote_nonnull_domain dom; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; @@ -3008,7 +3012,8 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_MEMORY_PARAMETERS = 289, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_BLOCK_COMMIT = 290, /* autogen autogen */ - REMOTE_PROC_NETWORK_UPDATE = 291 /* autogen autogen priority:high */ + REMOTE_PROC_NETWORK_UPDATE = 291, /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_EVENT_SUSPEND_DISK = 292 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 4d2627a..cbdd27c 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1718,6 +1718,9 @@ struct remote_domain_event_balloon_change_msg { remote_nonnull_domain dom; uint64_t actual; }; +struct remote_domain_event_suspend_disk_msg { + remote_nonnull_domain dom; +}; struct remote_domain_managed_save_args { remote_nonnull_domain dom; u_int flags; @@ -2415,4 +2418,5 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_MEMORY_PARAMETERS = 289, REMOTE_PROC_DOMAIN_BLOCK_COMMIT = 290, REMOTE_PROC_NETWORK_UPDATE = 291, + REMOTE_PROC_DOMAIN_EVENT_SUSPEND_DISK = 292, }; -- 1.7.12.3

On 10/12/12 21:28, Martin Kletzander wrote:
This patch adds support for SUSPEND_DISK event; both lifecycle and separated. The support is added for QEMU, machines are changed to PMSUSPENDED, but as QEMU sends SHUTDOWN afterwards, the state changes to shut-off. This and much more needs to be done in order for libvirt to work with transient devices, wake-ups etc. This patch is not aiming for that functionality. --- daemon/remote.c | 25 +++++++++++ examples/domain-events/events-c/event-test.c | 22 +++++++++- examples/domain-events/events-python/event-test.py | 3 +- include/libvirt/libvirt.h.in | 29 +++++++++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 50 ++++++++++++++++++++++ src/conf/domain_event.c | 32 +++++++++++++- src/conf/domain_event.h | 4 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_monitor.c | 10 +++++ src/qemu/qemu_monitor.h | 3 ++ src/qemu/qemu_monitor_json.c | 9 ++++ src/qemu/qemu_process.c | 47 ++++++++++++++++++++ src/remote/remote_driver.c | 31 ++++++++++++++ src/remote/remote_protocol.x | 7 ++- src/remote_protocol-structs | 4 ++ 16 files changed, 282 insertions(+), 5 deletions(-)
One philosophical question on start: Shouldn't this event be called PMSsuspendToDisk or something like that to stay consistent with the previous S3 event naming?
diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 81099b1..bc73ad0 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -5812,6 +5812,53 @@ libvirt_virConnectDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_ return ret; }
+static int +libvirt_virConnectDomainEventSuspendDiskCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int reason, + void *opaque)
Bad indentation.
+{ + PyObject *pyobj_cbData = (PyObject*)opaque; + PyObject *pyobj_dom; + PyObject *pyobj_ret; + PyObject *pyobj_conn; + PyObject *dictKey; + int ret = -1; + + LIBVIRT_ENSURE_THREAD_STATE; + /* Create a python instance of this virDomainPtr */ + virDomainRef(dom); + + pyobj_dom = libvirt_virDomainPtrWrap(dom); + Py_INCREF(pyobj_cbData); + + dictKey = libvirt_constcharPtrWrap("conn"); + pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey); + Py_DECREF(dictKey); + + /* Call the Callback Dispatcher */ + pyobj_ret = PyObject_CallMethod(pyobj_conn, + (char*)"_dispatchDomainEventSuspendDiskCallback", + (char*)"OiO", + pyobj_dom, + reason, + pyobj_cbData); + + Py_DECREF(pyobj_cbData); + Py_DECREF(pyobj_dom); + + if(!pyobj_ret) { + DEBUG("%s - ret:%p\n", __FUNCTION__, pyobj_ret); + PyErr_Print(); + } else { + Py_DECREF(pyobj_ret); + ret = 0; + } + + LIBVIRT_RELEASE_THREAD_STATE; + return ret; +} + static PyObject * libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) @@ -5884,6 +5931,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBalloonChangeCallback); break; + case VIR_DOMAIN_EVENT_ID_SUSPEND_DISK: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventSuspendDiskCallback); + break; }
if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 7da4a1e..c7010c8 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -1,7 +1,7 @@ /* * domain_event.c: domain event queue processing helpers * - * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010-2012 Red Hat, Inc. * Copyright (C) 2008 VirtualIron * * This library is free software; you can redistribute it and/or @@ -1107,10 +1107,10 @@ virDomainEventPMSuspendNew(int id, const char *name, virDomainEventPtr ev = virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_PMSUSPEND, id, name, uuid); - return ev; }
+ virDomainEventPtr virDomainEventPMSuspendNewFromObj(virDomainObjPtr obj) {
Remove the whitespace change in this hunk.
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 54b3a99..f4dc94f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -136,6 +136,8 @@ struct _qemuMonitorCallbacks { int (*domainBalloonChange)(qemuMonitorPtr mon, virDomainObjPtr vm, unsigned long long actual); + int (*domainSuspendDisk)(qemuMonitorPtr mon, + virDomainObjPtr vm); };
char *qemuMonitorEscapeArg(const char *in); @@ -213,6 +215,7 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, int status); int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, unsigned long long actual); +int qemuMonitorEmitSuspendDisk(qemuMonitorPtr mon);
int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index bd52ce4..d0fd23a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -70,6 +70,7 @@ static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr d static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data);
typedef struct { const char *type; @@ -91,6 +92,7 @@ static qemuEventHandler eventHandlers[] = { { "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, }, { "STOP", qemuMonitorJSONHandleStop, }, { "SUSPEND", qemuMonitorJSONHandlePMSuspend, }, + { "SUSPEND_DISK", qemuMonitorJSONHandleSuspendDisk, },
Hm, here it's apparent that the SUSPEND event is called PMSuspend where the SUSPEND_DISK lacks that. Should we unify this?
{ "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, @@ -891,6 +893,13 @@ qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, qemuMonitorEmitBalloonChange(mon, actual); }
+static void +qemuMonitorJSONHandleSuspendDisk(qemuMonitorPtr mon, + virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitSuspendDisk(mon); +} + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str,
ACK when the naming issue is resolved somehow (my opinion is to change it to be uniform) and with the two issues fixed. Peter

On Fri, Oct 12, 2012 at 09:28:04PM +0200, Martin Kletzander wrote:
This patch adds support for SUSPEND_DISK event; both lifecycle and separated. The support is added for QEMU, machines are changed to PMSUSPENDED, but as QEMU sends SHUTDOWN afterwards, the state changes to shut-off. This and much more needs to be done in order for libvirt to work with transient devices, wake-ups etc. This patch is not aiming for that functionality.
I'm not convinced that we actually need a new PMSUSPEND_DISK event callback. The existing callback: /** * virConnectDomainEventPMSuspendCallback: * @conn: connection object * @dom: domain on which the event occurred * @reason: reason why the callback was called, unused currently, * always passes 0 * @opaque: application specified data * * This callback occurs when the guest is waken up. * * The callback signature to use when registering for an event of type * VIR_DOMAIN_EVENT_ID_PMSuspend with virConnectDomainEventRegisterAny() */ typedef void (*virConnectDomainEventPMSuspendCallback)(virConnectPtr conn, virDomainPtr dom, int reason, void *opaque); already has a 'reason' argumnent that is unused, and could be used to distinguish between S3 and S4 (memory vs disk). Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 10/15/2012 12:07 PM, Daniel P. Berrange wrote:
On Fri, Oct 12, 2012 at 09:28:04PM +0200, Martin Kletzander wrote:
This patch adds support for SUSPEND_DISK event; both lifecycle and separated. The support is added for QEMU, machines are changed to PMSUSPENDED, but as QEMU sends SHUTDOWN afterwards, the state changes to shut-off. This and much more needs to be done in order for libvirt to work with transient devices, wake-ups etc. This patch is not aiming for that functionality.
I'm not convinced that we actually need a new PMSUSPEND_DISK event callback. The existing callback:
/** * virConnectDomainEventPMSuspendCallback: * @conn: connection object * @dom: domain on which the event occurred * @reason: reason why the callback was called, unused currently, * always passes 0 * @opaque: application specified data * * This callback occurs when the guest is waken up. * * The callback signature to use when registering for an event of type * VIR_DOMAIN_EVENT_ID_PMSuspend with virConnectDomainEventRegisterAny() */ typedef void (*virConnectDomainEventPMSuspendCallback)(virConnectPtr conn, virDomainPtr dom, int reason, void *opaque);
already has a 'reason' argumnent that is unused, and could be used to distinguish between S3 and S4 (memory vs disk).
Daniel
The problem is that there is only reason field free in the event, but this is not, strictly speaking, a reason, but a type of suspend. The reason could be used to say whether it was suspended y an agent, acpi call, etc., but from what I see elsewhere we don't use it as a type of suspend (disk/memory/hybrid). And as there is no free place in the RPC message for a type of the pmsuspend, I was forced to create a new one. Martin
participants (3)
-
Daniel P. Berrange
-
Martin Kletzander
-
Peter Krempa