From 3d7a1c44e2b23079485c0cd21ca2db04f886ec13 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 15 Aug 2018 21:37:35 +0800 Subject: [PATCH 1/6] monitor: simplify monitor_qmp_setup_handlers_bh When we reach monitor_qmp_setup_handlers_bh() we must be using the IOThread then, so no need to check against it any more. Instead, we assert. Reviewed-by: Markus Armbruster Signed-off-by: Peter Xu Message-Id: <20180815133747.25032-2-peterx@redhat.com> [Insufficiently useful comment dropped] Signed-off-by: Markus Armbruster --- monitor.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/monitor.c b/monitor.c index 021c11b1bf2a..c04977660322 100644 --- a/monitor.c +++ b/monitor.c @@ -4631,15 +4631,9 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) Monitor *mon = opaque; GMainContext *context; - if (mon->use_io_thread) { - /* Use @mon_iothread context */ - context = monitor_get_io_context(); - assert(context); - } else { - /* Use default main loop context */ - context = NULL; - } - + assert(mon->use_io_thread); + context = monitor_get_io_context(); + assert(context); qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read, monitor_qmp_event, NULL, mon, context, true); monitor_list_append(mon); From 8af6bb14a3a8bb40b0b3bf0171c018cd39026cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 17 Aug 2018 19:37:52 +0200 Subject: [PATCH 2/6] monitor: accept input on resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A chardev may stop trying to write if the associated can_read() callback returned 0. This happens when the monitor is suspended. The frontend is supposed to call qemu_chr_fe_accept_input() when it is ready to accept data again. An issue was observed with a spice port: pending commands may be delayed, as the chardev is not flushed. Most chardev don't use the accept_input() callback, and instead check regularly if they can write. The ones that do use it are braille, mux, msmouse, spice (abstract), spicevmc, spiceport, wctablet. Signed-off-by: Marc-André Lureau Message-Id: <20180817173752.19136-1-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/monitor.c b/monitor.c index c04977660322..c1add2ce09b3 100644 --- a/monitor.c +++ b/monitor.c @@ -4411,6 +4411,7 @@ void monitor_resume(Monitor *mon) assert(mon->rs); readline_show_prompt(mon->rs); } + qemu_chr_fe_accept_input(&mon->chr); } trace_monitor_suspend(mon, -1); } From 6cd112e26664994cb5eff207e765c9c3e55f73ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 29 Aug 2018 15:40:34 +0200 Subject: [PATCH 3/6] monitor: consitify qmp_send_response() QDict argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180829134043.31706-2-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monitor.c b/monitor.c index c1add2ce09b3..bd0810df4d96 100644 --- a/monitor.c +++ b/monitor.c @@ -503,9 +503,9 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...) return 0; } -static void qmp_send_response(Monitor *mon, QDict *rsp) +static void qmp_send_response(Monitor *mon, const QDict *rsp) { - QObject *data = QOBJECT(rsp); + const QObject *data = QOBJECT(rsp); QString *json; json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) : From 2aa788f5cb9e7fa24bed9091e7eae801f5c3ab04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 29 Aug 2018 15:40:35 +0200 Subject: [PATCH 4/6] qmp: constify qmp_is_oob() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180829134043.31706-3-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- include/qapi/qmp/dispatch.h | 2 +- qapi/qmp-dispatch.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 4e2e749faf1b..68a528a9aaee 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -50,7 +50,7 @@ bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, bool allow_oob); -bool qmp_is_oob(QDict *dict); +bool qmp_is_oob(const QDict *dict); typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index d8da1a62de52..1d922e04f710 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -155,7 +155,7 @@ QDict *qmp_error_response(Error *err) /* * Does @qdict look like a command to be run out-of-band? */ -bool qmp_is_oob(QDict *dict) +bool qmp_is_oob(const QDict *dict) { return qdict_haskey(dict, "exec-oob") && !qdict_haskey(dict, "execute"); From 27656018d8683d02175fa5b7911353645d64cd9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 29 Aug 2018 15:40:36 +0200 Subject: [PATCH 5/6] Revert "qmp: isolate responses into io thread" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit abe3cd0ff7f774966da6842620806ab7576fe4f3. There is no need to add an additional queue to send the reply to the IOThread, because QMP response is thread safe, and chardev write path is thread safe. It will schedule the watcher in the associated IOThread. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180829134043.31706-4-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 120 ++---------------------------------------------------- 1 file changed, 3 insertions(+), 117 deletions(-) diff --git a/monitor.c b/monitor.c index bd0810df4d96..25f656bf8a57 100644 --- a/monitor.c +++ b/monitor.c @@ -182,8 +182,6 @@ typedef struct { QemuMutex qmp_queue_lock; /* Input queue that holds all the parsed QMP requests */ GQueue *qmp_requests; - /* Output queue contains all the QMP responses in order */ - GQueue *qmp_responses; } MonitorQMP; /* @@ -247,9 +245,6 @@ IOThread *mon_iothread; /* Bottom half to dispatch the requests received from I/O thread */ QEMUBH *qmp_dispatcher_bh; -/* Bottom half to deliver the responses back to clients */ -QEMUBH *qmp_respond_bh; - struct QMPRequest { /* Owner of the request */ Monitor *mon; @@ -375,19 +370,10 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) } } -/* Caller must hold the mon->qmp.qmp_queue_lock */ -static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) -{ - while (!g_queue_is_empty(mon->qmp.qmp_responses)) { - qobject_unref((QDict *)g_queue_pop_head(mon->qmp.qmp_responses)); - } -} - static void monitor_qmp_cleanup_queues(Monitor *mon) { qemu_mutex_lock(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); - monitor_qmp_cleanup_resp_queue_locked(mon); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); } @@ -518,85 +504,6 @@ static void qmp_send_response(Monitor *mon, const QDict *rsp) qobject_unref(json); } -static void qmp_queue_response(Monitor *mon, QDict *rsp) -{ - if (mon->use_io_thread) { - /* - * Push a reference to the response queue. The I/O thread - * drains that queue and emits. - */ - qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(rsp)); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - qemu_bh_schedule(qmp_respond_bh); - } else { - /* - * Not using monitor I/O thread, i.e. we are in the main thread. - * Emit right away. - */ - qmp_send_response(mon, rsp); - } -} - -struct QMPResponse { - Monitor *mon; - QDict *data; -}; -typedef struct QMPResponse QMPResponse; - -static QDict *monitor_qmp_response_pop_one(Monitor *mon) -{ - QDict *data; - - qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - data = g_queue_pop_head(mon->qmp.qmp_responses); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - - return data; -} - -static void monitor_qmp_response_flush(Monitor *mon) -{ - QDict *data; - - while ((data = monitor_qmp_response_pop_one(mon))) { - qmp_send_response(mon, data); - qobject_unref(data); - } -} - -/* - * Pop a QMPResponse from any monitor's response queue into @response. - * Return false if all the queues are empty; else true. - */ -static bool monitor_qmp_response_pop_any(QMPResponse *response) -{ - Monitor *mon; - QDict *data = NULL; - - qemu_mutex_lock(&monitor_lock); - QTAILQ_FOREACH(mon, &mon_list, entry) { - data = monitor_qmp_response_pop_one(mon); - if (data) { - response->mon = mon; - response->data = data; - break; - } - } - qemu_mutex_unlock(&monitor_lock); - return data != NULL; -} - -static void monitor_qmp_bh_responder(void *opaque) -{ - QMPResponse response; - - while (monitor_qmp_response_pop_any(&response)) { - qmp_send_response(response.mon, response.data); - qobject_unref(response.data); - } -} - static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { /* Limit guest-triggerable events to 1 per second */ [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS }, @@ -620,7 +527,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) QTAILQ_FOREACH(mon, &mon_list, entry) { if (monitor_is_qmp(mon) && mon->qmp.commands != &qmp_cap_negotiation_commands) { - qmp_queue_response(mon, qdict); + qmp_send_response(mon, qdict); } } } @@ -818,7 +725,6 @@ static void monitor_data_init(Monitor *mon, bool skip_flush, mon->skip_flush = skip_flush; mon->use_io_thread = use_io_thread; mon->qmp.qmp_requests = g_queue_new(); - mon->qmp.qmp_responses = g_queue_new(); } static void monitor_data_destroy(Monitor *mon) @@ -833,9 +739,7 @@ static void monitor_data_destroy(Monitor *mon) qemu_mutex_destroy(&mon->mon_lock); qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); - monitor_qmp_cleanup_resp_queue_locked(mon); g_queue_free(mon->qmp.qmp_requests); - g_queue_free(mon->qmp.qmp_responses); } char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, @@ -4152,7 +4056,7 @@ static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id) qdict_put_obj(rsp, "id", qobject_ref(id)); } - qmp_queue_response(mon, rsp); + qmp_send_response(mon, rsp); } } @@ -4445,7 +4349,7 @@ static void monitor_qmp_event(void *opaque, int event) mon->qmp.commands = &qmp_cap_negotiation_commands; monitor_qmp_caps_reset(mon); data = qmp_greeting(mon); - qmp_queue_response(mon, data); + qmp_send_response(mon, data); qobject_unref(data); mon_refcount++; break; @@ -4456,7 +4360,6 @@ static void monitor_qmp_event(void *opaque, int event) * stdio, it's possible that stdout is still open when stdin * is closed. */ - monitor_qmp_response_flush(mon); monitor_qmp_cleanup_queues(mon); json_message_parser_destroy(&mon->qmp.parser); json_message_parser_init(&mon->qmp.parser, handle_qmp_command, @@ -4559,15 +4462,6 @@ static void monitor_iothread_init(void) qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), monitor_qmp_bh_dispatcher, NULL); - - /* - * The responder BH must be run in the monitor I/O thread, so that - * monitors that are using the I/O thread have their output - * written by the I/O thread. - */ - qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), - monitor_qmp_bh_responder, - NULL); } void monitor_init_globals(void) @@ -4714,12 +4608,6 @@ void monitor_cleanup(void) */ iothread_stop(mon_iothread); - /* - * Flush all response queues. Note that even after this flush, - * data may remain in output buffers. - */ - monitor_qmp_bh_responder(NULL); - /* Flush output buffers and destroy monitors */ qemu_mutex_lock(&monitor_lock); QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) { @@ -4733,8 +4621,6 @@ void monitor_cleanup(void) /* QEMUBHs needs to be deleted before destroying the I/O thread */ qemu_bh_delete(qmp_dispatcher_bh); qmp_dispatcher_bh = NULL; - qemu_bh_delete(qmp_respond_bh); - qmp_respond_bh = NULL; iothread_destroy(mon_iothread); mon_iothread = NULL; From 176160ce78b58f29d765ddace885e88a732df34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 29 Aug 2018 15:40:37 +0200 Subject: [PATCH 6/6] monitor: no need to save need_resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need for per-command need_resume granularity, it should resume after running an non-oob command on oob-disabled monitor. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180829134043.31706-5-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- monitor.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/monitor.c b/monitor.c index 25f656bf8a57..cf3b629cf7be 100644 --- a/monitor.c +++ b/monitor.c @@ -256,12 +256,6 @@ struct QMPRequest { */ QObject *req; Error *err; - /* - * Whether we need to resume the monitor afterward. This flag is - * used to emulate the old QMP server behavior that the current - * command must be completed before execution of the next one. - */ - bool need_resume; }; typedef struct QMPRequest QMPRequest; @@ -4131,11 +4125,14 @@ static void monitor_qmp_bh_dispatcher(void *data) { QMPRequest *req_obj = monitor_qmp_requests_pop_any(); QDict *rsp; + bool need_resume; if (!req_obj) { return; } + /* qmp_oob_enabled() might change after "qmp_capabilities" */ + need_resume = !qmp_oob_enabled(req_obj->mon); if (req_obj->req) { trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); @@ -4147,7 +4144,7 @@ static void monitor_qmp_bh_dispatcher(void *data) qobject_unref(rsp); } - if (req_obj->need_resume) { + if (need_resume) { /* Pairs with the monitor_suspend() in handle_qmp_command() */ monitor_resume(req_obj->mon); } @@ -4195,7 +4192,6 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) req_obj->id = id; req_obj->req = req; req_obj->err = err; - req_obj->need_resume = false; /* Protect qmp_requests and fetching its length. */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); @@ -4208,7 +4204,6 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) */ if (!qmp_oob_enabled(mon)) { monitor_suspend(mon); - req_obj->need_resume = true; } else { /* Drop the request if queue is full. */ if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) {