[libvirt] [PATCH 0/2] Avoid touching monitor when fetching guest XML or info

The virDomainGetXMLDesc and virDomainGetInfo APIs for QEMU suffer from needing to run 'query-balloon' to update the balloon level. This has caused us performance problems and even worse caused us to lock up on a dead QEMU. The following two patches to QEMU add a BALLOON_EVENT and a query-events command to QMP. With those two parts, we can avoiding running the query-balloon command at all. https://lists.gnu.org/archive/html/qemu-devel/2012-05/msg02215.html qemu upstream commit ID: 973603a813c5d60534b4fa0313f83be40e2b9c47 https://lists.gnu.org/archive/html/qemu-devel/2012-05/msg02255.html qemu upstream commit ID: 4860853d60ecea44b65e9cdefce980de3a641dce

From: "Daniel P. Berrange" <berrange@redhat.com> When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this * include/libvirt/libvirt.h.in: Define the virConnectDomainEventBalloonChangeCallback callback and VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE constant * python/libvirt-override-virConnect.py, python/libvirt-override.c: Wire up helpers for new event * daemon/remote.c: Helper for serializing balloon event * examples/domain-events/events-c/event-test.c, examples/domain-events/events-python/event-test.py: Add example of balloon event usage * src/conf/domain_event.c, src/conf/domain_event.h: Handling of balloon events * src/remote/remote_driver.c: Add handler of balloon events * src/remote/remote_protocol.x: Define wire protocol for balloon events Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 28 +++++++++++ examples/domain-events/events-c/event-test.c | 43 +++++++++++++---- examples/domain-events/events-python/event-test.py | 3 + include/libvirt/libvirt.h.in | 17 +++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 50 ++++++++++++++++++++ src/conf/domain_event.c | 35 ++++++++++++++ src/conf/domain_event.h | 3 + src/libvirt_private.syms | 2 + src/remote/remote_driver.c | 31 ++++++++++++ src/remote/remote_protocol.x | 9 +++- 11 files changed, 218 insertions(+), 12 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 095d854..9334221 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -582,6 +582,33 @@ static int remoteRelayDomainEventPMSuspend(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +remoteRelayDomainEventBalloonChange(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque) +{ + virNetServerClientPtr client = opaque; + remote_domain_event_balloon_change_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain balloon change event %s %d %lld", dom->name, dom->id, actual); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_domain(&data.dom, dom); + data.actual = actual; + + remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE, + (xdrproc_t)xdr_remote_domain_event_balloon_change_msg, &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -596,6 +623,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTrayChange), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMWakeup), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspend), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBalloonChange), }; 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 c54778f..9072281 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -208,16 +208,29 @@ static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, long long offset, void *opaque ATTRIBUTE_UNUSED) { - char *str = NULL; - /* HACK: use asprintf since we have gnulib's wrapper for %lld on Win32 - * but don't have a printf() replacement with %lld */ - if (asprintf(&str, "%s EVENT: Domain %s(%d) rtc change %lld\n", - __func__, virDomainGetName(dom), - virDomainGetID(dom), offset) < 0) - return 0; - - printf("%s", str); - free(str); +#ifdef WIN32 + printf("%s EVENT: Domain %s(%d) rtc change %I64d\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), offset); +#else + printf("%s EVENT: Domain %s(%d) rtc change %lld\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), offset); +#endif + + return 0; +} + +static int myDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque ATTRIBUTE_UNUSED) +{ +#ifdef WIN32 + printf("%s EVENT: Domain %s(%d) balloon change %I64d KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), actual); +#else + printf("%s EVENT: Domain %s(%d) balloon change %lld KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), actual); +#endif return 0; } @@ -391,6 +404,7 @@ int main(int argc, char **argv) int callback10ret = -1; int callback11ret = -1; int callback12ret = -1; + int callback13ret = -1; struct sigaction action_stop; memset(&action_stop, 0, sizeof(action_stop)); @@ -476,6 +490,11 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_PMSUSPEND, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMSuspendCallback), strdup("pmsuspend"), myFreeFunc); + callback13ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventBalloonChangeCallback), + strdup("callback balloonchange"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && (callback3ret != -1) && @@ -486,7 +505,8 @@ int main(int argc, char **argv) (callback9ret != -1) && (callback10ret != -1) && (callback11ret != -1) && - (callback12ret != -1)) { + (callback12ret != -1) && + (callback13ret != -1)) { if (virConnectSetKeepAlive(dconn, 5, 3) < 0) { virErrorPtr err = virGetLastError(); fprintf(stderr, "Failed to start keepalive protocol: %s\n", @@ -514,6 +534,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback10ret); virConnectDomainEventDeregisterAny(dconn, callback11ret); virConnectDomainEventDeregisterAny(dconn, callback12ret); + virConnectDomainEventDeregisterAny(dconn, callback13ret); if (callback8ret != -1) virConnectDomainEventDeregisterAny(dconn, callback8ret); } diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 96dc268..137c31c 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -483,6 +483,8 @@ def myDomainEventPMWakeupCallback(conn, dom, reason, opaque): def myDomainEventPMSuspendCallback(conn, dom, reason, opaque): print "myDomainEventPMSuspendCallback: Domain %s(%s) system pmsuspend" % ( dom.name(), dom.ID()) +def myDomainEventBalloonChangeCallback(conn, dom, utcoffset, actual): + print "myDomainEventBalloonChangeCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), actual) def usage(out=sys.stderr): print >>out, "usage: "+os.path.basename(sys.argv[0])+" [-hdl] [uri]" print >>out, " uri will default to qemu:///system" @@ -544,6 +546,7 @@ def main(): vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_TRAY_CHANGE, myDomainEventTrayChangeCallback, None) 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.setKeepAlive(5, 3) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6e8d5dd..e34438c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3861,6 +3861,22 @@ typedef void (*virConnectDomainEventPMSuspendCallback)(virConnectPtr conn, int reason, void *opaque); + +/** + * virConnectDomainEventBalloonChangeCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @actual: the new balloon level measured in kibibytes(blocks of 1024 bytes) + * @opaque: application specified data + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBalloonChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + unsigned long long actual, + void *opaque); + /** * VIR_DOMAIN_EVENT_CALLBACK: * @@ -3884,6 +3900,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_TRAY_CHANGE = 10, /* virConnectDomainEventTrayChangeCallback */ VIR_DOMAIN_EVENT_ID_PMWAKEUP = 11, /* virConnectDomainEventPMWakeupCallback */ VIR_DOMAIN_EVENT_ID_PMSUSPEND = 12, /* virConnectDomainEventPMSuspendCallback */ + VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE = 13, /* virConnectDomainEventBalloonChangeCallback */ #ifdef VIR_ENUM_SENTINELS /* diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index ecb5680..50177ab 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -161,6 +161,15 @@ cb(self, virDomain(self, _obj=dom), reason, opaque) return 0; + def _dispatchDomainEventBalloonChangeCallback(self, dom, actual, cbData): + """Dispatches events to python user domain balloon change event callbacks + """ + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, virDomain(self, _obj=dom), actual, 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 8ef9fa0..8b41dff 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -5352,6 +5352,53 @@ libvirt_virConnectDomainEventPMSuspendCallback(virConnectPtr conn ATTRIBUTE_UNUS return ret; } +static int +libvirt_virConnectDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + 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*)"_dispatchDomainEventBalloonChangeCallback", + (char*)"OLO", + pyobj_dom, + (PY_LONG_LONG)actual, + 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) @@ -5421,6 +5468,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, case VIR_DOMAIN_EVENT_ID_PMSUSPEND: cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventPMSuspendCallback); break; + case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBalloonChangeCallback); + break; } if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 3cfd940..8517fb4 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -121,6 +121,10 @@ struct _virDomainEvent { char *devAlias; int reason; } trayChange; + struct { + /* In unit of 1024 bytes */ + unsigned long long actual; + } balloonChange; } data; }; @@ -1109,6 +1113,31 @@ virDomainEventPMSuspendNewFromDom(virDomainPtr dom) return virDomainEventPMSuspendNew(dom->id, dom->name, dom->uuid); } +virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, + unsigned long long actual) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, + dom->id, dom->name, dom->uuid); + + if (ev) + ev->data.balloonChange.actual = actual; + + return ev; +} +virDomainEventPtr virDomainEventBalloonChangeNewFromObj(virDomainObjPtr obj, + unsigned long long actual) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, + obj->def->id, obj->def->name, obj->def->uuid); + + if (ev) + ev->data.balloonChange.actual = actual; + + return ev; +} + /** * virDomainEventQueuePush: * @evtQueue: the dom event queue @@ -1247,6 +1276,12 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, ((virConnectDomainEventPMSuspendCallback)cb)(conn, dom, 0, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: + ((virConnectDomainEventBalloonChangeCallback)cb)(conn, dom, + event->data.balloonChange.actual, + 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 d381aec..1274751 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -125,6 +125,9 @@ virDomainEventPtr virDomainEventPMWakeupNewFromDom(virDomainPtr dom); virDomainEventPtr virDomainEventPMSuspendNewFromObj(virDomainObjPtr obj); virDomainEventPtr virDomainEventPMSuspendNewFromDom(virDomainPtr dom); +virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, unsigned long long actual); +virDomainEventPtr virDomainEventBalloonChangeNewFromObj(virDomainObjPtr obj, unsigned long long actual); + void virDomainEventFree(virDomainEventPtr event); void virDomainEventStateFree(virDomainEventStatePtr state); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b173590..7373281 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -500,6 +500,8 @@ virDomainWatchdogModelTypeToString; # domain_event.h +virDomainEventBalloonChangeNewFromDom; +virDomainEventBalloonChangeNewFromObj; virDomainEventBlockJobNewFromObj; virDomainEventBlockJobNewFromDom; virDomainEventControlErrorNewFromDom; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index eac50e6..3314f80 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -253,6 +253,10 @@ static void remoteDomainBuildEventPMSuspend(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, @@ -307,6 +311,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventPMSuspend, sizeof(remote_domain_event_pmsuspend_msg), (xdrproc_t)xdr_remote_domain_event_pmsuspend_msg }, + { REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE, + remoteDomainBuildEventBalloonChange, + sizeof(remote_domain_event_balloon_change_msg), + (xdrproc_t)xdr_remote_domain_event_balloon_change_msg }, }; enum virDrvOpenRemoteFlags { @@ -3889,6 +3897,29 @@ remoteDomainBuildEventPMSuspend(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, remoteDomainEventQueue(priv, event); } + +static void +remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_balloon_change_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventBalloonChangeNewFromDom(dom, msg->actual); + 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 1da9f3e..91ea9c5 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2192,6 +2192,11 @@ struct remote_domain_event_pmsuspend_msg { remote_nonnull_domain dom; }; +struct remote_domain_event_balloon_change_msg { + remote_nonnull_domain dom; + unsigned hyper actual; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; @@ -2838,7 +2843,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_HAS_METADATA = 272, /* autogen autogen */ REMOTE_PROC_CONNECT_LIST_ALL_DOMAINS = 273, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275 /* skipgen skipgen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ + + REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? -- 1.7.7.5

On 07/12/2012 09:45 AM, Guannan Ren wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this
* include/libvirt/libvirt.h.in: Define the virConnectDomainEventBalloonChangeCallback callback and VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE constant * python/libvirt-override-virConnect.py, python/libvirt-override.c: Wire up helpers for new event * daemon/remote.c: Helper for serializing balloon event * examples/domain-events/events-c/event-test.c, examples/domain-events/events-python/event-test.py: Add example of balloon event usage * src/conf/domain_event.c, src/conf/domain_event.h: Handling of balloon events * src/remote/remote_driver.c: Add handler of balloon events * src/remote/remote_protocol.x: Define wire protocol for balloon events Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+++ b/examples/domain-events/events-c/event-test.c @@ -208,16 +208,29 @@ static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, long long offset, void *opaque ATTRIBUTE_UNUSED) { - char *str = NULL; - /* HACK: use asprintf since we have gnulib's wrapper for %lld on Win32 - * but don't have a printf() replacement with %lld */ - if (asprintf(&str, "%s EVENT: Domain %s(%d) rtc change %lld\n", - __func__, virDomainGetName(dom), - virDomainGetID(dom), offset) < 0) - return 0; - - printf("%s", str); - free(str); +#ifdef WIN32 + printf("%s EVENT: Domain %s(%d) rtc change %I64d\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), offset); +#else + printf("%s EVENT: Domain %s(%d) rtc change %lld\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), offset); +#endif
This hunk has nothing to do with the new code, and I detest the use of %I64d.
+static int myDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque ATTRIBUTE_UNUSED) +{ +#ifdef WIN32 + printf("%s EVENT: Domain %s(%d) balloon change %I64d KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), actual); +#else + printf("%s EVENT: Domain %s(%d) balloon change %lld KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), actual); +#endif
Here, you're repeating the same idiom. And on a pedantic point, %lld is for 'signed long long', but 'actual' is 'unsigned long long'. Can we instead just rely on <inttypes.h> and PRIuMAX here (and PRIdMAX earlier), along with a cast to [u]intmax_t? Mingw has a working <stdint.h>, so that would get rid of our #ifdef, as well as avoid the asprintf() hack as well as the ugly %I64d format string.
@@ -2838,7 +2843,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_HAS_METADATA = 272, /* autogen autogen */ REMOTE_PROC_CONNECT_LIST_ALL_DOMAINS = 273, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275 /* skipgen skipgen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ + + REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276 /* autogen autogen */
Spurious blank line - we only group at sets of 10, not 5. Other than that, this looks fine to me. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Thu, Jul 12, 2012 at 02:33:59PM -0600, Eric Blake wrote:
On 07/12/2012 09:45 AM, Guannan Ren wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this
* include/libvirt/libvirt.h.in: Define the virConnectDomainEventBalloonChangeCallback callback and VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE constant * python/libvirt-override-virConnect.py, python/libvirt-override.c: Wire up helpers for new event * daemon/remote.c: Helper for serializing balloon event * examples/domain-events/events-c/event-test.c, examples/domain-events/events-python/event-test.py: Add example of balloon event usage * src/conf/domain_event.c, src/conf/domain_event.h: Handling of balloon events * src/remote/remote_driver.c: Add handler of balloon events * src/remote/remote_protocol.x: Define wire protocol for balloon events Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
+++ b/examples/domain-events/events-c/event-test.c @@ -208,16 +208,29 @@ static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, long long offset, void *opaque ATTRIBUTE_UNUSED) { - char *str = NULL; - /* HACK: use asprintf since we have gnulib's wrapper for %lld on Win32 - * but don't have a printf() replacement with %lld */ - if (asprintf(&str, "%s EVENT: Domain %s(%d) rtc change %lld\n", - __func__, virDomainGetName(dom), - virDomainGetID(dom), offset) < 0) - return 0; - - printf("%s", str); - free(str); +#ifdef WIN32 + printf("%s EVENT: Domain %s(%d) rtc change %I64d\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), offset); +#else + printf("%s EVENT: Domain %s(%d) rtc change %lld\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), offset); +#endif
This hunk has nothing to do with the new code, and I detest the use of %I64d.
+static int myDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque ATTRIBUTE_UNUSED) +{ +#ifdef WIN32 + printf("%s EVENT: Domain %s(%d) balloon change %I64d KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), actual); +#else + printf("%s EVENT: Domain %s(%d) balloon change %lld KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), actual); +#endif
Here, you're repeating the same idiom. And on a pedantic point, %lld is for 'signed long long', but 'actual' is 'unsigned long long'. Can we instead just rely on <inttypes.h> and PRIuMAX here (and PRIdMAX earlier), along with a cast to [u]intmax_t? Mingw has a working <stdint.h>, so that would get rid of our #ifdef, as well as avoid the asprintf() hack as well as the ugly %I64d format string.
IMHO this part of the patch should be removed - what was there already is better. 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 07/12/2012 09:45 AM, Guannan Ren wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this
daemon/remote.c | 28 +++++++++++ examples/domain-events/events-c/event-test.c | 43 +++++++++++++---- examples/domain-events/events-python/event-test.py | 3 + include/libvirt/libvirt.h.in | 17 +++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 50 ++++++++++++++++++++ src/conf/domain_event.c | 35 ++++++++++++++ src/conf/domain_event.h | 3 + src/libvirt_private.syms | 2 + src/remote/remote_driver.c | 31 ++++++++++++ src/remote/remote_protocol.x | 9 +++-
Oh, one other thing - you'll need to regenerate src/remote_protocol-structs. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> If QEMU supports the BALLOON_EVENT QMP event, then we can avoid invoking 'query-balloon' when returning XML or the domain info. * src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: Add QEMU_CAPS_BALLOON_EVENT * src/qemu/qemu_driver.c: Skip query-balloon in qemudDomainGetInfo and qemuDomainGetXMLDesc if we have QEMU_CAPS_BALLOON_EVENT set * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Check for BALLOON_EVENT at connect to monitor. Add callback for balloon change notifications * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: Add handling of BALLOON_EVENT and impl 'query-events' check Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/qemu/qemu_capabilities.c | 1 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_driver.c | 7 ++++- src/qemu/qemu_monitor.c | 18 +++++++++++- src/qemu/qemu_monitor.h | 5 +++ src/qemu/qemu_monitor_json.c | 65 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 2 + src/qemu/qemu_process.c | 31 ++++++++++++++++++++ 8 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 9e4d927..d438c06 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -167,6 +167,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "dump-guest-memory", "nec-usb-xhci", "virtio-s390", + "balloon-event", ); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 9b5ff30..6526ea8 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -134,6 +134,7 @@ enum qemuCapsFlags { QEMU_CAPS_DUMP_GUEST_MEMORY = 96, /* dump-guest-memory command */ QEMU_CAPS_NEC_USB_XHCI = 97, /* -device nec-usb-xhci */ QEMU_CAPS_VIRTIO_S390 = 98, /* -device virtio-*-s390 */ + QEMU_CAPS_BALLOON_EVENT = 99, /* Async event for balloon changes */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dc04d13..29ebb4c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2234,6 +2234,8 @@ static int qemudDomainGetInfo(virDomainPtr dom, if ((vm->def->memballoon != NULL) && (vm->def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_NONE)) { info->memory = vm->def->mem.max_balloon; + } else if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_BALLOON_EVENT)) { + info->memory = vm->def->mem.cur_balloon; } else if (qemuDomainJobAllowed(priv, QEMU_JOB_QUERY)) { if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) goto cleanup; @@ -4560,6 +4562,7 @@ static char *qemuDomainGetXMLDesc(virDomainPtr dom, char *ret = NULL; unsigned long long balloon; int err = 0; + qemuDomainObjPrivatePtr priv; /* Flags checked by virDomainDefFormat */ @@ -4574,11 +4577,13 @@ static char *qemuDomainGetXMLDesc(virDomainPtr dom, goto cleanup; } + priv = vm->privateData; + /* Refresh current memory based on balloon info if supported */ if ((vm->def->memballoon != NULL) && (vm->def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE) && + !qemuCapsGet(priv->qemuCaps, QEMU_CAPS_BALLOON_EVENT) && (virDomainObjIsActive(vm))) { - qemuDomainObjPrivatePtr priv = vm->privateData; /* Don't delay if someone's using the monitor, just use * existing most recent data instead */ if (qemuDomainJobAllowed(priv, QEMU_JOB_QUERY)) { diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b8a2f2f..42a5d6c 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1086,6 +1086,16 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, } +int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, + unsigned long long actual) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, ret, domainBalloonChange, mon->vm, actual); + return ret; +} + int qemuMonitorSetCapabilities(qemuMonitorPtr mon, virBitmapPtr qemuCaps) @@ -1102,11 +1112,17 @@ int qemuMonitorSetCapabilities(qemuMonitorPtr mon, if (mon->json) { ret = qemuMonitorJSONSetCapabilities(mon); - if (ret) + if (ret < 0) goto cleanup; ret = qemuMonitorJSONCheckCommands(mon, qemuCaps, &json_hmp); + if (ret < 0) + goto cleanup; mon->json_hmp = json_hmp > 0; + + ret = qemuMonitorJSONCheckEvents(mon, qemuCaps); + if (ret < 0) + goto cleanup; } else { ret = 0; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 66bec38..c96282b 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -133,6 +133,9 @@ struct _qemuMonitorCallbacks { virDomainObjPtr vm); int (*domainPMSuspend)(qemuMonitorPtr mon, virDomainObjPtr vm); + int (*domainBalloonChange)(qemuMonitorPtr mon, + virDomainObjPtr vm, + unsigned long long actual); }; char *qemuMonitorEscapeArg(const char *in); @@ -208,6 +211,8 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, const char *diskAlias, int type, int status); +int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, + unsigned long long actual); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 7317c42..a99bf60 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -69,6 +69,7 @@ static void qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon, virJSONValuePtr da static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -76,6 +77,7 @@ typedef struct { } qemuEventHandler; static qemuEventHandler eventHandlers[] = { + { "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, }, { "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, }, { "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, }, { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, }, @@ -875,6 +877,19 @@ qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, VIR_DOMAIN_BLOCK_JOB_CANCELED); } +static void +qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, + virJSONValuePtr data) +{ + unsigned long long actual = 0; + if (virJSONValueObjectGetNumberUlong(data, "actual", &actual) < 0) { + VIR_WARN("missing actual in balloon change event"); + return; + } + actual = VIR_DIV_UP(actual, 1024); + qemuMonitorEmitBalloonChange(mon, actual); +} + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, @@ -1004,6 +1019,56 @@ cleanup: int +qemuMonitorJSONCheckEvents(qemuMonitorPtr mon, + virBitmapPtr qemuCaps) +{ + int ret = -1; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-events", NULL); + virJSONValuePtr reply = NULL; + virJSONValuePtr data; + int i, n; + + if (!cmd) + return ret; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { + ret = 0; + goto cleanup; + } + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + if (!(data = virJSONValueObjectGet(reply, "return")) || + data->type != VIR_JSON_TYPE_ARRAY || + (n = virJSONValueArraySize(data)) <= 0) + goto cleanup; + + for (i = 0; i < n; i++) { + virJSONValuePtr entry; + const char *name; + + if (!(entry = virJSONValueArrayGet(data, i)) || + !(name = virJSONValueObjectGetString(entry, "name"))) + goto cleanup; + + if (STREQ(name, "BALLOON_CHANGE")) + qemuCapsSet(qemuCaps, QEMU_CAPS_BALLOON_EVENT); + } + + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, virConnectPtr conn ATTRIBUTE_UNUSED) { diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index e8bd9b8..5293895 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -45,6 +45,8 @@ int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon); int qemuMonitorJSONCheckCommands(qemuMonitorPtr mon, virBitmapPtr qemuCaps, int *json_hmp); +int qemuMonitorJSONCheckEvents(qemuMonitorPtr mon, + virBitmapPtr qemuCaps); int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index bf32487..c120517 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1148,6 +1148,36 @@ qemuProcessHandlePMSuspend(qemuMonitorPtr mon ATTRIBUTE_UNUSED, return 0; } +static int +qemuProcessHandleBalloonChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + unsigned long long actual) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + + virDomainObjLock(vm); + event = virDomainEventBalloonChangeNewFromObj(vm, actual); + + VIR_DEBUG("Updating balloon from %lld to %lld kb", + vm->def->mem.cur_balloon, actual); + vm->def->mem.cur_balloon = actual; + + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + VIR_WARN("unable to save domain status with balloon change"); + + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .destroy = qemuProcessHandleMonitorDestroy, .eofNotify = qemuProcessHandleMonitorEOF, @@ -1164,6 +1194,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainTrayChange = qemuProcessHandleTrayChange, .domainPMWakeup = qemuProcessHandlePMWakeup, .domainPMSuspend = qemuProcessHandlePMSuspend, + .domainBalloonChange = qemuProcessHandleBalloonChange, }; static int -- 1.7.7.5

On 07/12/2012 09:45 AM, Guannan Ren wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
If QEMU supports the BALLOON_EVENT QMP event, then we can avoid invoking 'query-balloon' when returning XML or the domain info.
ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Thu, Jul 12, 2012 at 11:45:55PM +0800, Guannan Ren wrote:
The virDomainGetXMLDesc and virDomainGetInfo APIs for QEMU suffer from needing to run 'query-balloon' to update the balloon level. This has caused us performance problems and even worse caused us to lock up on a dead QEMU.
The following two patches to QEMU add a BALLOON_EVENT and a query-events command to QMP. With those two parts, we can avoiding running the query-balloon command at all.
Thanks for re-posting these patches - but in the future, if you are going to take over someone else's patches it would be a good idea to contact them beforehand to let them know - this would save possible duplicated work. Obviously these patches get my ACK, but someone else needs to review them really :-) 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 07/13/2012 01:01 AM, Daniel P. Berrange wrote:
The virDomainGetXMLDesc and virDomainGetInfo APIs for QEMU suffer from needing to run 'query-balloon' to update the balloon level. This has caused us performance problems and even worse caused us to lock up on a dead QEMU.
The following two patches to QEMU add a BALLOON_EVENT and a query-events command to QMP. With those two parts, we can avoiding running the query-balloon command at all. Thanks for re-posting these patches - but in the future, if you are going to take over someone else's patches it would be a good idea to contact
On Thu, Jul 12, 2012 at 11:45:55PM +0800, Guannan Ren wrote: them beforehand to let them know - this would save possible duplicated work.
Get it. Guannan Ren

From: "Daniel P. Berrange" <berrange@redhat.com> When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this * include/libvirt/libvirt.h.in: Define the virConnectDomainEventBalloonChangeCallback callback and VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE constant * python/libvirt-override-virConnect.py, python/libvirt-override.c: Wire up helpers for new event * daemon/remote.c: Helper for serializing balloon event * examples/domain-events/events-c/event-test.c, examples/domain-events/events-python/event-test.py: Add example of balloon event usage * src/conf/domain_event.c, src/conf/domain_event.h: Handling of balloon events * src/remote/remote_driver.c: Add handler of balloon events * src/remote/remote_protocol.x: Define wire protocol for balloon events * src/remote_protocol-structs: Likewise. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 28 +++++++++++ examples/domain-events/events-c/event-test.c | 22 ++++++++- examples/domain-events/events-python/event-test.py | 3 + include/libvirt/libvirt.h.in | 17 +++++++ python/libvirt-override-virConnect.py | 9 ++++ python/libvirt-override.c | 50 ++++++++++++++++++++ src/conf/domain_event.c | 35 ++++++++++++++ src/conf/domain_event.h | 3 + src/libvirt_private.syms | 2 + src/remote/remote_driver.c | 31 ++++++++++++ src/remote/remote_protocol.x | 8 +++- src/remote_protocol-structs | 5 ++ 12 files changed, 211 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 095d854..9334221 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -582,6 +582,33 @@ static int remoteRelayDomainEventPMSuspend(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +remoteRelayDomainEventBalloonChange(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque) +{ + virNetServerClientPtr client = opaque; + remote_domain_event_balloon_change_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain balloon change event %s %d %lld", dom->name, dom->id, actual); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_domain(&data.dom, dom); + data.actual = actual; + + remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE, + (xdrproc_t)xdr_remote_domain_event_balloon_change_msg, &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -596,6 +623,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTrayChange), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMWakeup), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventPMSuspend), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBalloonChange), }; 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 c54778f..ef6e77a 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -4,6 +4,7 @@ #include <stdlib.h> #include <string.h> #include <signal.h> +#include <inttypes.h> #include <libvirt/libvirt.h> #include <libvirt/virterror.h> @@ -222,6 +223,17 @@ static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) balloon change %" PRIuMAX "KB\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), (uintmax_t)actual); + + return 0; +} + static int myDomainEventWatchdogCallback(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int action, @@ -391,6 +403,7 @@ int main(int argc, char **argv) int callback10ret = -1; int callback11ret = -1; int callback12ret = -1; + int callback13ret = -1; struct sigaction action_stop; memset(&action_stop, 0, sizeof(action_stop)); @@ -476,6 +489,11 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_PMSUSPEND, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMSuspendCallback), strdup("pmsuspend"), myFreeFunc); + callback13ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventBalloonChangeCallback), + strdup("callback balloonchange"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && (callback3ret != -1) && @@ -486,7 +504,8 @@ int main(int argc, char **argv) (callback9ret != -1) && (callback10ret != -1) && (callback11ret != -1) && - (callback12ret != -1)) { + (callback12ret != -1) && + (callback13ret != -1)) { if (virConnectSetKeepAlive(dconn, 5, 3) < 0) { virErrorPtr err = virGetLastError(); fprintf(stderr, "Failed to start keepalive protocol: %s\n", @@ -514,6 +533,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback10ret); virConnectDomainEventDeregisterAny(dconn, callback11ret); virConnectDomainEventDeregisterAny(dconn, callback12ret); + virConnectDomainEventDeregisterAny(dconn, callback13ret); if (callback8ret != -1) virConnectDomainEventDeregisterAny(dconn, callback8ret); } diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 96dc268..137c31c 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -483,6 +483,8 @@ def myDomainEventPMWakeupCallback(conn, dom, reason, opaque): def myDomainEventPMSuspendCallback(conn, dom, reason, opaque): print "myDomainEventPMSuspendCallback: Domain %s(%s) system pmsuspend" % ( dom.name(), dom.ID()) +def myDomainEventBalloonChangeCallback(conn, dom, utcoffset, actual): + print "myDomainEventBalloonChangeCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), actual) def usage(out=sys.stderr): print >>out, "usage: "+os.path.basename(sys.argv[0])+" [-hdl] [uri]" print >>out, " uri will default to qemu:///system" @@ -544,6 +546,7 @@ def main(): vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_TRAY_CHANGE, myDomainEventTrayChangeCallback, None) 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.setKeepAlive(5, 3) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6e8d5dd..e34438c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3861,6 +3861,22 @@ typedef void (*virConnectDomainEventPMSuspendCallback)(virConnectPtr conn, int reason, void *opaque); + +/** + * virConnectDomainEventBalloonChangeCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @actual: the new balloon level measured in kibibytes(blocks of 1024 bytes) + * @opaque: application specified data + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBalloonChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + unsigned long long actual, + void *opaque); + /** * VIR_DOMAIN_EVENT_CALLBACK: * @@ -3884,6 +3900,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_TRAY_CHANGE = 10, /* virConnectDomainEventTrayChangeCallback */ VIR_DOMAIN_EVENT_ID_PMWAKEUP = 11, /* virConnectDomainEventPMWakeupCallback */ VIR_DOMAIN_EVENT_ID_PMSUSPEND = 12, /* virConnectDomainEventPMSuspendCallback */ + VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE = 13, /* virConnectDomainEventBalloonChangeCallback */ #ifdef VIR_ENUM_SENTINELS /* diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index ecb5680..50177ab 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -161,6 +161,15 @@ cb(self, virDomain(self, _obj=dom), reason, opaque) return 0; + def _dispatchDomainEventBalloonChangeCallback(self, dom, actual, cbData): + """Dispatches events to python user domain balloon change event callbacks + """ + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, virDomain(self, _obj=dom), actual, 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 8ef9fa0..8b41dff 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -5352,6 +5352,53 @@ libvirt_virConnectDomainEventPMSuspendCallback(virConnectPtr conn ATTRIBUTE_UNUS return ret; } +static int +libvirt_virConnectDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + 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*)"_dispatchDomainEventBalloonChangeCallback", + (char*)"OLO", + pyobj_dom, + (PY_LONG_LONG)actual, + 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) @@ -5421,6 +5468,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self, case VIR_DOMAIN_EVENT_ID_PMSUSPEND: cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventPMSuspendCallback); break; + case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBalloonChangeCallback); + break; } if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 3cfd940..8517fb4 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -121,6 +121,10 @@ struct _virDomainEvent { char *devAlias; int reason; } trayChange; + struct { + /* In unit of 1024 bytes */ + unsigned long long actual; + } balloonChange; } data; }; @@ -1109,6 +1113,31 @@ virDomainEventPMSuspendNewFromDom(virDomainPtr dom) return virDomainEventPMSuspendNew(dom->id, dom->name, dom->uuid); } +virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, + unsigned long long actual) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, + dom->id, dom->name, dom->uuid); + + if (ev) + ev->data.balloonChange.actual = actual; + + return ev; +} +virDomainEventPtr virDomainEventBalloonChangeNewFromObj(virDomainObjPtr obj, + unsigned long long actual) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, + obj->def->id, obj->def->name, obj->def->uuid); + + if (ev) + ev->data.balloonChange.actual = actual; + + return ev; +} + /** * virDomainEventQueuePush: * @evtQueue: the dom event queue @@ -1247,6 +1276,12 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, ((virConnectDomainEventPMSuspendCallback)cb)(conn, dom, 0, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: + ((virConnectDomainEventBalloonChangeCallback)cb)(conn, dom, + event->data.balloonChange.actual, + 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 d381aec..1274751 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -125,6 +125,9 @@ virDomainEventPtr virDomainEventPMWakeupNewFromDom(virDomainPtr dom); virDomainEventPtr virDomainEventPMSuspendNewFromObj(virDomainObjPtr obj); virDomainEventPtr virDomainEventPMSuspendNewFromDom(virDomainPtr dom); +virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, unsigned long long actual); +virDomainEventPtr virDomainEventBalloonChangeNewFromObj(virDomainObjPtr obj, unsigned long long actual); + void virDomainEventFree(virDomainEventPtr event); void virDomainEventStateFree(virDomainEventStatePtr state); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b173590..7373281 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -500,6 +500,8 @@ virDomainWatchdogModelTypeToString; # domain_event.h +virDomainEventBalloonChangeNewFromDom; +virDomainEventBalloonChangeNewFromObj; virDomainEventBlockJobNewFromObj; virDomainEventBlockJobNewFromDom; virDomainEventControlErrorNewFromDom; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index eac50e6..3314f80 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -253,6 +253,10 @@ static void remoteDomainBuildEventPMSuspend(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, @@ -307,6 +311,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventPMSuspend, sizeof(remote_domain_event_pmsuspend_msg), (xdrproc_t)xdr_remote_domain_event_pmsuspend_msg }, + { REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE, + remoteDomainBuildEventBalloonChange, + sizeof(remote_domain_event_balloon_change_msg), + (xdrproc_t)xdr_remote_domain_event_balloon_change_msg }, }; enum virDrvOpenRemoteFlags { @@ -3889,6 +3897,29 @@ remoteDomainBuildEventPMSuspend(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, remoteDomainEventQueue(priv, event); } + +static void +remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_balloon_change_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventBalloonChangeNewFromDom(dom, msg->actual); + 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 1da9f3e..8f1d9b5 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2192,6 +2192,11 @@ struct remote_domain_event_pmsuspend_msg { remote_nonnull_domain dom; }; +struct remote_domain_event_balloon_change_msg { + remote_nonnull_domain dom; + unsigned hyper actual; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; @@ -2838,7 +2843,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_HAS_METADATA = 272, /* autogen autogen */ REMOTE_PROC_CONNECT_LIST_ALL_DOMAINS = 273, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275 /* skipgen skipgen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ + REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276 /* 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 b667527..511284c 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1661,6 +1661,10 @@ struct remote_domain_event_pmwakeup_msg { struct remote_domain_event_pmsuspend_msg { remote_nonnull_domain dom; }; +struct remote_domain_event_balloon_change_msg { + remote_nonnull_domain dom; + uint64_t actual; +}; struct remote_domain_managed_save_args { remote_nonnull_domain dom; u_int flags; @@ -2246,4 +2250,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_DOMAINS = 273, REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, + REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, }; -- 1.7.7.5

On 07/13/2012 03:05 AM, Guannan Ren wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this
* include/libvirt/libvirt.h.in: Define the virConnectDomainEventBalloonChangeCallback callback and VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE constant * python/libvirt-override-virConnect.py, python/libvirt-override.c: Wire up helpers for new event * daemon/remote.c: Helper for serializing balloon event * examples/domain-events/events-c/event-test.c, examples/domain-events/events-python/event-test.py: Add example of balloon event usage * src/conf/domain_event.c, src/conf/domain_event.h: Handling of balloon events * src/remote/remote_driver.c: Add handler of balloon events * src/remote/remote_protocol.x: Define wire protocol for balloon events * src/remote_protocol-structs: Likewise. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Typically, once one person has issued a Signed-off-by, and you make further edits, then you should also add a signed-off-by, to show that you contributed as well ('git commit --amend -s' can help). But libvirt hasn't had any strict policy on s-o-b, so I don't really care too much. The changes to examples and remote_protocol-structs look better, and the rest of the patch had no comments from me in v1, so: ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 07/13/2012 10:06 PM, Eric Blake wrote:
On 07/13/2012 03:05 AM, Guannan Ren wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
When the guest changes its memory balloon applications may want to know what the new value is, without having to periodically poll on XML / domain info. Introduce a "balloon change" event to let apps see this
* include/libvirt/libvirt.h.in: Define the virConnectDomainEventBalloonChangeCallback callback and VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE constant * python/libvirt-override-virConnect.py, python/libvirt-override.c: Wire up helpers for new event * daemon/remote.c: Helper for serializing balloon event * examples/domain-events/events-c/event-test.c, examples/domain-events/events-python/event-test.py: Add example of balloon event usage * src/conf/domain_event.c, src/conf/domain_event.h: Handling of balloon events * src/remote/remote_driver.c: Add handler of balloon events * src/remote/remote_protocol.x: Define wire protocol for balloon events * src/remote_protocol-structs: Likewise. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Typically, once one person has issued a Signed-off-by, and you make further edits, then you should also add a signed-off-by, to show that you contributed as well ('git commit --amend -s' can help). But libvirt hasn't had any strict policy on s-o-b, so I don't really care too much.
The changes to examples and remote_protocol-structs look better, and the rest of the patch had no comments from me in v1, so:
ACK.
I think I should follow Signed-off-by policy, it makes more sense. These two patches are pushed. Thanks. Guannan
participants (3)
-
Daniel P. Berrange
-
Eric Blake
-
Guannan Ren