aboutsummaryrefslogtreecommitdiff
path: root/monitor
diff options
context:
space:
mode:
Diffstat (limited to 'monitor')
-rw-r--r--monitor/hmp.c35
-rw-r--r--monitor/monitor-internal.h5
-rw-r--r--monitor/monitor.c72
-rw-r--r--monitor/qmp.c106
4 files changed, 117 insertions, 101 deletions
diff --git a/monitor/hmp.c b/monitor/hmp.c
index 5cab56d..69c1b7e 100644
--- a/monitor/hmp.c
+++ b/monitor/hmp.c
@@ -1401,45 +1401,42 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
static void monitor_event(void *opaque, QEMUChrEvent event)
{
Monitor *mon = opaque;
- MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
switch (event) {
case CHR_EVENT_MUX_IN:
qemu_mutex_lock(&mon->mon_lock);
- mon->mux_out = 0;
- qemu_mutex_unlock(&mon->mon_lock);
- if (mon->reset_seen) {
- readline_restart(hmp_mon->rs);
+ if (mon->mux_out) {
+ mon->mux_out = 0;
monitor_resume(mon);
- monitor_flush(mon);
- } else {
- qatomic_mb_set(&mon->suspend_cnt, 0);
}
+ qemu_mutex_unlock(&mon->mon_lock);
break;
case CHR_EVENT_MUX_OUT:
- if (mon->reset_seen) {
- if (qatomic_mb_read(&mon->suspend_cnt) == 0) {
- monitor_printf(mon, "\n");
+ qemu_mutex_lock(&mon->mon_lock);
+ if (!mon->mux_out) {
+ if (mon->reset_seen && !mon->suspend_cnt) {
+ monitor_puts_locked(mon, "\n");
+ } else {
+ monitor_flush_locked(mon);
}
- monitor_flush(mon);
monitor_suspend(mon);
- } else {
- qatomic_inc(&mon->suspend_cnt);
+ mon->mux_out = 1;
}
- qemu_mutex_lock(&mon->mon_lock);
- mon->mux_out = 1;
qemu_mutex_unlock(&mon->mon_lock);
break;
case CHR_EVENT_OPENED:
monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
"information\n", QEMU_VERSION);
+ qemu_mutex_lock(&mon->mon_lock);
+ mon->reset_seen = 1;
if (!mon->mux_out) {
- readline_restart(hmp_mon->rs);
- readline_show_prompt(hmp_mon->rs);
+ /* Suspend-resume forces the prompt to be printed. */
+ monitor_suspend(mon);
+ monitor_resume(mon);
}
- mon->reset_seen = 1;
+ qemu_mutex_unlock(&mon->mon_lock);
mon_refcount++;
break;
diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
index 53e3808..252de85 100644
--- a/monitor/monitor-internal.h
+++ b/monitor/monitor-internal.h
@@ -94,7 +94,6 @@ typedef struct HMPCommand {
struct Monitor {
CharBackend chr;
- int reset_seen;
int suspend_cnt; /* Needs to be accessed atomically */
bool is_qmp;
bool skip_flush;
@@ -115,8 +114,8 @@ struct Monitor {
QLIST_HEAD(, mon_fd_t) fds;
GString *outbuf;
guint out_watch;
- /* Read under either BQL or mon_lock, written with BQL+mon_lock. */
int mux_out;
+ int reset_seen;
};
struct MonitorHMP {
@@ -166,7 +165,6 @@ typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
extern IOThread *mon_iothread;
extern Coroutine *qmp_dispatcher_co;
extern bool qmp_dispatcher_co_shutdown;
-extern bool qmp_dispatcher_co_busy;
extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
extern QemuMutex monitor_lock;
extern MonitorList mon_list;
@@ -184,6 +182,7 @@ void monitor_fdsets_cleanup(void);
void qmp_send_response(MonitorQMP *mon, const QDict *rsp);
void monitor_data_destroy_qmp(MonitorQMP *mon);
void coroutine_fn monitor_qmp_dispatcher_co(void *data);
+void qmp_dispatcher_co_wake(void);
int get_monitor_def(Monitor *mon, int64_t *pval, const char *name);
void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
diff --git a/monitor/monitor.c b/monitor/monitor.c
index 6025356..dc352f9 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -56,29 +56,11 @@ IOThread *mon_iothread;
/* Coroutine to dispatch the requests received from I/O thread */
Coroutine *qmp_dispatcher_co;
-/* Set to true when the dispatcher coroutine should terminate */
-bool qmp_dispatcher_co_shutdown;
-
/*
- * qmp_dispatcher_co_busy is used for synchronisation between the
- * monitor thread and the main thread to ensure that the dispatcher
- * coroutine never gets scheduled a second time when it's already
- * scheduled (scheduling the same coroutine twice is forbidden).
- *
- * It is true if the coroutine is active and processing requests.
- * Additional requests may then be pushed onto mon->qmp_requests,
- * and @qmp_dispatcher_co_shutdown may be set without further ado.
- * @qmp_dispatcher_co_busy must not be woken up in this case.
- *
- * If false, you also have to set @qmp_dispatcher_co_busy to true and
- * wake up @qmp_dispatcher_co after pushing the new requests.
- *
- * The coroutine will automatically change this variable back to false
- * before it yields. Nobody else may set the variable to false.
- *
- * Access must be atomic for thread safety.
+ * Set to true when the dispatcher coroutine should terminate. Protected
+ * by monitor_lock.
*/
-bool qmp_dispatcher_co_busy;
+bool qmp_dispatcher_co_shutdown;
/*
* Protects mon_list, monitor_qapi_event_state, coroutine_mon,
@@ -154,22 +136,19 @@ static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
return !monitor_uses_readline(container_of(mon, MonitorHMP, common));
}
-static void monitor_flush_locked(Monitor *mon);
-
static gboolean monitor_unblocked(void *do_not_use, GIOCondition cond,
void *opaque)
{
Monitor *mon = opaque;
- qemu_mutex_lock(&mon->mon_lock);
+ QEMU_LOCK_GUARD(&mon->mon_lock);
mon->out_watch = 0;
monitor_flush_locked(mon);
- qemu_mutex_unlock(&mon->mon_lock);
return FALSE;
}
/* Caller must hold mon->mon_lock */
-static void monitor_flush_locked(Monitor *mon)
+void monitor_flush_locked(Monitor *mon)
{
int rc;
size_t len;
@@ -203,18 +182,16 @@ static void monitor_flush_locked(Monitor *mon)
void monitor_flush(Monitor *mon)
{
- qemu_mutex_lock(&mon->mon_lock);
+ QEMU_LOCK_GUARD(&mon->mon_lock);
monitor_flush_locked(mon);
- qemu_mutex_unlock(&mon->mon_lock);
}
/* flush at every end of line */
-int monitor_puts(Monitor *mon, const char *str)
+int monitor_puts_locked(Monitor *mon, const char *str)
{
int i;
char c;
- qemu_mutex_lock(&mon->mon_lock);
for (i = 0; str[i]; i++) {
c = str[i];
if (c == '\n') {
@@ -225,11 +202,16 @@ int monitor_puts(Monitor *mon, const char *str)
monitor_flush_locked(mon);
}
}
- qemu_mutex_unlock(&mon->mon_lock);
return i;
}
+int monitor_puts(Monitor *mon, const char *str)
+{
+ QEMU_LOCK_GUARD(&mon->mon_lock);
+ return monitor_puts_locked(mon, str);
+}
+
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
{
char *buf;
@@ -569,6 +551,17 @@ static void monitor_accept_input(void *opaque)
{
Monitor *mon = opaque;
+ qemu_mutex_lock(&mon->mon_lock);
+ if (!monitor_is_qmp(mon) && mon->reset_seen) {
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+ assert(hmp_mon->rs);
+ readline_restart(hmp_mon->rs);
+ qemu_mutex_unlock(&mon->mon_lock);
+ readline_show_prompt(hmp_mon->rs);
+ } else {
+ qemu_mutex_unlock(&mon->mon_lock);
+ }
+
qemu_chr_fe_accept_input(&mon->chr);
}
@@ -587,12 +580,6 @@ void monitor_resume(Monitor *mon)
ctx = qemu_get_aio_context();
}
- if (!monitor_is_qmp(mon)) {
- MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
- assert(hmp_mon->rs);
- readline_show_prompt(hmp_mon->rs);
- }
-
aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
}
@@ -603,7 +590,7 @@ int monitor_can_read(void *opaque)
{
Monitor *mon = opaque;
- return !qatomic_mb_read(&mon->suspend_cnt);
+ return !qatomic_read(&mon->suspend_cnt);
}
void monitor_list_append(Monitor *mon)
@@ -674,14 +661,14 @@ void monitor_cleanup(void)
* we'll just leave them in the queue without sending a response
* and monitor_data_destroy() will free them.
*/
- qmp_dispatcher_co_shutdown = true;
- if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) {
- aio_co_wake(qmp_dispatcher_co);
+ WITH_QEMU_LOCK_GUARD(&monitor_lock) {
+ qmp_dispatcher_co_shutdown = true;
}
+ qmp_dispatcher_co_wake();
AIO_WAIT_WHILE_UNLOCKED(NULL,
(aio_poll(iohandler_get_aio_context(), false),
- qatomic_mb_read(&qmp_dispatcher_co_busy)));
+ qatomic_read(&qmp_dispatcher_co)));
/*
* We need to explicitly stop the I/O thread (but not destroy it),
@@ -732,7 +719,6 @@ void monitor_init_globals(void)
* rid of those assumptions.
*/
qmp_dispatcher_co = qemu_coroutine_create(monitor_qmp_dispatcher_co, NULL);
- qatomic_mb_set(&qmp_dispatcher_co_busy, true);
aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co);
}
diff --git a/monitor/qmp.c b/monitor/qmp.c
index 092c527..c8e0156 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
@@ -33,6 +33,30 @@
#include "qapi/qmp/qlist.h"
#include "trace.h"
+/*
+ * qmp_dispatcher_co_busy is used for synchronisation between the
+ * monitor thread and the main thread to ensure that the dispatcher
+ * coroutine never gets scheduled a second time when it's already
+ * scheduled (scheduling the same coroutine twice is forbidden).
+ *
+ * It is true if the coroutine will process at least one more request
+ * before going to sleep. Either it has been kicked already, or it
+ * is active and processing requests. Additional requests may therefore
+ * be pushed onto mon->qmp_requests, and @qmp_dispatcher_co_shutdown may
+ * be set without further ado. @qmp_dispatcher_co must not be woken up
+ * in this case.
+ *
+ * If false, you have to wake up @qmp_dispatcher_co after pushing new
+ * requests. You also have to set @qmp_dispatcher_co_busy to true
+ * before waking up the coroutine.
+ *
+ * The coroutine will automatically change this variable back to false
+ * before it yields. Nobody else may set the variable to false.
+ *
+ * Access must be atomic for thread safety.
+ */
+static bool qmp_dispatcher_co_busy = true;
+
struct QMPRequest {
/* Owner of the request */
MonitorQMP *mon;
@@ -178,8 +202,6 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
Monitor *mon;
MonitorQMP *qmp_mon;
- QEMU_LOCK_GUARD(&monitor_lock);
-
QTAILQ_FOREACH(mon, &mon_list, entry) {
if (!monitor_is_qmp(mon)) {
continue;
@@ -207,53 +229,56 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
return req_obj;
}
-void coroutine_fn monitor_qmp_dispatcher_co(void *data)
+static QMPRequest *monitor_qmp_dispatcher_pop_any(void)
{
- QMPRequest *req_obj = NULL;
- QDict *rsp;
- bool oob_enabled;
- MonitorQMP *mon;
-
while (true) {
- assert(qatomic_mb_read(&qmp_dispatcher_co_busy) == true);
+ /*
+ * To avoid double scheduling, busy is true on entry to
+ * monitor_qmp_dispatcher_co(), and must be set again before
+ * aio_co_wake()-ing it.
+ */
+ assert(qatomic_read(&qmp_dispatcher_co_busy) == true);
/*
* Mark the dispatcher as not busy already here so that we
* don't miss any new requests coming in the middle of our
* processing.
+ *
+ * Clear qmp_dispatcher_co_busy before reading request.
*/
qatomic_mb_set(&qmp_dispatcher_co_busy, false);
- /* On shutdown, don't take any more requests from the queue */
- if (qmp_dispatcher_co_shutdown) {
- return;
- }
+ WITH_QEMU_LOCK_GUARD(&monitor_lock) {
+ QMPRequest *req_obj;
- while (!(req_obj = monitor_qmp_requests_pop_any_with_lock())) {
- /*
- * No more requests to process. Wait to be reentered from
- * handle_qmp_command() when it pushes more requests, or
- * from monitor_cleanup() when it requests shutdown.
- */
- if (!qmp_dispatcher_co_shutdown) {
- qemu_coroutine_yield();
-
- /*
- * busy must be set to true again by whoever
- * rescheduled us to avoid double scheduling
- */
- assert(qatomic_xchg(&qmp_dispatcher_co_busy, false) == true);
+ /* On shutdown, don't take any more requests from the queue */
+ if (qmp_dispatcher_co_shutdown) {
+ return NULL;
}
- /*
- * qmp_dispatcher_co_shutdown may have changed if we
- * yielded and were reentered from monitor_cleanup()
- */
- if (qmp_dispatcher_co_shutdown) {
- return;
+ req_obj = monitor_qmp_requests_pop_any_with_lock();
+ if (req_obj) {
+ return req_obj;
}
}
+ /*
+ * No more requests to process. Wait to be reentered from
+ * handle_qmp_command() when it pushes more requests, or
+ * from monitor_cleanup() when it requests shutdown.
+ */
+ qemu_coroutine_yield();
+ }
+}
+
+void coroutine_fn monitor_qmp_dispatcher_co(void *data)
+{
+ QMPRequest *req_obj;
+ QDict *rsp;
+ bool oob_enabled;
+ MonitorQMP *mon;
+
+ while ((req_obj = monitor_qmp_dispatcher_pop_any()) != NULL) {
trace_monitor_qmp_in_band_dequeue(req_obj,
req_obj->mon->qmp_requests->length);
@@ -340,6 +365,17 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data)
aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co);
qemu_coroutine_yield();
}
+ qatomic_set(&qmp_dispatcher_co, NULL);
+}
+
+void qmp_dispatcher_co_wake(void)
+{
+ /* Write request before reading qmp_dispatcher_co_busy. */
+ smp_mb__before_rmw();
+
+ if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) {
+ aio_co_wake(qmp_dispatcher_co);
+ }
}
static void handle_qmp_command(void *opaque, QObject *req, Error *err)
@@ -403,9 +439,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
}
/* Kick the dispatcher routine */
- if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) {
- aio_co_wake(qmp_dispatcher_co);
- }
+ qmp_dispatcher_co_wake();
}
static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)