The wait command will pause the monitor the command was issued in until a new
event becomes available. Events are queued if there isn't a waiter present.
The wait command completes after a single event is available.
Today, we queue events indefinitely but in the future, I suspect we'll drop
events that are older than a certain amount of time to avoid infinitely
allocating memory for long running VMs.
To make use of the new notification mechanism, this patch introduces a
qemu_notify_event() API. This API takes three parameters: a class which is
meant to classify the type of event being generated, a name which is meant to
distinguish which event in the class that has been generated, and a details
parameters which is meant to allow events to send arbitrary data with a given
event.
v1->v2
- Added a -d flag to poll for events instead of waiting
- Made old events expire after 10 minutes
Signed-off-by: Anthony Liguori <aliguori(a)us.ibm.com>
diff --git a/Makefile b/Makefile
index 633774e..b87870b 100644
--- a/Makefile
+++ b/Makefile
@@ -84,7 +84,7 @@ OBJS+=usb-serial.o usb-net.o
OBJS+=sd.o ssi-sd.o
OBJS+=bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
OBJS+=buffered_file.o migration.o migration-tcp.o net.o qemu-sockets.o
-OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o
+OBJS+=qemu-char.o aio.o net-checksum.o savevm.o cache-utils.o wait.o
ifdef CONFIG_BRLAPI
OBJS+= baum.o
diff --git a/monitor.c b/monitor.c
index ca1c11c..94c14b2 100644
--- a/monitor.c
+++ b/monitor.c
@@ -42,6 +42,7 @@
#include "migration.h"
#include "kvm.h"
#include "acl.h"
+#include "wait.h"
//#define DEBUG
//#define DEBUG_COMPLETION
@@ -1745,6 +1746,8 @@ static const mon_cmd_t mon_cmds[] = {
"acl allow vnc.username fred\n"
"acl deny vnc.username bob\n"
"acl reset vnc.username\n" },
+ { "wait", "-d", do_wait,
+ "[-d]", "wait for an asynchronous event (use -d to poll event)"
},
{ NULL, NULL, },
};
diff --git a/wait.c b/wait.c
new file mode 100644
index 0000000..8f6cbad
--- /dev/null
+++ b/wait.c
@@ -0,0 +1,145 @@
+/*
+ * Asynchronous monitor notification support
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori(a)us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "monitor.h"
+#include "sys-queue.h"
+#include "osdep.h"
+#include "wait.h"
+
+typedef struct WaitEvent
+{
+ qemu_timeval timestamp;
+ char *class;
+ char *name;
+ char *details;
+ TAILQ_ENTRY(WaitEvent) node;
+} WaitEvent;
+
+typedef struct PendingWaiter
+{
+ Monitor *mon;
+ int polling;
+ TAILQ_ENTRY(PendingWaiter) node;
+} PendingWaiter;
+
+/* How long do we hold on to events (in seconds)
+ * default to 10 minutes. */
+#define EVENT_EXPIRATION_AGE (10 * 60)
+
+static TAILQ_HEAD(, WaitEvent) pending_events =
+ TAILQ_HEAD_INITIALIZER(pending_events);
+static TAILQ_HEAD(, PendingWaiter) pending_waiters =
+ TAILQ_HEAD_INITIALIZER(pending_waiters);
+
+static void free_event(WaitEvent *e)
+{
+ qemu_free(e->details);
+ qemu_free(e->name);
+ qemu_free(e->class);
+ qemu_free(e);
+}
+
+static void dispatch_event(PendingWaiter *w, WaitEvent *e)
+{
+ monitor_printf(w->mon, "%ld.%06ld: %s: %s\n",
+ e->timestamp.tv_sec, e->timestamp.tv_usec,
+ e->class, e->name);
+ if (e->details && strlen(e->details)) {
+ monitor_printf(w->mon, "%s\n", e->details);
+ }
+ if (!w->polling)
+ monitor_resume(w->mon);
+}
+
+static void remove_stale_events(void)
+{
+ qemu_timeval now;
+
+ qemu_gettimeofday(&now);
+
+ while (!TAILQ_EMPTY(&pending_events)) {
+ WaitEvent *e;
+
+ e = TAILQ_FIRST(&pending_events);
+ if ((now.tv_sec - e->timestamp.tv_sec) > EVENT_EXPIRATION_AGE) {
+ TAILQ_REMOVE(&pending_events, e, node);
+ free_event(e);
+ } else {
+ break;
+ }
+ }
+}
+
+static int try_to_process_events(void)
+{
+ int processed_events = 0;
+
+ while (!TAILQ_EMPTY(&pending_events) &&
!TAILQ_EMPTY(&pending_waiters)) {
+ WaitEvent *e;
+ PendingWaiter *w;
+
+ e = TAILQ_FIRST(&pending_events);
+ TAILQ_REMOVE(&pending_events, e, node);
+
+ w = TAILQ_FIRST(&pending_waiters);
+ TAILQ_REMOVE(&pending_waiters, w, node);
+
+ dispatch_event(w, e);
+
+ free_event(e);
+ qemu_free(w);
+
+ processed_events = 1;
+ }
+
+ remove_stale_events();
+
+ return processed_events;
+}
+
+void qemu_notify_event(const char *class, const char *name, const char *details)
+{
+ WaitEvent *e;
+
+ e = qemu_mallocz(sizeof(*e));
+
+ qemu_gettimeofday(&e->timestamp);
+ e->class = qemu_strdup(class);
+ e->name = qemu_strdup(name);
+ if (details)
+ e->details = qemu_strdup(details);
+
+ TAILQ_INSERT_TAIL(&pending_events, e, node);
+
+ try_to_process_events();
+}
+
+void do_wait(Monitor *mon, int polling)
+{
+ PendingWaiter *w;
+
+ w = qemu_mallocz(sizeof(*w));
+ w->mon = mon;
+ w->polling = polling;
+
+ TAILQ_INSERT_TAIL(&pending_waiters, w, node);
+
+ if (!w->polling)
+ monitor_suspend(w->mon);
+
+ if (!try_to_process_events() && w->polling) {
+ TAILQ_REMOVE(&pending_waiters, w, node);
+ qemu_free(w);
+ }
+}
diff --git a/wait.h b/wait.h
new file mode 100644
index 0000000..3fb455f
--- /dev/null
+++ b/wait.h
@@ -0,0 +1,23 @@
+/*
+ * Asynchronous monitor notification support
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori(a)us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_WAIT_H
+#define QEMU_WAIT_H
+
+#include "monitor.h"
+
+void qemu_notify_event(const char *class, const char *name,
+ const char *details);
+void do_wait(Monitor *mon, int polling);
+
+#endif