hmp: Add support for coroutine command handlers

Often, QMP command handlers are not only called to handle QMP commands,
but also from a corresponding HMP command handler. In order to give them
a consistent environment, optionally run HMP command handlers in a
coroutine, too.

The implementation is a lot simpler than in QMP because for HMP, we
still block the VM while the coroutine is running.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-Id: <20201005155855.256490-11-kwolf@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Kevin Wolf 2020-10-05 17:58:51 +02:00 committed by Markus Armbruster
parent 9ce44e2ce2
commit bb4b9ead95
3 changed files with 35 additions and 7 deletions

View File

@ -617,8 +617,8 @@ pitfalls are:
Since the command handler may assume coroutine context, any callers Since the command handler may assume coroutine context, any callers
other than the QMP dispatcher must also call it in coroutine context. other than the QMP dispatcher must also call it in coroutine context.
In particular, HMP commands calling such a QMP command handler must In particular, HMP commands calling such a QMP command handler must be
enter coroutine context before calling the handler. marked .coroutine = true in hmp-commands.hx.
It is an error to specify both 'coroutine': true and 'allow-oob': true 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 for a command. We don't currently have a use case for both together and

View File

@ -1056,12 +1056,26 @@ 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;
const HMPCommand *cmd; const HMPCommand *cmd;
const char *cmd_start = cmdline; const char *cmd_start = cmdline;
Monitor *old_mon;
trace_handle_hmp_command(mon, cmdline); trace_handle_hmp_command(mon, cmdline);
@ -1080,10 +1094,23 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
return; return;
} }
if (!cmd->coroutine) {
/* old_mon is non-NULL when called from qmp_human_monitor_command() */ /* old_mon is non-NULL when called from qmp_human_monitor_command() */
old_mon = monitor_set_cur(qemu_coroutine_self(), &mon->common); Monitor *old_mon = monitor_set_cur(qemu_coroutine_self(), &mon->common);
cmd->cmd(&mon->common, qdict); cmd->cmd(&mon->common, qdict);
monitor_set_cur(qemu_coroutine_self(), old_mon); 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);
} }

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