diff options
Diffstat (limited to 'monitor')
-rw-r--r-- | monitor/hmp.c | 35 | ||||
-rw-r--r-- | monitor/monitor-internal.h | 5 | ||||
-rw-r--r-- | monitor/monitor.c | 72 | ||||
-rw-r--r-- | monitor/qmp.c | 106 |
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) |