diff options
Diffstat (limited to 'monitor')
| -rw-r--r-- | monitor/hmp-cmds.c | 4 | ||||
| -rw-r--r-- | monitor/hmp.c | 44 | ||||
| -rw-r--r-- | monitor/misc.c | 38 | ||||
| -rw-r--r-- | monitor/monitor-internal.h | 7 | ||||
| -rw-r--r-- | monitor/monitor.c | 101 | ||||
| -rw-r--r-- | monitor/qmp-cmds-control.c | 2 | ||||
| -rw-r--r-- | monitor/qmp-cmds.c | 2 | ||||
| -rw-r--r-- | monitor/qmp.c | 131 |
8 files changed, 247 insertions, 82 deletions
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index dc0de39219..9789f4277f 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -998,7 +998,7 @@ void hmp_cpu(Monitor *mon, const QDict *qdict) /* XXX: drop the monitor_set_cpu() usage when all HMP commands that use it are converted to the QAPI */ cpu_index = qdict_get_int(qdict, "index"); - if (monitor_set_cpu(cpu_index) < 0) { + if (monitor_set_cpu(mon, cpu_index) < 0) { monitor_printf(mon, "invalid CPU index\n"); } } @@ -1009,7 +1009,7 @@ void hmp_memsave(Monitor *mon, const QDict *qdict) const char *filename = qdict_get_str(qdict, "filename"); uint64_t addr = qdict_get_int(qdict, "val"); Error *err = NULL; - int cpu_index = monitor_get_cpu_index(); + int cpu_index = monitor_get_cpu_index(mon); if (cpu_index < 0) { monitor_printf(mon, "No CPU available\n"); diff --git a/monitor/hmp.c b/monitor/hmp.c index 4ecdefd705..c5cd9d372b 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -1056,6 +1056,21 @@ fail: return NULL; } +typedef struct HandleHmpCommandCo { + Monitor *mon; + const HMPCommand *cmd; + QDict *qdict; + bool done; +} HandleHmpCommandCo; + +static void handle_hmp_command_co(void *opaque) +{ + HandleHmpCommandCo *data = opaque; + data->cmd->cmd(data->mon, data->qdict); + monitor_set_cur(qemu_coroutine_self(), NULL); + data->done = true; +} + void handle_hmp_command(MonitorHMP *mon, const char *cmdline) { QDict *qdict; @@ -1079,7 +1094,24 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) return; } - cmd->cmd(&mon->common, qdict); + if (!cmd->coroutine) { + /* old_mon is non-NULL when called from qmp_human_monitor_command() */ + Monitor *old_mon = monitor_set_cur(qemu_coroutine_self(), &mon->common); + cmd->cmd(&mon->common, qdict); + monitor_set_cur(qemu_coroutine_self(), old_mon); + } else { + HandleHmpCommandCo data = { + .mon = &mon->common, + .cmd = cmd, + .qdict = qdict, + .done = false, + }; + Coroutine *co = qemu_coroutine_create(handle_hmp_command_co, &data); + monitor_set_cur(co, &mon->common); + aio_co_enter(qemu_get_aio_context(), co); + AIO_WAIT_WHILE(qemu_get_aio_context(), !data.done); + } + qobject_unref(qdict); } @@ -1300,26 +1332,20 @@ cleanup: static void monitor_read(void *opaque, const uint8_t *buf, int size) { - MonitorHMP *mon; - Monitor *old_mon = cur_mon; + MonitorHMP *mon = container_of(opaque, MonitorHMP, common); int i; - cur_mon = opaque; - mon = container_of(cur_mon, MonitorHMP, common); - if (mon->rs) { for (i = 0; i < size; i++) { readline_handle_byte(mon->rs, buf[i]); } } else { if (size == 0 || buf[size - 1] != 0) { - monitor_printf(cur_mon, "corrupted command\n"); + monitor_printf(&mon->common, "corrupted command\n"); } else { handle_hmp_command(mon, (char *)buf); } } - - cur_mon = old_mon; } static void monitor_event(void *opaque, QEMUChrEvent event) diff --git a/monitor/misc.c b/monitor/misc.c index 6e0da0cb96..4a859fb24a 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -120,18 +120,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, int64_t cpu_index, Error **errp) { char *output = NULL; - Monitor *old_mon; MonitorHMP hmp = {}; monitor_data_init(&hmp.common, false, true, false); - old_mon = cur_mon; - cur_mon = &hmp.common; - if (has_cpu_index) { - int ret = monitor_set_cpu(cpu_index); + int ret = monitor_set_cpu(&hmp.common, cpu_index); if (ret < 0) { - cur_mon = old_mon; error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number"); goto out; @@ -139,7 +134,6 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, } handle_hmp_command(&hmp, command_line); - cur_mon = old_mon; WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { if (qstring_get_length(hmp.common.outbuf) > 0) { @@ -255,7 +249,7 @@ static void monitor_init_qmp_commands(void) } /* Set the current CPU defined by the user. Callers must hold BQL. */ -int monitor_set_cpu(int cpu_index) +int monitor_set_cpu(Monitor *mon, int cpu_index) { CPUState *cpu; @@ -263,29 +257,29 @@ int monitor_set_cpu(int cpu_index) if (cpu == NULL) { return -1; } - g_free(cur_mon->mon_cpu_path); - cur_mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); + g_free(mon->mon_cpu_path); + mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); return 0; } /* Callers must hold BQL. */ -static CPUState *mon_get_cpu_sync(bool synchronize) +static CPUState *mon_get_cpu_sync(Monitor *mon, bool synchronize) { CPUState *cpu = NULL; - if (cur_mon->mon_cpu_path) { - cpu = (CPUState *) object_resolve_path_type(cur_mon->mon_cpu_path, + if (mon->mon_cpu_path) { + cpu = (CPUState *) object_resolve_path_type(mon->mon_cpu_path, TYPE_CPU, NULL); if (!cpu) { - g_free(cur_mon->mon_cpu_path); - cur_mon->mon_cpu_path = NULL; + g_free(mon->mon_cpu_path); + mon->mon_cpu_path = NULL; } } - if (!cur_mon->mon_cpu_path) { + if (!mon->mon_cpu_path) { if (!first_cpu) { return NULL; } - monitor_set_cpu(first_cpu->cpu_index); + monitor_set_cpu(mon, first_cpu->cpu_index); cpu = first_cpu; } assert(cpu != NULL); @@ -297,7 +291,7 @@ static CPUState *mon_get_cpu_sync(bool synchronize) CPUState *mon_get_cpu(void) { - return mon_get_cpu_sync(true); + return mon_get_cpu_sync(monitor_cur(), true); } CPUArchState *mon_get_cpu_env(void) @@ -307,9 +301,9 @@ CPUArchState *mon_get_cpu_env(void) return cs ? cs->env_ptr : NULL; } -int monitor_get_cpu_index(void) +int monitor_get_cpu_index(Monitor *mon) { - CPUState *cs = mon_get_cpu_sync(false); + CPUState *cs = mon_get_cpu_sync(mon, false); return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; } @@ -1232,6 +1226,7 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict) void qmp_getfd(const char *fdname, Error **errp) { + Monitor *cur_mon = monitor_cur(); mon_fd_t *monfd; int fd, tmp_fd; @@ -1270,6 +1265,7 @@ void qmp_getfd(const char *fdname, Error **errp) void qmp_closefd(const char *fdname, Error **errp) { + Monitor *cur_mon = monitor_cur(); mon_fd_t *monfd; int tmp_fd; @@ -1356,7 +1352,7 @@ AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque, const char *opaque, Error **errp) { int fd; - Monitor *mon = cur_mon; + Monitor *mon = monitor_cur(); AddfdInfo *fdinfo; fd = qemu_chr_fe_get_msgfd(&mon->chr); diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index b39e03b744..ad2e64be13 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -74,6 +74,7 @@ typedef struct HMPCommand { const char *help; const char *flags; /* p=preconfig */ void (*cmd)(Monitor *mon, const QDict *qdict); + bool coroutine; /* * @sub_table is a list of 2nd level of commands. If it does not exist, * cmd should be used. If it exists, sub_table[?].cmd should be @@ -155,7 +156,9 @@ static inline bool monitor_is_qmp(const Monitor *mon) typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList; extern IOThread *mon_iothread; -extern QEMUBH *qmp_dispatcher_bh; +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; @@ -173,7 +176,7 @@ void monitor_fdsets_cleanup(void); void qmp_send_response(MonitorQMP *mon, const QDict *rsp); void monitor_data_destroy_qmp(MonitorQMP *mon); -void monitor_qmp_bh_dispatcher(void *data); +void coroutine_fn monitor_qmp_dispatcher_co(void *data); int get_monitor_def(int64_t *pval, const char *name); void help_cmd(Monitor *mon, const char *name); diff --git a/monitor/monitor.c b/monitor/monitor.c index 0f32892ad4..ceffe1a83b 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -55,24 +55,85 @@ typedef struct { /* Shared monitor I/O thread */ IOThread *mon_iothread; -/* Bottom half to dispatch the requests received from I/O thread */ -QEMUBH *qmp_dispatcher_bh; +/* Coroutine to dispatch the requests received from I/O thread */ +Coroutine *qmp_dispatcher_co; -/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */ +/* 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. + */ +bool qmp_dispatcher_co_busy; + +/* + * Protects mon_list, monitor_qapi_event_state, coroutine_mon, + * monitor_destroyed. + */ QemuMutex monitor_lock; static GHashTable *monitor_qapi_event_state; +static GHashTable *coroutine_mon; /* Maps Coroutine* to Monitor* */ MonitorList mon_list; int mon_refcount; static bool monitor_destroyed; -__thread Monitor *cur_mon; +Monitor *monitor_cur(void) +{ + Monitor *mon; + + qemu_mutex_lock(&monitor_lock); + mon = g_hash_table_lookup(coroutine_mon, qemu_coroutine_self()); + qemu_mutex_unlock(&monitor_lock); + + return mon; +} + +/** + * Sets a new current monitor and returns the old one. + * + * If a non-NULL monitor is set for a coroutine, another call + * resetting it to NULL is required before the coroutine terminates, + * otherwise a stale entry would remain in the hash table. + */ +Monitor *monitor_set_cur(Coroutine *co, Monitor *mon) +{ + Monitor *old_monitor = monitor_cur(); + + qemu_mutex_lock(&monitor_lock); + if (mon) { + g_hash_table_replace(coroutine_mon, co, mon); + } else { + g_hash_table_remove(coroutine_mon, co); + } + qemu_mutex_unlock(&monitor_lock); + + return old_monitor; +} /** * Is the current monitor, if any, a QMP monitor? */ bool monitor_cur_is_qmp(void) { + Monitor *cur_mon = monitor_cur(); + return cur_mon && monitor_is_qmp(cur_mon); } @@ -209,6 +270,8 @@ int monitor_printf(Monitor *mon, const char *fmt, ...) */ int error_vprintf(const char *fmt, va_list ap) { + Monitor *cur_mon = monitor_cur(); + if (cur_mon && !monitor_cur_is_qmp()) { return monitor_vprintf(cur_mon, fmt, ap); } @@ -217,6 +280,8 @@ int error_vprintf(const char *fmt, va_list ap) int error_vprintf_unless_qmp(const char *fmt, va_list ap) { + Monitor *cur_mon = monitor_cur(); + if (!cur_mon) { return vfprintf(stderr, fmt, ap); } @@ -582,9 +647,24 @@ void monitor_cleanup(void) } qemu_mutex_unlock(&monitor_lock); - /* QEMUBHs needs to be deleted before destroying the I/O thread */ - qemu_bh_delete(qmp_dispatcher_bh); - qmp_dispatcher_bh = NULL; + /* + * The dispatcher needs to stop before destroying the I/O thread. + * + * We need to poll both qemu_aio_context and iohandler_ctx to make + * sure that the dispatcher coroutine keeps making progress and + * eventually terminates. qemu_aio_context is automatically + * polled by calling AIO_WAIT_WHILE on it, but we must poll + * iohandler_ctx manually. + */ + qmp_dispatcher_co_shutdown = true; + if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { + aio_co_wake(qmp_dispatcher_co); + } + + AIO_WAIT_WHILE(qemu_get_aio_context(), + (aio_poll(iohandler_get_aio_context(), false), + qatomic_mb_read(&qmp_dispatcher_co_busy))); + if (mon_iothread) { iothread_destroy(mon_iothread); mon_iothread = NULL; @@ -601,15 +681,16 @@ void monitor_init_globals_core(void) { monitor_qapi_event_init(); qemu_mutex_init(&monitor_lock); + coroutine_mon = g_hash_table_new(NULL, NULL); /* * The dispatcher BH must run in the main loop thread, since we * have commands assuming that context. It would be nice to get * rid of those assumptions. */ - qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), - monitor_qmp_bh_dispatcher, - NULL); + 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); } int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp) diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index 8f04cfa6e6..a456762f6a 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -69,6 +69,7 @@ static bool qmp_caps_accept(MonitorQMP *mon, QMPCapabilityList *list, void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, Error **errp) { + Monitor *cur_mon = monitor_cur(); MonitorQMP *mon; assert(monitor_is_qmp(cur_mon)); @@ -119,6 +120,7 @@ static void query_commands_cb(const QmpCommand *cmd, void *opaque) CommandInfoList *qmp_query_commands(Error **errp) { CommandInfoList *list = NULL; + Monitor *cur_mon = monitor_cur(); MonitorQMP *mon; assert(monitor_is_qmp(cur_mon)); diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 0ab5b78580..1abef70a89 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -328,7 +328,7 @@ void qmp_add_client(const char *protocol, const char *fdname, Chardev *s; int fd; - fd = monitor_get_fd(cur_mon, fdname, errp); + fd = monitor_get_fd(monitor_cur(), fdname, errp); if (fd < 0) { return; } diff --git a/monitor/qmp.c b/monitor/qmp.c index d433ceae5b..b42f8c6af3 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -133,18 +133,17 @@ static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp) } } +/* + * Runs outside of coroutine context for OOB commands, but in + * coroutine context for everything else. + */ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req) { - Monitor *old_mon; QDict *rsp; QDict *error; - old_mon = cur_mon; - cur_mon = &mon->common; - - rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon)); - - cur_mon = old_mon; + rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon), + &mon->common); if (mon->commands == &qmp_cap_negotiation_commands) { error = qdict_get_qdict(rsp, "error"); @@ -211,43 +210,99 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) return req_obj; } -void monitor_qmp_bh_dispatcher(void *data) +void coroutine_fn monitor_qmp_dispatcher_co(void *data) { - QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock(); + QMPRequest *req_obj = NULL; QDict *rsp; bool need_resume; MonitorQMP *mon; - if (!req_obj) { - return; - } + while (true) { + assert(qatomic_mb_read(&qmp_dispatcher_co_busy) == true); - mon = req_obj->mon; - /* qmp_oob_enabled() might change after "qmp_capabilities" */ - need_resume = !qmp_oob_enabled(mon) || - mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1; - qemu_mutex_unlock(&mon->qmp_queue_lock); - if (req_obj->req) { - QDict *qdict = qobject_to(QDict, req_obj->req); - QObject *id = qdict ? qdict_get(qdict, "id") : NULL; - trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: ""); - monitor_qmp_dispatch(mon, req_obj->req); - } else { - assert(req_obj->err); - rsp = qmp_error_response(req_obj->err); - req_obj->err = NULL; - monitor_qmp_respond(mon, rsp); - qobject_unref(rsp); - } + /* + * Mark the dispatcher as not busy already here so that we + * don't miss any new requests coming in the middle of our + * processing. + */ + qatomic_mb_set(&qmp_dispatcher_co_busy, false); + + 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); + } + + /* + * qmp_dispatcher_co_shutdown may have changed if we + * yielded and were reentered from monitor_cleanup() + */ + if (qmp_dispatcher_co_shutdown) { + return; + } + } - if (need_resume) { - /* Pairs with the monitor_suspend() in handle_qmp_command() */ - monitor_resume(&mon->common); - } - qmp_request_free(req_obj); + if (qatomic_xchg(&qmp_dispatcher_co_busy, true) == true) { + /* + * Someone rescheduled us (probably because a new requests + * came in), but we didn't actually yield. Do that now, + * only to be immediately reentered and removed from the + * list of scheduled coroutines. + */ + qemu_coroutine_yield(); + } + + /* + * Move the coroutine from iohandler_ctx to qemu_aio_context for + * executing the command handler so that it can make progress if it + * involves an AIO_WAIT_WHILE(). + */ + aio_co_schedule(qemu_get_aio_context(), qmp_dispatcher_co); + qemu_coroutine_yield(); + + mon = req_obj->mon; + /* qmp_oob_enabled() might change after "qmp_capabilities" */ + need_resume = !qmp_oob_enabled(mon) || + mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1; + qemu_mutex_unlock(&mon->qmp_queue_lock); + if (req_obj->req) { + QDict *qdict = qobject_to(QDict, req_obj->req); + QObject *id = qdict ? qdict_get(qdict, "id") : NULL; + trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: ""); + monitor_qmp_dispatch(mon, req_obj->req); + } else { + assert(req_obj->err); + rsp = qmp_error_response(req_obj->err); + req_obj->err = NULL; + monitor_qmp_respond(mon, rsp); + qobject_unref(rsp); + } - /* Reschedule instead of looping so the main loop stays responsive */ - qemu_bh_schedule(qmp_dispatcher_bh); + if (need_resume) { + /* Pairs with the monitor_suspend() in handle_qmp_command() */ + monitor_resume(&mon->common); + } + qmp_request_free(req_obj); + + /* + * Yield and reschedule so the main loop stays responsive. + * + * Move back to iohandler_ctx so that nested event loops for + * qemu_aio_context don't start new monitor commands. + */ + aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co); + qemu_coroutine_yield(); + } } static void handle_qmp_command(void *opaque, QObject *req, Error *err) @@ -308,7 +363,9 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) qemu_mutex_unlock(&mon->qmp_queue_lock); /* Kick the dispatcher routine */ - qemu_bh_schedule(qmp_dispatcher_bh); + if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { + aio_co_wake(qmp_dispatcher_co); + } } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) |