Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2020-10-09' into staging

Monitor patches for 2020-10-09

# gpg: Signature made Fri 09 Oct 2020 06:16:51 BST
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-monitor-2020-10-09:
  block: Convert 'block_resize' to coroutine
  block: Add bdrv_lock()/unlock()
  block: Add bdrv_co_enter()/leave()
  util/async: Add aio_co_reschedule_self()
  hmp: Add support for coroutine command handlers
  qmp: Move dispatcher to a coroutine
  qapi: Add a 'coroutine' flag for commands
  monitor: Make current monitor a per-coroutine property
  qmp: Call monitor_set_cur() only in qmp_dispatch()
  qmp: Assert that no other monitor is active
  hmp: Update current monitor only in handle_hmp_command()
  monitor: Use getter/setter functions for cur_mon
  monitor: Add Monitor parameter to monitor_get_cpu_index()
  monitor: Add Monitor parameter to monitor_set_cpu()

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2020-10-09 13:20:46 +01:00
50 changed files with 573 additions and 143 deletions
+4 -4
View File
@@ -1,5 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "monitor/monitor.h" #include "qemu/qemu-print.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "audio.h" #include "audio.h"
@@ -94,9 +94,9 @@ static void wav_capture_info (void *opaque)
WAVState *wav = opaque; WAVState *wav = opaque;
char *path = wav->path; char *path = wav->path;
monitor_printf (cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n", qemu_printf("Capturing audio(%d,%d,%d) to %s: %d bytes\n",
wav->freq, wav->bits, wav->nchannels, wav->freq, wav->bits, wav->nchannels,
path ? path : "<not available>", wav->bytes); path ? path : "<not available>", wav->bytes);
} }
static struct capture_ops wav_capture_ops = { static struct capture_ops wav_capture_ops = {
+50
View File
@@ -6303,6 +6303,56 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs)
return bs ? bs->aio_context : qemu_get_aio_context(); return bs ? bs->aio_context : qemu_get_aio_context();
} }
AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs)
{
Coroutine *self = qemu_coroutine_self();
AioContext *old_ctx = qemu_coroutine_get_aio_context(self);
AioContext *new_ctx;
/*
* Increase bs->in_flight to ensure that this operation is completed before
* moving the node to a different AioContext. Read new_ctx only afterwards.
*/
bdrv_inc_in_flight(bs);
new_ctx = bdrv_get_aio_context(bs);
aio_co_reschedule_self(new_ctx);
return old_ctx;
}
void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx)
{
aio_co_reschedule_self(old_ctx);
bdrv_dec_in_flight(bs);
}
void coroutine_fn bdrv_co_lock(BlockDriverState *bs)
{
AioContext *ctx = bdrv_get_aio_context(bs);
/* In the main thread, bs->aio_context won't change concurrently */
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
/*
* We're in coroutine context, so we already hold the lock of the main
* loop AioContext. Don't lock it twice to avoid deadlocks.
*/
assert(qemu_in_coroutine());
if (ctx != qemu_get_aio_context()) {
aio_context_acquire(ctx);
}
}
void coroutine_fn bdrv_co_unlock(BlockDriverState *bs)
{
AioContext *ctx = bdrv_get_aio_context(bs);
assert(qemu_in_coroutine());
if (ctx != qemu_get_aio_context()) {
aio_context_release(ctx);
}
}
void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
{ {
aio_co_enter(bdrv_get_aio_context(bs), co); aio_co_enter(bdrv_get_aio_context(bs), co);
+8 -8
View File
@@ -2449,14 +2449,14 @@ BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
return ret; return ret;
} }
void qmp_block_resize(bool has_device, const char *device, void coroutine_fn qmp_block_resize(bool has_device, const char *device,
bool has_node_name, const char *node_name, bool has_node_name, const char *node_name,
int64_t size, Error **errp) int64_t size, Error **errp)
{ {
Error *local_err = NULL; Error *local_err = NULL;
BlockBackend *blk = NULL; BlockBackend *blk = NULL;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context; AioContext *old_ctx;
bs = bdrv_lookup_bs(has_device ? device : NULL, bs = bdrv_lookup_bs(has_device ? device : NULL,
has_node_name ? node_name : NULL, has_node_name ? node_name : NULL,
@@ -2466,9 +2466,6 @@ void qmp_block_resize(bool has_device, const char *device,
return; return;
} }
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
if (size < 0) { if (size < 0) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
goto out; goto out;
@@ -2485,12 +2482,15 @@ void qmp_block_resize(bool has_device, const char *device,
} }
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
old_ctx = bdrv_co_enter(bs);
blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
bdrv_co_leave(bs, old_ctx);
bdrv_drained_end(bs); bdrv_drained_end(bs);
out: out:
bdrv_co_lock(bs);
blk_unref(blk); blk_unref(blk);
aio_context_release(aio_context); bdrv_co_unlock(bs);
} }
void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
+29
View File
@@ -472,6 +472,7 @@ Syntax:
'*gen': false, '*gen': false,
'*allow-oob': true, '*allow-oob': true,
'*allow-preconfig': true, '*allow-preconfig': true,
'*coroutine': true,
'*if': COND, '*if': COND,
'*features': FEATURES } '*features': FEATURES }
@@ -596,6 +597,34 @@ before the machine is built. It defaults to false. For example:
QMP is available before the machine is built only when QEMU was QMP is available before the machine is built only when QEMU was
started with --preconfig. started with --preconfig.
Member 'coroutine' tells the QMP dispatcher whether the command handler
is safe to be run in a coroutine. It defaults to false. If it is true,
the command handler is called from coroutine context and may yield while
waiting for an external event (such as I/O completion) in order to avoid
blocking the guest and other background operations.
Coroutine safety can be hard to prove, similar to thread safety. Common
pitfalls are:
- The global mutex isn't held across qemu_coroutine_yield(), so
operations that used to assume that they execute atomically may have
to be more careful to protect against changes in the global state.
- Nested event loops (AIO_WAIT_WHILE() etc.) are problematic in
coroutine context and can easily lead to deadlocks. They should be
replaced by yielding and reentering the coroutine when the condition
becomes false.
Since the command handler may assume coroutine context, any callers
other than the QMP dispatcher must also call it in coroutine context.
In particular, HMP commands calling such a QMP command handler must be
marked .coroutine = true in hmp-commands.hx.
It is an error to specify both 'coroutine': true and 'allow-oob': true
for a command. We don't currently have a use case for both together and
without a use case, it's not entirely clear what the semantics should
be.
The optional 'if' member specifies a conditional. See "Configuring The optional 'if' member specifies a conditional. See "Configuring
the schema" below for more on this. the schema" below for more on this.
+1 -1
View File
@@ -330,7 +330,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
def visit_command(self, name, info, ifcond, features, arg_type, def visit_command(self, name, info, ifcond, features, arg_type,
ret_type, gen, success_response, boxed, allow_oob, ret_type, gen, success_response, boxed, allow_oob,
allow_preconfig): allow_preconfig, coroutine):
doc = self._cur_doc doc = self._cur_doc
self._add_doc('Command', self._add_doc('Command',
self._nodes_for_arguments(doc, self._nodes_for_arguments(doc,
+1 -1
View File
@@ -1986,7 +1986,7 @@ void qmp_dump_guest_memory(bool paging, const char *file,
#if !defined(WIN32) #if !defined(WIN32)
if (strstart(file, "fd:", &p)) { if (strstart(file, "fd:", &p)) {
fd = monitor_get_fd(cur_mon, p, errp); fd = monitor_get_fd(monitor_cur(), p, errp);
if (fd == -1) { if (fd == -1) {
return; return;
} }
+1
View File
@@ -76,6 +76,7 @@ ERST
.params = "device size", .params = "device size",
.help = "resize a block image", .help = "resize a block image",
.cmd = hmp_block_resize, .cmd = hmp_block_resize,
.coroutine = true,
}, },
SRST SRST
+1 -1
View File
@@ -34,7 +34,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
for (cpu = cpu_list; cpu; cpu = cpu->next) { for (cpu = cpu_list; cpu; cpu = cpu->next) {
int active = ' '; int active = ' ';
if (cpu->value->cpu_index == monitor_get_cpu_index()) { if (cpu->value->cpu_index == monitor_get_cpu_index(mon)) {
active = '*'; active = '*';
} }
+1 -1
View File
@@ -177,7 +177,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
} }
if (vs->conf.vhostfd) { if (vs->conf.vhostfd) {
vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); vhostfd = monitor_fd_param(monitor_cur(), vs->conf.vhostfd, errp);
if (vhostfd == -1) { if (vhostfd == -1) {
error_prepend(errp, "vhost-scsi: unable to parse vhostfd: "); error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
return; return;
+1 -1
View File
@@ -143,7 +143,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
} }
if (vsock->conf.vhostfd) { if (vsock->conf.vhostfd) {
vhostfd = monitor_fd_param(cur_mon, vsock->conf.vhostfd, errp); vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
if (vhostfd == -1) { if (vhostfd == -1) {
error_prepend(errp, "vhost-vsock: unable to parse vhostfd: "); error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
return; return;
+10
View File
@@ -17,6 +17,7 @@
#ifdef CONFIG_LINUX_IO_URING #ifdef CONFIG_LINUX_IO_URING
#include <liburing.h> #include <liburing.h>
#endif #endif
#include "qemu/coroutine.h"
#include "qemu/queue.h" #include "qemu/queue.h"
#include "qemu/event_notifier.h" #include "qemu/event_notifier.h"
#include "qemu/thread.h" #include "qemu/thread.h"
@@ -654,6 +655,15 @@ static inline bool aio_node_check(AioContext *ctx, bool is_external)
*/ */
void aio_co_schedule(AioContext *ctx, struct Coroutine *co); void aio_co_schedule(AioContext *ctx, struct Coroutine *co);
/**
* aio_co_reschedule_self:
* @new_ctx: the new context
*
* Move the currently running coroutine to new_ctx. If the coroutine is already
* running in new_ctx, do nothing.
*/
void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx);
/** /**
* aio_co_wake: * aio_co_wake:
* @co: the coroutine * @co: the coroutine
+31
View File
@@ -640,6 +640,37 @@ bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag);
*/ */
AioContext *bdrv_get_aio_context(BlockDriverState *bs); AioContext *bdrv_get_aio_context(BlockDriverState *bs);
/**
* Move the current coroutine to the AioContext of @bs and return the old
* AioContext of the coroutine. Increase bs->in_flight so that draining @bs
* will wait for the operation to proceed until the corresponding
* bdrv_co_leave().
*
* Consequently, you can't call drain inside a bdrv_co_enter/leave() section as
* this will deadlock.
*/
AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs);
/**
* Ends a section started by bdrv_co_enter(). Move the current coroutine back
* to old_ctx and decrease bs->in_flight again.
*/
void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx);
/**
* Locks the AioContext of @bs if it's not the current AioContext. This avoids
* double locking which could lead to deadlocks: This is a coroutine_fn, so we
* know we already own the lock of the current AioContext.
*
* May only be called in the main thread.
*/
void coroutine_fn bdrv_co_lock(BlockDriverState *bs);
/**
* Unlocks the AioContext of @bs if it's not the current AioContext.
*/
void coroutine_fn bdrv_co_unlock(BlockDriverState *bs);
/** /**
* Transfer control to @co in the aio context of @bs * Transfer control to @co in the aio context of @bs
*/ */
+4 -3
View File
@@ -5,7 +5,6 @@
#include "qapi/qapi-types-misc.h" #include "qapi/qapi-types-misc.h"
#include "qemu/readline.h" #include "qemu/readline.h"
extern __thread Monitor *cur_mon;
typedef struct MonitorHMP MonitorHMP; typedef struct MonitorHMP MonitorHMP;
typedef struct MonitorOptions MonitorOptions; typedef struct MonitorOptions MonitorOptions;
@@ -13,6 +12,8 @@ typedef struct MonitorOptions MonitorOptions;
extern QemuOptsList qemu_mon_opts; extern QemuOptsList qemu_mon_opts;
Monitor *monitor_cur(void);
Monitor *monitor_set_cur(Coroutine *co, Monitor *mon);
bool monitor_cur_is_qmp(void); bool monitor_cur_is_qmp(void);
void monitor_init_globals(void); void monitor_init_globals(void);
@@ -33,8 +34,8 @@ int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
GCC_FMT_ATTR(2, 0); GCC_FMT_ATTR(2, 0);
int monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3); int monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void monitor_flush(Monitor *mon); void monitor_flush(Monitor *mon);
int monitor_set_cpu(int cpu_index); int monitor_set_cpu(Monitor *mon, int cpu_index);
int monitor_get_cpu_index(void); int monitor_get_cpu_index(Monitor *mon);
void monitor_read_command(MonitorHMP *mon, int show_prompt); void monitor_read_command(MonitorHMP *mon, int show_prompt);
int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
+4 -1
View File
@@ -14,6 +14,7 @@
#ifndef QAPI_QMP_DISPATCH_H #ifndef QAPI_QMP_DISPATCH_H
#define QAPI_QMP_DISPATCH_H #define QAPI_QMP_DISPATCH_H
#include "monitor/monitor.h"
#include "qemu/queue.h" #include "qemu/queue.h"
typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
@@ -24,11 +25,13 @@ typedef enum QmpCommandOptions
QCO_NO_SUCCESS_RESP = (1U << 0), QCO_NO_SUCCESS_RESP = (1U << 0),
QCO_ALLOW_OOB = (1U << 1), QCO_ALLOW_OOB = (1U << 1),
QCO_ALLOW_PRECONFIG = (1U << 2), QCO_ALLOW_PRECONFIG = (1U << 2),
QCO_COROUTINE = (1U << 3),
} QmpCommandOptions; } QmpCommandOptions;
typedef struct QmpCommand typedef struct QmpCommand
{ {
const char *name; const char *name;
/* Runs in coroutine context if QCO_COROUTINE is set */
QmpCommandFunc *fn; QmpCommandFunc *fn;
QmpCommandOptions options; QmpCommandOptions options;
QTAILQ_ENTRY(QmpCommand) node; QTAILQ_ENTRY(QmpCommand) node;
@@ -49,7 +52,7 @@ const char *qmp_command_name(const QmpCommand *cmd);
bool qmp_has_success_response(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd);
QDict *qmp_error_response(Error *err); QDict *qmp_error_response(Error *err);
QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
bool allow_oob); bool allow_oob, Monitor *cur_mon);
bool qmp_is_oob(const QDict *dict); bool qmp_is_oob(const QDict *dict);
typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque);
+2 -2
View File
@@ -26,7 +26,7 @@
void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp) void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp)
{ {
QIOChannel *ioc; QIOChannel *ioc;
int fd = monitor_get_fd(cur_mon, fdname, errp); int fd = monitor_get_fd(monitor_cur(), fdname, errp);
if (fd == -1) { if (fd == -1) {
return; return;
} }
@@ -55,7 +55,7 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc,
void fd_start_incoming_migration(const char *fdname, Error **errp) void fd_start_incoming_migration(const char *fdname, Error **errp)
{ {
QIOChannel *ioc; QIOChannel *ioc;
int fd = monitor_fd_param(cur_mon, fdname, errp); int fd = monitor_fd_param(monitor_cur(), fdname, errp);
if (fd == -1) { if (fd == -1) {
return; return;
} }
+2 -2
View File
@@ -998,7 +998,7 @@ void hmp_cpu(Monitor *mon, const QDict *qdict)
/* XXX: drop the monitor_set_cpu() usage when all HMP commands that /* XXX: drop the monitor_set_cpu() usage when all HMP commands that
use it are converted to the QAPI */ use it are converted to the QAPI */
cpu_index = qdict_get_int(qdict, "index"); 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"); 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"); const char *filename = qdict_get_str(qdict, "filename");
uint64_t addr = qdict_get_int(qdict, "val"); uint64_t addr = qdict_get_int(qdict, "val");
Error *err = NULL; Error *err = NULL;
int cpu_index = monitor_get_cpu_index(); int cpu_index = monitor_get_cpu_index(mon);
if (cpu_index < 0) { if (cpu_index < 0) {
monitor_printf(mon, "No CPU available\n"); monitor_printf(mon, "No CPU available\n");
+35 -9
View File
@@ -1056,6 +1056,21 @@ fail:
return NULL; 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) void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
{ {
QDict *qdict; QDict *qdict;
@@ -1079,7 +1094,24 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
return; 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); qobject_unref(qdict);
} }
@@ -1300,26 +1332,20 @@ cleanup:
static void monitor_read(void *opaque, const uint8_t *buf, int size) static void monitor_read(void *opaque, const uint8_t *buf, int size)
{ {
MonitorHMP *mon; MonitorHMP *mon = container_of(opaque, MonitorHMP, common);
Monitor *old_mon = cur_mon;
int i; int i;
cur_mon = opaque;
mon = container_of(cur_mon, MonitorHMP, common);
if (mon->rs) { if (mon->rs) {
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
readline_handle_byte(mon->rs, buf[i]); readline_handle_byte(mon->rs, buf[i]);
} }
} else { } else {
if (size == 0 || buf[size - 1] != 0) { if (size == 0 || buf[size - 1] != 0) {
monitor_printf(cur_mon, "corrupted command\n"); monitor_printf(&mon->common, "corrupted command\n");
} else { } else {
handle_hmp_command(mon, (char *)buf); handle_hmp_command(mon, (char *)buf);
} }
} }
cur_mon = old_mon;
} }
static void monitor_event(void *opaque, QEMUChrEvent event) static void monitor_event(void *opaque, QEMUChrEvent event)
+17 -21
View File
@@ -120,18 +120,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
int64_t cpu_index, Error **errp) int64_t cpu_index, Error **errp)
{ {
char *output = NULL; char *output = NULL;
Monitor *old_mon;
MonitorHMP hmp = {}; MonitorHMP hmp = {};
monitor_data_init(&hmp.common, false, true, false); monitor_data_init(&hmp.common, false, true, false);
old_mon = cur_mon;
cur_mon = &hmp.common;
if (has_cpu_index) { if (has_cpu_index) {
int ret = monitor_set_cpu(cpu_index); int ret = monitor_set_cpu(&hmp.common, cpu_index);
if (ret < 0) { if (ret < 0) {
cur_mon = old_mon;
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
"a CPU number"); "a CPU number");
goto out; 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); handle_hmp_command(&hmp, command_line);
cur_mon = old_mon;
WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) {
if (qstring_get_length(hmp.common.outbuf) > 0) { 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. */ /* 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; CPUState *cpu;
@@ -263,29 +257,29 @@ int monitor_set_cpu(int cpu_index)
if (cpu == NULL) { if (cpu == NULL) {
return -1; return -1;
} }
g_free(cur_mon->mon_cpu_path); g_free(mon->mon_cpu_path);
cur_mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu));
return 0; return 0;
} }
/* Callers must hold BQL. */ /* 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; CPUState *cpu = NULL;
if (cur_mon->mon_cpu_path) { if (mon->mon_cpu_path) {
cpu = (CPUState *) object_resolve_path_type(cur_mon->mon_cpu_path, cpu = (CPUState *) object_resolve_path_type(mon->mon_cpu_path,
TYPE_CPU, NULL); TYPE_CPU, NULL);
if (!cpu) { if (!cpu) {
g_free(cur_mon->mon_cpu_path); g_free(mon->mon_cpu_path);
cur_mon->mon_cpu_path = NULL; mon->mon_cpu_path = NULL;
} }
} }
if (!cur_mon->mon_cpu_path) { if (!mon->mon_cpu_path) {
if (!first_cpu) { if (!first_cpu) {
return NULL; return NULL;
} }
monitor_set_cpu(first_cpu->cpu_index); monitor_set_cpu(mon, first_cpu->cpu_index);
cpu = first_cpu; cpu = first_cpu;
} }
assert(cpu != NULL); assert(cpu != NULL);
@@ -297,7 +291,7 @@ static CPUState *mon_get_cpu_sync(bool synchronize)
CPUState *mon_get_cpu(void) 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) CPUArchState *mon_get_cpu_env(void)
@@ -307,9 +301,9 @@ CPUArchState *mon_get_cpu_env(void)
return cs ? cs->env_ptr : NULL; 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; 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) void qmp_getfd(const char *fdname, Error **errp)
{ {
Monitor *cur_mon = monitor_cur();
mon_fd_t *monfd; mon_fd_t *monfd;
int fd, tmp_fd; int fd, tmp_fd;
@@ -1270,6 +1265,7 @@ void qmp_getfd(const char *fdname, Error **errp)
void qmp_closefd(const char *fdname, Error **errp) void qmp_closefd(const char *fdname, Error **errp)
{ {
Monitor *cur_mon = monitor_cur();
mon_fd_t *monfd; mon_fd_t *monfd;
int tmp_fd; 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) const char *opaque, Error **errp)
{ {
int fd; int fd;
Monitor *mon = cur_mon; Monitor *mon = monitor_cur();
AddfdInfo *fdinfo; AddfdInfo *fdinfo;
fd = qemu_chr_fe_get_msgfd(&mon->chr); fd = qemu_chr_fe_get_msgfd(&mon->chr);
+5 -2
View File
@@ -74,6 +74,7 @@ typedef struct HMPCommand {
const char *help; const char *help;
const char *flags; /* p=preconfig */ const char *flags; /* p=preconfig */
void (*cmd)(Monitor *mon, const QDict *qdict); void (*cmd)(Monitor *mon, const QDict *qdict);
bool coroutine;
/* /*
* @sub_table is a list of 2nd level of commands. If it does not exist, * @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 * 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; typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
extern IOThread *mon_iothread; 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 QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
extern QemuMutex monitor_lock; extern QemuMutex monitor_lock;
extern MonitorList mon_list; extern MonitorList mon_list;
@@ -173,7 +176,7 @@ void monitor_fdsets_cleanup(void);
void qmp_send_response(MonitorQMP *mon, const QDict *rsp); void qmp_send_response(MonitorQMP *mon, const QDict *rsp);
void monitor_data_destroy_qmp(MonitorQMP *mon); 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); int get_monitor_def(int64_t *pval, const char *name);
void help_cmd(Monitor *mon, const char *name); void help_cmd(Monitor *mon, const char *name);
+91 -10
View File
@@ -55,24 +55,85 @@ typedef struct {
/* Shared monitor I/O thread */ /* Shared monitor I/O thread */
IOThread *mon_iothread; IOThread *mon_iothread;
/* Bottom half to dispatch the requests received from I/O thread */ /* Coroutine to dispatch the requests received from I/O thread */
QEMUBH *qmp_dispatcher_bh; 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; QemuMutex monitor_lock;
static GHashTable *monitor_qapi_event_state; static GHashTable *monitor_qapi_event_state;
static GHashTable *coroutine_mon; /* Maps Coroutine* to Monitor* */
MonitorList mon_list; MonitorList mon_list;
int mon_refcount; int mon_refcount;
static bool monitor_destroyed; 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? * Is the current monitor, if any, a QMP monitor?
*/ */
bool monitor_cur_is_qmp(void) bool monitor_cur_is_qmp(void)
{ {
Monitor *cur_mon = monitor_cur();
return cur_mon && monitor_is_qmp(cur_mon); 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) int error_vprintf(const char *fmt, va_list ap)
{ {
Monitor *cur_mon = monitor_cur();
if (cur_mon && !monitor_cur_is_qmp()) { if (cur_mon && !monitor_cur_is_qmp()) {
return monitor_vprintf(cur_mon, fmt, ap); 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) int error_vprintf_unless_qmp(const char *fmt, va_list ap)
{ {
Monitor *cur_mon = monitor_cur();
if (!cur_mon) { if (!cur_mon) {
return vfprintf(stderr, fmt, ap); return vfprintf(stderr, fmt, ap);
} }
@@ -582,9 +647,24 @@ void monitor_cleanup(void)
} }
qemu_mutex_unlock(&monitor_lock); qemu_mutex_unlock(&monitor_lock);
/* QEMUBHs needs to be deleted before destroying the I/O thread */ /*
qemu_bh_delete(qmp_dispatcher_bh); * The dispatcher needs to stop before destroying the I/O thread.
qmp_dispatcher_bh = NULL; *
* 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) { if (mon_iothread) {
iothread_destroy(mon_iothread); iothread_destroy(mon_iothread);
mon_iothread = NULL; mon_iothread = NULL;
@@ -601,15 +681,16 @@ void monitor_init_globals_core(void)
{ {
monitor_qapi_event_init(); monitor_qapi_event_init();
qemu_mutex_init(&monitor_lock); 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 * The dispatcher BH must run in the main loop thread, since we
* have commands assuming that context. It would be nice to get * have commands assuming that context. It would be nice to get
* rid of those assumptions. * rid of those assumptions.
*/ */
qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), qmp_dispatcher_co = qemu_coroutine_create(monitor_qmp_dispatcher_co, NULL);
monitor_qmp_bh_dispatcher, qatomic_mb_set(&qmp_dispatcher_co_busy, true);
NULL); aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co);
} }
int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp) int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp)

Some files were not shown because too many files have changed in this diff Show More