From 9ac3788b0be368e2826a8e9adadc1ada87d89273 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Thu, 6 Dec 2018 00:37:33 +0400 Subject: char: add a QEMU_CHAR_FEATURE_GCONTEXT flag QEMU_CHAR_FEATURE_GCONTEXT declares the character device can switch GMainContext. Assert we don't switch context when the character device doesn't provide this feature. Character device users must not violate this restriction. In particular, user configurations that violate them must be rejected. Existing frontend that rely on context switching would now assert() if the backend doesn't allow it (instead of silently producing undesired events in the default context). Following patches improve the situation by reporting an error earlier instead, on the frontend side. Signed-off-by: Marc-André Lureau Message-Id: <20181205203737.9011-4-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- include/chardev/char.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/chardev/char.h b/include/chardev/char.h index 7becd8c80c..014566c3de 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -47,6 +47,9 @@ typedef enum { QEMU_CHAR_FEATURE_FD_PASS, /* Whether replay or record mode is enabled */ QEMU_CHAR_FEATURE_REPLAY, + /* Whether the gcontext can be changed after calling + * qemu_chr_be_update_read_handlers() */ + QEMU_CHAR_FEATURE_GCONTEXT, QEMU_CHAR_FEATURE_LAST, } ChardevFeature; -- cgit v1.2.3-55-g7522 From 9ab84470ffc2781df3acd4607bc6d2ae64d6d7e3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 9 Oct 2018 14:27:13 +0800 Subject: monitor: Suspend monitor instead dropping commands When a QMP client sends in-band commands more quickly that we can process them, we can either queue them without limit (QUEUE), drop commands when the queue is full (DROP), or suspend receiving commands when the queue is full (SUSPEND). None of them is ideal: * QUEUE lets a misbehaving client make QEMU eat memory without bounds. Not such a hot idea. * With DROP, the client has to cope with dropped in-band commands. To inform the client, we send a COMMAND_DROPPED event then. The event is flawed by design in two ways: it's ambiguous (see commit d621cfe0a17), and it brings back the "eat memory without bounds" problem. * With SUSPEND, the client has to manage the flow of in-band commands to keep the monitor available for out-of-band commands. We currently DROP. Switch to SUSPEND. Managing the flow of in-band commands to keep the monitor available for out-of-band commands isn't really hard: just count the number of "outstanding" in-band commands (commands sent minus replies received), and if it exceeds the limit, hold back additional ones until it drops below the limit again. Note that we need to be careful pairing the suspend with a resume, or else the monitor will hang, possibly forever. And here since we need to make sure both: (1) popping request from the req queue, and (2) reading length of the req queue will be in the same critical section, we let the pop function take the corresponding queue lock when there is a request, then we release the lock from the caller. Reviewed-by: Marc-André Lureau Signed-off-by: Peter Xu Message-Id: <20181009062718.1914-2-peterx@redhat.com> Signed-off-by: Markus Armbruster --- docs/interop/qmp-spec.txt | 5 +++-- include/monitor/monitor.h | 2 ++ monitor.c | 52 +++++++++++++++++++++-------------------------- qapi/misc.json | 40 ------------------------------------ 4 files changed, 28 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 8f7da0245d..67e44a8120 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -130,8 +130,9 @@ to pass "id" with out-of-band commands. Passing it with all commands is recommended for clients that accept capability "oob". If the client sends in-band commands faster than the server can -execute them, the server will eventually drop commands to limit the -queue length. The sever sends event COMMAND_DROPPED then. +execute them, the server will stop reading the requests from the QMP +channel until the request queue length is reduced to an acceptable +range. Only a few commands support out-of-band execution. The ones that do have "allow-oob": true in output of query-qmp-schema. diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 6fd2c53b09..0c0a37d8cb 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -15,6 +15,8 @@ extern __thread Monitor *cur_mon; #define MONITOR_USE_PRETTY 0x08 #define MONITOR_USE_OOB 0x10 +#define QMP_REQ_QUEUE_LEN_MAX 8 + bool monitor_cur_is_qmp(void); void monitor_init_globals(void); diff --git a/monitor.c b/monitor.c index 5ce2c58b76..d2529260ed 100644 --- a/monitor.c +++ b/monitor.c @@ -4110,8 +4110,12 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) * processing commands only on a very busy monitor. To achieve that, * when we process one request on a specific monitor, we put that * monitor to the end of mon_list queue. + * + * Note: if the function returned with non-NULL, then the caller will + * be with mon->qmp.qmp_queue_lock held, and the caller is responsible + * to release it. */ -static QMPRequest *monitor_qmp_requests_pop_any(void) +static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) { QMPRequest *req_obj = NULL; Monitor *mon; @@ -4121,10 +4125,11 @@ static QMPRequest *monitor_qmp_requests_pop_any(void) QTAILQ_FOREACH(mon, &mon_list, entry) { qemu_mutex_lock(&mon->qmp.qmp_queue_lock); req_obj = g_queue_pop_head(mon->qmp.qmp_requests); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); if (req_obj) { + /* With the lock of corresponding queue held */ break; } + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); } if (req_obj) { @@ -4143,30 +4148,34 @@ static QMPRequest *monitor_qmp_requests_pop_any(void) static void monitor_qmp_bh_dispatcher(void *data) { - QMPRequest *req_obj = monitor_qmp_requests_pop_any(); + QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock(); QDict *rsp; bool need_resume; + Monitor *mon; if (!req_obj) { return; } + mon = req_obj->mon; /* qmp_oob_enabled() might change after "qmp_capabilities" */ - need_resume = !qmp_oob_enabled(req_obj->mon); + need_resume = !qmp_oob_enabled(mon) || + mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1; + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); 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); + monitor_qmp_dispatch(mon, req_obj->req, req_obj->id); } else { assert(req_obj->err); rsp = qmp_error_response(req_obj->err); req_obj->err = NULL; - monitor_qmp_respond(req_obj->mon, rsp, NULL); + monitor_qmp_respond(mon, rsp, NULL); qobject_unref(rsp); } if (need_resume) { /* Pairs with the monitor_suspend() in handle_qmp_command() */ - monitor_resume(req_obj->mon); + monitor_resume(mon); } qmp_request_free(req_obj); @@ -4174,8 +4183,6 @@ static void monitor_qmp_bh_dispatcher(void *data) qemu_bh_schedule(qmp_dispatcher_bh); } -#define QMP_REQ_QUEUE_LEN_MAX (8) - static void handle_qmp_command(void *opaque, QObject *req, Error *err) { Monitor *mon = opaque; @@ -4217,28 +4224,14 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) qemu_mutex_lock(&mon->qmp.qmp_queue_lock); /* - * If OOB is not enabled on the current monitor, we'll emulate the - * old behavior that we won't process the current monitor any more - * until it has responded. This helps make sure that as long as - * OOB is not enabled, the server will never drop any command. + * Suspend the monitor when we can't queue more requests after + * this one. Dequeuing in monitor_qmp_bh_dispatcher() will resume + * it. Note that when OOB is disabled, we queue at most one + * command, for backward compatibility. */ - if (!qmp_oob_enabled(mon)) { + if (!qmp_oob_enabled(mon) || + mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) { monitor_suspend(mon); - } else { - /* Drop the request if queue is full. */ - if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) { - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - /* - * FIXME @id's scope is just @mon, and broadcasting it is - * wrong. If another monitor's client has a command with - * the same ID in flight, the event will incorrectly claim - * that command was dropped. - */ - qapi_event_send_command_dropped(id, - COMMAND_DROP_REASON_QUEUE_FULL); - qmp_request_free(req_obj); - return; - } } /* @@ -4246,6 +4239,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) * handled in time order. Ownership for req_obj, req, id, * etc. will be delivered to the handler side. */ + assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); g_queue_push_tail(mon->qmp.qmp_requests, req_obj); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); diff --git a/qapi/misc.json b/qapi/misc.json index 45121492dd..4211a732f3 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3444,46 +3444,6 @@ ## { 'command': 'query-sev-capabilities', 'returns': 'SevCapability' } -## -# @CommandDropReason: -# -# Reasons that caused one command to be dropped. -# -# @queue-full: the command queue is full. This can only occur when -# the client sends a new non-oob command before the -# response to the previous non-oob command has been -# received. -# -# Since: 2.12 -## -{ 'enum': 'CommandDropReason', - 'data': [ 'queue-full' ] } - -## -# @COMMAND_DROPPED: -# -# Emitted when a command is dropped due to some reason. Commands can -# only be dropped when the oob capability is enabled. -# -# @id: The dropped command's "id" field. -# FIXME Broken by design. Events are broadcast to all monitors. If -# another monitor's client has a command with the same ID in flight, -# the event will incorrectly claim that command was dropped. -# -# @reason: The reason why the command is dropped. -# -# Since: 2.12 -# -# Example: -# -# { "event": "COMMAND_DROPPED", -# "data": {"result": {"id": "libvirt-102", -# "reason": "queue-full" } } } -# -## -{ 'event': 'COMMAND_DROPPED' , - 'data': { 'id': 'any', 'reason': 'CommandDropReason' } } - ## # @set-numa-node: # -- cgit v1.2.3-55-g7522 From 8258292e18c39480b64eba9f3551ab772ce29b5d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 9 Oct 2018 14:27:15 +0800 Subject: monitor: Remove "x-oob", offer capability "oob" unconditionally Out-of-band command execution was introduced in commit cf869d53172. Unfortunately, we ran into a regression, and had to turn it into an experimental option for 2.12 (commit be933ffc23). http://lists.gnu.org/archive/html/qemu-devel/2018-03/msg06231.html The regression has since been fixed (commit 951702f39c7 "monitor: bind dispatch bh to iohandler context"). A thorough re-review of OOB commands led to a few more issues, which have also been addressed. This patch partly reverts be933ffc23 (monitor: new parameter "x-oob"), and makes QMP monitors again offer capability "oob" whenever they can provide it, i.e. when the monitor's character device is capable of running in an I/O thread. Some trivial touch-up in the test code is required to make sure qmp-test won't break. Reviewed-by: Markus Armbruster Reviewed-by: Marc-André Lureau Signed-off-by: Peter Xu Message-Id: <20181009062718.1914-4-peterx@redhat.com> [Conflict with "monitor: check if chardev can switch gcontext for OOB" resolved, commit message updated] Signed-off-by: Markus Armbruster --- include/monitor/monitor.h | 1 - monitor.c | 23 +++++------------------ tests/libqtest.c | 2 +- tests/qmp-test.c | 2 +- vl.c | 5 ----- 5 files changed, 7 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 0c0a37d8cb..c1b40a9cac 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -13,7 +13,6 @@ extern __thread Monitor *cur_mon; #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 #define MONITOR_USE_PRETTY 0x08 -#define MONITOR_USE_OOB 0x10 #define QMP_REQ_QUEUE_LEN_MAX 8 diff --git a/monitor.c b/monitor.c index d2529260ed..6e81b09294 100644 --- a/monitor.c +++ b/monitor.c @@ -4567,22 +4567,12 @@ void monitor_init(Chardev *chr, int flags) { Monitor *mon = g_malloc(sizeof(*mon)); bool use_readline = flags & MONITOR_USE_READLINE; - bool use_oob = flags & MONITOR_USE_OOB; - - if (use_oob) { - if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT)) { - error_report("Monitor out-of-band is not supported with " - "%s typed chardev backend", - object_get_typename(OBJECT(chr))); - exit(1); - } - if (use_readline) { - error_report("Monitor out-of-band is only supported by QMP"); - exit(1); - } - } - monitor_data_init(mon, false, use_oob); + /* Note: we run QMP monitor in I/O thread when @chr supports that */ + monitor_data_init(mon, false, + (flags & MONITOR_USE_CONTROL) + && qemu_chr_has_feature(chr, + QEMU_CHAR_FEATURE_GCONTEXT)); qemu_chr_fe_init(&mon->chr, chr, &error_abort); mon->flags = flags; @@ -4677,9 +4667,6 @@ QemuOptsList qemu_mon_opts = { },{ .name = "pretty", .type = QEMU_OPT_BOOL, - },{ - .name = "x-oob", - .type = QEMU_OPT_BOOL, }, { /* end of list */ } }, diff --git a/tests/libqtest.c b/tests/libqtest.c index 75e07e16e7..225a123649 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -224,7 +224,7 @@ QTestState *qtest_init_without_qmp_handshake(bool use_oob, "-display none " "%s", qemu_binary, socket_path, getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", - qmp_socket_path, use_oob ? ",x-oob=on" : "", + qmp_socket_path, "", extra_args ?: ""); g_test_message("starting QEMU: %s", command); diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 7517be4654..5ba22af6a0 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -116,7 +116,7 @@ static void test_qmp_protocol(void) g_assert(q); test_version(qdict_get(q, "version")); capabilities = qdict_get_qlist(q, "capabilities"); - g_assert(capabilities && qlist_empty(capabilities)); + g_assert(capabilities); qobject_unref(resp); /* Test valid command before handshake */ diff --git a/vl.c b/vl.c index a5ae5f23d2..2a8b2ee16d 100644 --- a/vl.c +++ b/vl.c @@ -2322,11 +2322,6 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) if (qemu_opt_get_bool(opts, "pretty", 0)) flags |= MONITOR_USE_PRETTY; - /* OOB is off by default */ - if (qemu_opt_get_bool(opts, "x-oob", 0)) { - flags |= MONITOR_USE_OOB; - } - chardev = qemu_opt_get(opts, "chardev"); if (!chardev) { error_report("chardev is required"); -- cgit v1.2.3-55-g7522