* [Qemu-devel] [RFC PATCH 01/10] monitor: Remove unused password prompting fields
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 15:39 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 02/10] monitor: Split monitor_init in HMP and QMP function Kevin Wolf
` (11 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Commit 788cf9f8c removed the code for password prompting from the
monitor. Since then, the Monitor fields password_completion_cb and
password_opaque have been unused. Remove them.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
monitor.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/monitor.c b/monitor.c
index 6428eb3b7e..70ce9e8a77 100644
--- a/monitor.c
+++ b/monitor.c
@@ -220,8 +220,6 @@ struct Monitor {
MonitorQMP qmp;
gchar *mon_cpu_path;
- BlockCompletionFunc *password_completion_cb;
- void *password_opaque;
mon_cmd_t *cmd_table;
QTAILQ_ENTRY(Monitor) entry;
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 01/10] monitor: Remove unused password prompting fields
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 01/10] monitor: Remove unused password prompting fields Kevin Wolf
@ 2019-06-07 15:39 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 15:39 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Commit 788cf9f8c removed the code for password prompting from the
> monitor. Since then, the Monitor fields password_completion_cb and
> password_opaque have been unused. Remove them.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
A shame, I love the idea of password completion. 123<tab>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> monitor.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/monitor.c b/monitor.c
> index 6428eb3b7e..70ce9e8a77 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -220,8 +220,6 @@ struct Monitor {
>
> MonitorQMP qmp;
> gchar *mon_cpu_path;
> - BlockCompletionFunc *password_completion_cb;
> - void *password_opaque;
> mon_cmd_t *cmd_table;
> QTAILQ_ENTRY(Monitor) entry;
>
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 02/10] monitor: Split monitor_init in HMP and QMP function
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 01/10] monitor: Remove unused password prompting fields Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 15:46 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 03/10] monitor: Make MonitorQMP a child class of Monitor Kevin Wolf
` (10 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Instead of mixing HMP and QMP monitors in the same function, separate
the monitor creation function for both.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
monitor.c | 86 +++++++++++++++++++++++++++++++------------------------
1 file changed, 49 insertions(+), 37 deletions(-)
diff --git a/monitor.c b/monitor.c
index 70ce9e8a77..bb23cc0450 100644
--- a/monitor.c
+++ b/monitor.c
@@ -702,7 +702,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline);
static void monitor_iothread_init(void);
-static void monitor_data_init(Monitor *mon, bool skip_flush,
+static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
bool use_io_thread)
{
if (use_io_thread && !mon_iothread) {
@@ -717,6 +717,7 @@ 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->flags = flags;
}
static void monitor_data_destroy(Monitor *mon)
@@ -740,7 +741,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
char *output = NULL;
Monitor *old_mon, hmp;
- monitor_data_init(&hmp, true, false);
+ monitor_data_init(&hmp, 0, true, false);
old_mon = cur_mon;
cur_mon = &hmp;
@@ -4603,19 +4604,48 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
monitor_list_append(mon);
}
-void monitor_init(Chardev *chr, int flags)
+static void monitor_init_qmp(Chardev *chr, int flags)
{
Monitor *mon = g_malloc(sizeof(*mon));
- bool use_readline = flags & MONITOR_USE_READLINE;
/* 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));
+ monitor_data_init(mon, flags, false,
+ qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
qemu_chr_fe_init(&mon->chr, chr, &error_abort);
- mon->flags = flags;
+ qemu_chr_fe_set_echo(&mon->chr, true);
+
+ json_message_parser_init(&mon->qmp.parser, handle_qmp_command, mon, NULL);
+ if (mon->use_io_thread) {
+ /*
+ * Make sure the old iowatch is gone. It's possible when
+ * e.g. the chardev is in client mode, with wait=on.
+ */
+ remove_fd_in_watch(chr);
+ /*
+ * We can't call qemu_chr_fe_set_handlers() directly here
+ * since chardev might be running in the monitor I/O
+ * thread. Schedule a bottom half.
+ */
+ aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
+ monitor_qmp_setup_handlers_bh, mon);
+ /* The bottom half will add @mon to @mon_list */
+ } else {
+ qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
+ monitor_qmp_read, monitor_qmp_event,
+ NULL, mon, NULL, true);
+ monitor_list_append(mon);
+ }
+}
+
+static void monitor_init_hmp(Chardev *chr, int flags)
+{
+ Monitor *mon = g_malloc(sizeof(*mon));
+ bool use_readline = flags & MONITOR_USE_READLINE;
+
+ monitor_data_init(mon, flags, false, false);
+ qemu_chr_fe_init(&mon->chr, chr, &error_abort);
+
if (use_readline) {
mon->rs = readline_init(monitor_readline_printf,
monitor_readline_flush,
@@ -4624,36 +4654,18 @@ void monitor_init(Chardev *chr, int flags)
monitor_read_command(mon, 0);
}
- if (monitor_is_qmp(mon)) {
- qemu_chr_fe_set_echo(&mon->chr, true);
- json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
- mon, NULL);
- if (mon->use_io_thread) {
- /*
- * Make sure the old iowatch is gone. It's possible when
- * e.g. the chardev is in client mode, with wait=on.
- */
- remove_fd_in_watch(chr);
- /*
- * We can't call qemu_chr_fe_set_handlers() directly here
- * since chardev might be running in the monitor I/O
- * thread. Schedule a bottom half.
- */
- aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
- monitor_qmp_setup_handlers_bh, mon);
- /* The bottom half will add @mon to @mon_list */
- return;
- } else {
- qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
- monitor_qmp_read, monitor_qmp_event,
- NULL, mon, NULL, true);
- }
+ qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
+ monitor_event, NULL, mon, NULL, true);
+ monitor_list_append(mon);
+}
+
+void monitor_init(Chardev *chr, int flags)
+{
+ if (flags & MONITOR_USE_CONTROL) {
+ monitor_init_qmp(chr, flags);
} else {
- qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
- monitor_event, NULL, mon, NULL, true);
+ monitor_init_hmp(chr, flags);
}
-
- monitor_list_append(mon);
}
void monitor_cleanup(void)
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 02/10] monitor: Split monitor_init in HMP and QMP function
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 02/10] monitor: Split monitor_init in HMP and QMP function Kevin Wolf
@ 2019-06-07 15:46 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 15:46 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Instead of mixing HMP and QMP monitors in the same function, separate
> the monitor creation function for both.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> monitor.c | 86 +++++++++++++++++++++++++++++++------------------------
> 1 file changed, 49 insertions(+), 37 deletions(-)
>
> diff --git a/monitor.c b/monitor.c
> index 70ce9e8a77..bb23cc0450 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -702,7 +702,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline);
>
> static void monitor_iothread_init(void);
>
> -static void monitor_data_init(Monitor *mon, bool skip_flush,
> +static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> bool use_io_thread)
> {
> if (use_io_thread && !mon_iothread) {
> @@ -717,6 +717,7 @@ 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->flags = flags;
> }
>
> static void monitor_data_destroy(Monitor *mon)
> @@ -740,7 +741,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
> char *output = NULL;
> Monitor *old_mon, hmp;
>
> - monitor_data_init(&hmp, true, false);
> + monitor_data_init(&hmp, 0, true, false);
>
> old_mon = cur_mon;
> cur_mon = &hmp;
> @@ -4603,19 +4604,48 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
> monitor_list_append(mon);
> }
>
> -void monitor_init(Chardev *chr, int flags)
> +static void monitor_init_qmp(Chardev *chr, int flags)
> {
> Monitor *mon = g_malloc(sizeof(*mon));
> - bool use_readline = flags & MONITOR_USE_READLINE;
>
> /* 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));
> + monitor_data_init(mon, flags, false,
> + qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
>
> qemu_chr_fe_init(&mon->chr, chr, &error_abort);
> - mon->flags = flags;
> + qemu_chr_fe_set_echo(&mon->chr, true);
> +
> + json_message_parser_init(&mon->qmp.parser, handle_qmp_command, mon, NULL);
> + if (mon->use_io_thread) {
> + /*
> + * Make sure the old iowatch is gone. It's possible when
> + * e.g. the chardev is in client mode, with wait=on.
> + */
> + remove_fd_in_watch(chr);
> + /*
> + * We can't call qemu_chr_fe_set_handlers() directly here
> + * since chardev might be running in the monitor I/O
> + * thread. Schedule a bottom half.
> + */
> + aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> + monitor_qmp_setup_handlers_bh, mon);
> + /* The bottom half will add @mon to @mon_list */
> + } else {
> + qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
> + monitor_qmp_read, monitor_qmp_event,
> + NULL, mon, NULL, true);
> + monitor_list_append(mon);
> + }
> +}
> +
> +static void monitor_init_hmp(Chardev *chr, int flags)
> +{
> + Monitor *mon = g_malloc(sizeof(*mon));
> + bool use_readline = flags & MONITOR_USE_READLINE;
> +
> + monitor_data_init(mon, flags, false, false);
> + qemu_chr_fe_init(&mon->chr, chr, &error_abort);
> +
> if (use_readline) {
> mon->rs = readline_init(monitor_readline_printf,
> monitor_readline_flush,
> @@ -4624,36 +4654,18 @@ void monitor_init(Chardev *chr, int flags)
> monitor_read_command(mon, 0);
> }
>
> - if (monitor_is_qmp(mon)) {
> - qemu_chr_fe_set_echo(&mon->chr, true);
> - json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
> - mon, NULL);
> - if (mon->use_io_thread) {
> - /*
> - * Make sure the old iowatch is gone. It's possible when
> - * e.g. the chardev is in client mode, with wait=on.
> - */
> - remove_fd_in_watch(chr);
> - /*
> - * We can't call qemu_chr_fe_set_handlers() directly here
> - * since chardev might be running in the monitor I/O
> - * thread. Schedule a bottom half.
> - */
> - aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> - monitor_qmp_setup_handlers_bh, mon);
> - /* The bottom half will add @mon to @mon_list */
> - return;
> - } else {
> - qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
> - monitor_qmp_read, monitor_qmp_event,
> - NULL, mon, NULL, true);
> - }
> + qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
> + monitor_event, NULL, mon, NULL, true);
> + monitor_list_append(mon);
> +}
> +
> +void monitor_init(Chardev *chr, int flags)
> +{
> + if (flags & MONITOR_USE_CONTROL) {
> + monitor_init_qmp(chr, flags);
> } else {
> - qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
> - monitor_event, NULL, mon, NULL, true);
> + monitor_init_hmp(chr, flags);
> }
> -
> - monitor_list_append(mon);
> }
>
> void monitor_cleanup(void)
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 03/10] monitor: Make MonitorQMP a child class of Monitor
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 01/10] monitor: Remove unused password prompting fields Kevin Wolf
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 02/10] monitor: Split monitor_init in HMP and QMP function Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 16:17 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 04/10] monitor: Create MonitorHMP with readline state Kevin Wolf
` (9 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Currently, struct Monitor mixes state that is only relevant for HMP,
state that is only relevant for QMP, and some actually shared state.
In particular, a MonitorQMP field is present in the state of any
monitor, even if it's not a QMP monitor and therefore doesn't use the
state.
As a first step towards a clean separation between QMP and HMP, let
MonitorQMP extend Monitor and create a MonitorQMP object only when the
monitor is actually a QMP monitor.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
monitor.c | 214 ++++++++++++++++++++++++++++++------------------------
1 file changed, 118 insertions(+), 96 deletions(-)
diff --git a/monitor.c b/monitor.c
index bb23cc0450..d18cf18393 100644
--- a/monitor.c
+++ b/monitor.c
@@ -166,26 +166,6 @@ struct MonFdset {
QLIST_ENTRY(MonFdset) next;
};
-typedef struct {
- JSONMessageParser parser;
- /*
- * When a client connects, we're in capabilities negotiation mode.
- * @commands is &qmp_cap_negotiation_commands then. When command
- * qmp_capabilities succeeds, we go into command mode, and
- * @command becomes &qmp_commands.
- */
- QmpCommandList *commands;
- bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
- bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
- /*
- * Protects qmp request/response queue.
- * Take monitor_lock first when you need both.
- */
- QemuMutex qmp_queue_lock;
- /* Input queue that holds all the parsed QMP requests */
- GQueue *qmp_requests;
-} MonitorQMP;
-
/*
* To prevent flooding clients, events can be throttled. The
* throttling is calculated globally, rather than per-Monitor
@@ -218,7 +198,6 @@ struct Monitor {
*/
ReadLineState *rs;
- MonitorQMP qmp;
gchar *mon_cpu_path;
mon_cmd_t *cmd_table;
QTAILQ_ENTRY(Monitor) entry;
@@ -239,6 +218,27 @@ struct Monitor {
int mux_out;
};
+typedef struct {
+ Monitor common;
+ JSONMessageParser parser;
+ /*
+ * When a client connects, we're in capabilities negotiation mode.
+ * @commands is &qmp_cap_negotiation_commands then. When command
+ * qmp_capabilities succeeds, we go into command mode, and
+ * @command becomes &qmp_commands.
+ */
+ QmpCommandList *commands;
+ bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
+ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
+ /*
+ * Protects qmp request/response queue.
+ * Take monitor_lock first when you need both.
+ */
+ QemuMutex qmp_queue_lock;
+ /* Input queue that holds all the parsed QMP requests */
+ GQueue *qmp_requests;
+} MonitorQMP;
+
/* Shared monitor I/O thread */
IOThread *mon_iothread;
@@ -247,7 +247,7 @@ QEMUBH *qmp_dispatcher_bh;
struct QMPRequest {
/* Owner of the request */
- Monitor *mon;
+ MonitorQMP *mon;
/*
* Request object to be handled or Error to be reported
* (exactly one of them is non-null)
@@ -355,18 +355,18 @@ static void qmp_request_free(QMPRequest *req)
}
/* Caller must hold mon->qmp.qmp_queue_lock */
-static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
+static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
{
- while (!g_queue_is_empty(mon->qmp.qmp_requests)) {
- qmp_request_free(g_queue_pop_head(mon->qmp.qmp_requests));
+ while (!g_queue_is_empty(mon->qmp_requests)) {
+ qmp_request_free(g_queue_pop_head(mon->qmp_requests));
}
}
-static void monitor_qmp_cleanup_queues(Monitor *mon)
+static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
{
- qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ qemu_mutex_lock(&mon->qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
- qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ qemu_mutex_unlock(&mon->qmp_queue_lock);
}
@@ -478,17 +478,17 @@ int monitor_printf(Monitor *mon, const char *fmt, ...)
return ret;
}
-static void qmp_send_response(Monitor *mon, const QDict *rsp)
+static void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
{
const QObject *data = QOBJECT(rsp);
QString *json;
- json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
- qobject_to_json(data);
+ json = mon->common.flags & MONITOR_USE_PRETTY ?
+ qobject_to_json_pretty(data) : qobject_to_json(data);
assert(json != NULL);
qstring_append_chr(json, '\n');
- monitor_puts(mon, qstring_get_str(json));
+ monitor_puts(&mon->common, qstring_get_str(json));
qobject_unref(json);
}
@@ -511,12 +511,17 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
{
Monitor *mon;
+ MonitorQMP *qmp_mon;
trace_monitor_protocol_event_emit(event, qdict);
QTAILQ_FOREACH(mon, &mon_list, entry) {
- if (monitor_is_qmp(mon)
- && mon->qmp.commands != &qmp_cap_negotiation_commands) {
- qmp_send_response(mon, qdict);
+ if (!monitor_is_qmp(mon)) {
+ continue;
+ }
+
+ qmp_mon = container_of(mon, MonitorQMP, common);
+ if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
+ qmp_send_response(qmp_mon, qdict);
}
}
}
@@ -710,29 +715,33 @@ static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
}
memset(mon, 0, sizeof(Monitor));
qemu_mutex_init(&mon->mon_lock);
- qemu_mutex_init(&mon->qmp.qmp_queue_lock);
mon->outbuf = qstring_new();
/* Use *mon_cmds by default. */
mon->cmd_table = mon_cmds;
mon->skip_flush = skip_flush;
mon->use_io_thread = use_io_thread;
- mon->qmp.qmp_requests = g_queue_new();
mon->flags = flags;
}
+static void monitor_data_destroy_qmp(MonitorQMP *mon)
+{
+ json_message_parser_destroy(&mon->parser);
+ qemu_mutex_destroy(&mon->qmp_queue_lock);
+ monitor_qmp_cleanup_req_queue_locked(mon);
+ g_queue_free(mon->qmp_requests);
+}
+
static void monitor_data_destroy(Monitor *mon)
{
g_free(mon->mon_cpu_path);
qemu_chr_fe_deinit(&mon->chr, false);
if (monitor_is_qmp(mon)) {
- json_message_parser_destroy(&mon->qmp.parser);
+ MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
+ monitor_data_destroy_qmp(qmp_mon);
}
readline_free(mon->rs);
qobject_unref(mon->outbuf);
qemu_mutex_destroy(&mon->mon_lock);
- qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
- monitor_qmp_cleanup_req_queue_locked(mon);
- g_queue_free(mon->qmp.qmp_requests);
}
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
@@ -1085,8 +1094,9 @@ static void query_commands_cb(QmpCommand *cmd, void *opaque)
CommandInfoList *qmp_query_commands(Error **errp)
{
CommandInfoList *list = NULL;
+ MonitorQMP *mon = container_of(cur_mon, MonitorQMP, common);
- qmp_for_each_command(cur_mon->qmp.commands, query_commands_cb, &list);
+ qmp_for_each_command(mon->commands, query_commands_cb, &list);
return list;
}
@@ -1153,16 +1163,16 @@ static void monitor_init_qmp_commands(void)
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}
-static bool qmp_oob_enabled(Monitor *mon)
+static bool qmp_oob_enabled(MonitorQMP *mon)
{
- return mon->qmp.capab[QMP_CAPABILITY_OOB];
+ return mon->capab[QMP_CAPABILITY_OOB];
}
-static void monitor_qmp_caps_reset(Monitor *mon)
+static void monitor_qmp_caps_reset(MonitorQMP *mon)
{
- memset(mon->qmp.capab_offered, 0, sizeof(mon->qmp.capab_offered));
- memset(mon->qmp.capab, 0, sizeof(mon->qmp.capab));
- mon->qmp.capab_offered[QMP_CAPABILITY_OOB] = mon->use_io_thread;
+ memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
+ memset(mon->capab, 0, sizeof(mon->capab));
+ mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
}
/*
@@ -1170,7 +1180,7 @@ static void monitor_qmp_caps_reset(Monitor *mon)
* On success, set mon->qmp.capab[], and return true.
* On error, set @errp, and return false.
*/
-static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
+static bool qmp_caps_accept(MonitorQMP *mon, QMPCapabilityList *list,
Error **errp)
{
GString *unavailable = NULL;
@@ -1179,7 +1189,7 @@ static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
memset(capab, 0, sizeof(capab));
for (; list; list = list->next) {
- if (!mon->qmp.capab_offered[list->value]) {
+ if (!mon->capab_offered[list->value]) {
if (!unavailable) {
unavailable = g_string_new(QMPCapability_str(list->value));
} else {
@@ -1196,25 +1206,27 @@ static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
return false;
}
- memcpy(mon->qmp.capab, capab, sizeof(capab));
+ memcpy(mon->capab, capab, sizeof(capab));
return true;
}
void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
Error **errp)
{
- if (cur_mon->qmp.commands == &qmp_commands) {
+ MonitorQMP *mon = container_of(cur_mon, MonitorQMP, common);
+
+ if (mon->commands == &qmp_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Capabilities negotiation is already complete, command "
"ignored");
return;
}
- if (!qmp_caps_accept(cur_mon, enable, errp)) {
+ if (!qmp_caps_accept(mon, enable, errp)) {
return;
}
- cur_mon->qmp.commands = &qmp_commands;
+ mon->commands = &qmp_commands;
}
/* Set the current CPU defined by the user. Callers must hold BQL. */
@@ -4121,27 +4133,27 @@ static int monitor_can_read(void *opaque)
* Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
* Nothing is emitted then.
*/
-static void monitor_qmp_respond(Monitor *mon, QDict *rsp)
+static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
{
if (rsp) {
qmp_send_response(mon, rsp);
}
}
-static void monitor_qmp_dispatch(Monitor *mon, QObject *req)
+static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
{
Monitor *old_mon;
QDict *rsp;
QDict *error;
old_mon = cur_mon;
- cur_mon = mon;
+ cur_mon = &mon->common;
- rsp = qmp_dispatch(mon->qmp.commands, req, qmp_oob_enabled(mon));
+ rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon));
cur_mon = old_mon;
- if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
+ if (mon->commands == &qmp_cap_negotiation_commands) {
error = qdict_get_qdict(rsp, "error");
if (error
&& !g_strcmp0(qdict_get_try_str(error, "class"),
@@ -4166,24 +4178,30 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req)
* 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
+ * be with qmp_mon->qmp_queue_lock held, and the caller is responsible
* to release it.
*/
static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
{
QMPRequest *req_obj = NULL;
Monitor *mon;
+ MonitorQMP *qmp_mon;
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH(mon, &mon_list, entry) {
- qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
- req_obj = g_queue_pop_head(mon->qmp.qmp_requests);
+ if (!monitor_is_qmp(mon)) {
+ continue;
+ }
+
+ qmp_mon = container_of(mon, MonitorQMP, common);
+ qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
+ req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
if (req_obj) {
/* With the lock of corresponding queue held */
break;
}
- qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
}
if (req_obj) {
@@ -4205,7 +4223,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
QDict *rsp;
bool need_resume;
- Monitor *mon;
+ MonitorQMP *mon;
if (!req_obj) {
return;
@@ -4214,8 +4232,8 @@ static void monitor_qmp_bh_dispatcher(void *data)
mon = req_obj->mon;
/* qmp_oob_enabled() might change after "qmp_capabilities" */
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);
+ 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;
@@ -4231,7 +4249,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
if (need_resume) {
/* Pairs with the monitor_suspend() in handle_qmp_command() */
- monitor_resume(mon);
+ monitor_resume(&mon->common);
}
qmp_request_free(req_obj);
@@ -4241,7 +4259,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
static void handle_qmp_command(void *opaque, QObject *req, Error *err)
{
- Monitor *mon = opaque;
+ MonitorQMP *mon = opaque;
QObject *id = NULL;
QDict *qdict;
QMPRequest *req_obj;
@@ -4273,7 +4291,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
req_obj->err = err;
/* Protect qmp_requests and fetching its length. */
- qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ qemu_mutex_lock(&mon->qmp_queue_lock);
/*
* Suspend the monitor when we can't queue more requests after
@@ -4282,8 +4300,8 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
* command, for backward compatibility.
*/
if (!qmp_oob_enabled(mon) ||
- mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
- monitor_suspend(mon);
+ mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
+ monitor_suspend(&mon->common);
}
/*
@@ -4291,9 +4309,9 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
* handled in time order. Ownership for req_obj, req,
* 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);
+ assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
+ g_queue_push_tail(mon->qmp_requests, req_obj);
+ qemu_mutex_unlock(&mon->qmp_queue_lock);
/* Kick the dispatcher routine */
qemu_bh_schedule(qmp_dispatcher_bh);
@@ -4301,9 +4319,9 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
{
- Monitor *mon = opaque;
+ MonitorQMP *mon = opaque;
- json_message_parser_feed(&mon->qmp.parser, (const char *) buf, size);
+ json_message_parser_feed(&mon->parser, (const char *) buf, size);
}
static void monitor_read(void *opaque, const uint8_t *buf, int size)
@@ -4389,7 +4407,7 @@ void monitor_resume(Monitor *mon)
trace_monitor_suspend(mon, -1);
}
-static QDict *qmp_greeting(Monitor *mon)
+static QDict *qmp_greeting(MonitorQMP *mon)
{
QList *cap_list = qlist_new();
QObject *ver = NULL;
@@ -4398,7 +4416,7 @@ static QDict *qmp_greeting(Monitor *mon)
qmp_marshal_query_version(NULL, &ver, NULL);
for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
- if (mon->qmp.capab_offered[cap]) {
+ if (mon->capab_offered[cap]) {
qlist_append_str(cap_list, QMPCapability_str(cap));
}
}
@@ -4411,11 +4429,11 @@ static QDict *qmp_greeting(Monitor *mon)
static void monitor_qmp_event(void *opaque, int event)
{
QDict *data;
- Monitor *mon = opaque;
+ MonitorQMP *mon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
- mon->qmp.commands = &qmp_cap_negotiation_commands;
+ mon->commands = &qmp_cap_negotiation_commands;
monitor_qmp_caps_reset(mon);
data = qmp_greeting(mon);
qmp_send_response(mon, data);
@@ -4430,8 +4448,8 @@ static void monitor_qmp_event(void *opaque, int event)
* is closed.
*/
monitor_qmp_cleanup_queues(mon);
- json_message_parser_destroy(&mon->qmp.parser);
- json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
+ json_message_parser_destroy(&mon->parser);
+ json_message_parser_init(&mon->parser, handle_qmp_command,
mon, NULL);
mon_refcount--;
monitor_fdsets_cleanup();
@@ -4593,30 +4611,34 @@ static void monitor_list_append(Monitor *mon)
static void monitor_qmp_setup_handlers_bh(void *opaque)
{
- Monitor *mon = opaque;
+ MonitorQMP *mon = opaque;
GMainContext *context;
- assert(mon->use_io_thread);
+ assert(mon->common.use_io_thread);
context = iothread_get_g_main_context(mon_iothread);
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);
+ qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
+ monitor_qmp_read, monitor_qmp_event,
+ NULL, &mon->common, context, true);
+ monitor_list_append(&mon->common);
}
static void monitor_init_qmp(Chardev *chr, int flags)
{
- Monitor *mon = g_malloc(sizeof(*mon));
+ MonitorQMP *mon = g_malloc0(sizeof(*mon));
/* Note: we run QMP monitor in I/O thread when @chr supports that */
- monitor_data_init(mon, flags, false,
+ monitor_data_init(&mon->common, flags, false,
qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
- qemu_chr_fe_init(&mon->chr, chr, &error_abort);
- qemu_chr_fe_set_echo(&mon->chr, true);
+ qemu_mutex_init(&mon->qmp_queue_lock);
+ mon->qmp_requests = g_queue_new();
- json_message_parser_init(&mon->qmp.parser, handle_qmp_command, mon, NULL);
- if (mon->use_io_thread) {
+ qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
+ qemu_chr_fe_set_echo(&mon->common.chr, true);
+
+ json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
+ if (mon->common.use_io_thread) {
/*
* Make sure the old iowatch is gone. It's possible when
* e.g. the chardev is in client mode, with wait=on.
@@ -4631,10 +4653,10 @@ static void monitor_init_qmp(Chardev *chr, int flags)
monitor_qmp_setup_handlers_bh, mon);
/* The bottom half will add @mon to @mon_list */
} else {
- qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
+ qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
monitor_qmp_read, monitor_qmp_event,
- NULL, mon, NULL, true);
- monitor_list_append(mon);
+ NULL, &mon->common, NULL, true);
+ monitor_list_append(&mon->common);
}
}
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 03/10] monitor: Make MonitorQMP a child class of Monitor
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 03/10] monitor: Make MonitorQMP a child class of Monitor Kevin Wolf
@ 2019-06-07 16:17 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 16:17 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Currently, struct Monitor mixes state that is only relevant for HMP,
> state that is only relevant for QMP, and some actually shared state.
> In particular, a MonitorQMP field is present in the state of any
> monitor, even if it's not a QMP monitor and therefore doesn't use the
> state.
>
> As a first step towards a clean separation between QMP and HMP, let
> MonitorQMP extend Monitor and create a MonitorQMP object only when the
> monitor is actually a QMP monitor.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> monitor.c | 214 ++++++++++++++++++++++++++++++------------------------
> 1 file changed, 118 insertions(+), 96 deletions(-)
>
> diff --git a/monitor.c b/monitor.c
> index bb23cc0450..d18cf18393 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -166,26 +166,6 @@ struct MonFdset {
> QLIST_ENTRY(MonFdset) next;
> };
>
> -typedef struct {
> - JSONMessageParser parser;
> - /*
> - * When a client connects, we're in capabilities negotiation mode.
> - * @commands is &qmp_cap_negotiation_commands then. When command
> - * qmp_capabilities succeeds, we go into command mode, and
> - * @command becomes &qmp_commands.
> - */
> - QmpCommandList *commands;
> - bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
> - bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
> - /*
> - * Protects qmp request/response queue.
> - * Take monitor_lock first when you need both.
> - */
> - QemuMutex qmp_queue_lock;
> - /* Input queue that holds all the parsed QMP requests */
> - GQueue *qmp_requests;
> -} MonitorQMP;
> -
> /*
> * To prevent flooding clients, events can be throttled. The
> * throttling is calculated globally, rather than per-Monitor
> @@ -218,7 +198,6 @@ struct Monitor {
> */
> ReadLineState *rs;
>
> - MonitorQMP qmp;
> gchar *mon_cpu_path;
> mon_cmd_t *cmd_table;
> QTAILQ_ENTRY(Monitor) entry;
> @@ -239,6 +218,27 @@ struct Monitor {
> int mux_out;
> };
>
> +typedef struct {
> + Monitor common;
> + JSONMessageParser parser;
> + /*
> + * When a client connects, we're in capabilities negotiation mode.
> + * @commands is &qmp_cap_negotiation_commands then. When command
> + * qmp_capabilities succeeds, we go into command mode, and
> + * @command becomes &qmp_commands.
> + */
> + QmpCommandList *commands;
> + bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
> + bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
> + /*
> + * Protects qmp request/response queue.
> + * Take monitor_lock first when you need both.
> + */
> + QemuMutex qmp_queue_lock;
> + /* Input queue that holds all the parsed QMP requests */
> + GQueue *qmp_requests;
> +} MonitorQMP;
> +
> /* Shared monitor I/O thread */
> IOThread *mon_iothread;
>
> @@ -247,7 +247,7 @@ QEMUBH *qmp_dispatcher_bh;
>
> struct QMPRequest {
> /* Owner of the request */
> - Monitor *mon;
> + MonitorQMP *mon;
> /*
> * Request object to be handled or Error to be reported
> * (exactly one of them is non-null)
> @@ -355,18 +355,18 @@ static void qmp_request_free(QMPRequest *req)
> }
>
> /* Caller must hold mon->qmp.qmp_queue_lock */
> -static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
> +static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
> {
> - while (!g_queue_is_empty(mon->qmp.qmp_requests)) {
> - qmp_request_free(g_queue_pop_head(mon->qmp.qmp_requests));
> + while (!g_queue_is_empty(mon->qmp_requests)) {
> + qmp_request_free(g_queue_pop_head(mon->qmp_requests));
> }
> }
>
> -static void monitor_qmp_cleanup_queues(Monitor *mon)
> +static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
> {
> - qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
> + qemu_mutex_lock(&mon->qmp_queue_lock);
> monitor_qmp_cleanup_req_queue_locked(mon);
> - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
> + qemu_mutex_unlock(&mon->qmp_queue_lock);
> }
>
>
> @@ -478,17 +478,17 @@ int monitor_printf(Monitor *mon, const char *fmt, ...)
> return ret;
> }
>
> -static void qmp_send_response(Monitor *mon, const QDict *rsp)
> +static void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
> {
> const QObject *data = QOBJECT(rsp);
> QString *json;
>
> - json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
> - qobject_to_json(data);
> + json = mon->common.flags & MONITOR_USE_PRETTY ?
> + qobject_to_json_pretty(data) : qobject_to_json(data);
> assert(json != NULL);
>
> qstring_append_chr(json, '\n');
> - monitor_puts(mon, qstring_get_str(json));
> + monitor_puts(&mon->common, qstring_get_str(json));
>
> qobject_unref(json);
> }
> @@ -511,12 +511,17 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
> static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
> {
> Monitor *mon;
> + MonitorQMP *qmp_mon;
>
> trace_monitor_protocol_event_emit(event, qdict);
> QTAILQ_FOREACH(mon, &mon_list, entry) {
> - if (monitor_is_qmp(mon)
> - && mon->qmp.commands != &qmp_cap_negotiation_commands) {
> - qmp_send_response(mon, qdict);
> + if (!monitor_is_qmp(mon)) {
> + continue;
> + }
> +
> + qmp_mon = container_of(mon, MonitorQMP, common);
> + if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
> + qmp_send_response(qmp_mon, qdict);
> }
> }
> }
> @@ -710,29 +715,33 @@ static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> }
> memset(mon, 0, sizeof(Monitor));
> qemu_mutex_init(&mon->mon_lock);
> - qemu_mutex_init(&mon->qmp.qmp_queue_lock);
> mon->outbuf = qstring_new();
> /* Use *mon_cmds by default. */
> mon->cmd_table = mon_cmds;
> mon->skip_flush = skip_flush;
> mon->use_io_thread = use_io_thread;
> - mon->qmp.qmp_requests = g_queue_new();
> mon->flags = flags;
> }
>
> +static void monitor_data_destroy_qmp(MonitorQMP *mon)
> +{
> + json_message_parser_destroy(&mon->parser);
> + qemu_mutex_destroy(&mon->qmp_queue_lock);
> + monitor_qmp_cleanup_req_queue_locked(mon);
> + g_queue_free(mon->qmp_requests);
> +}
> +
> static void monitor_data_destroy(Monitor *mon)
> {
> g_free(mon->mon_cpu_path);
> qemu_chr_fe_deinit(&mon->chr, false);
> if (monitor_is_qmp(mon)) {
> - json_message_parser_destroy(&mon->qmp.parser);
> + MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
> + monitor_data_destroy_qmp(qmp_mon);
> }
> readline_free(mon->rs);
> qobject_unref(mon->outbuf);
> qemu_mutex_destroy(&mon->mon_lock);
> - qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
> - monitor_qmp_cleanup_req_queue_locked(mon);
> - g_queue_free(mon->qmp.qmp_requests);
> }
>
> char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
> @@ -1085,8 +1094,9 @@ static void query_commands_cb(QmpCommand *cmd, void *opaque)
> CommandInfoList *qmp_query_commands(Error **errp)
> {
> CommandInfoList *list = NULL;
> + MonitorQMP *mon = container_of(cur_mon, MonitorQMP, common);
>
> - qmp_for_each_command(cur_mon->qmp.commands, query_commands_cb, &list);
> + qmp_for_each_command(mon->commands, query_commands_cb, &list);
>
> return list;
> }
> @@ -1153,16 +1163,16 @@ static void monitor_init_qmp_commands(void)
> qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
> }
>
> -static bool qmp_oob_enabled(Monitor *mon)
> +static bool qmp_oob_enabled(MonitorQMP *mon)
> {
> - return mon->qmp.capab[QMP_CAPABILITY_OOB];
> + return mon->capab[QMP_CAPABILITY_OOB];
> }
>
> -static void monitor_qmp_caps_reset(Monitor *mon)
> +static void monitor_qmp_caps_reset(MonitorQMP *mon)
> {
> - memset(mon->qmp.capab_offered, 0, sizeof(mon->qmp.capab_offered));
> - memset(mon->qmp.capab, 0, sizeof(mon->qmp.capab));
> - mon->qmp.capab_offered[QMP_CAPABILITY_OOB] = mon->use_io_thread;
> + memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
> + memset(mon->capab, 0, sizeof(mon->capab));
> + mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
> }
>
> /*
> @@ -1170,7 +1180,7 @@ static void monitor_qmp_caps_reset(Monitor *mon)
> * On success, set mon->qmp.capab[], and return true.
> * On error, set @errp, and return false.
> */
> -static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
> +static bool qmp_caps_accept(MonitorQMP *mon, QMPCapabilityList *list,
> Error **errp)
> {
> GString *unavailable = NULL;
> @@ -1179,7 +1189,7 @@ static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
> memset(capab, 0, sizeof(capab));
>
> for (; list; list = list->next) {
> - if (!mon->qmp.capab_offered[list->value]) {
> + if (!mon->capab_offered[list->value]) {
> if (!unavailable) {
> unavailable = g_string_new(QMPCapability_str(list->value));
> } else {
> @@ -1196,25 +1206,27 @@ static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
> return false;
> }
>
> - memcpy(mon->qmp.capab, capab, sizeof(capab));
> + memcpy(mon->capab, capab, sizeof(capab));
> return true;
> }
>
> void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
> Error **errp)
> {
> - if (cur_mon->qmp.commands == &qmp_commands) {
> + MonitorQMP *mon = container_of(cur_mon, MonitorQMP, common);
> +
> + if (mon->commands == &qmp_commands) {
> error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
> "Capabilities negotiation is already complete, command "
> "ignored");
> return;
> }
>
> - if (!qmp_caps_accept(cur_mon, enable, errp)) {
> + if (!qmp_caps_accept(mon, enable, errp)) {
> return;
> }
>
> - cur_mon->qmp.commands = &qmp_commands;
> + mon->commands = &qmp_commands;
> }
>
> /* Set the current CPU defined by the user. Callers must hold BQL. */
> @@ -4121,27 +4133,27 @@ static int monitor_can_read(void *opaque)
> * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
> * Nothing is emitted then.
> */
> -static void monitor_qmp_respond(Monitor *mon, QDict *rsp)
> +static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
> {
> if (rsp) {
> qmp_send_response(mon, rsp);
> }
> }
>
> -static void monitor_qmp_dispatch(Monitor *mon, QObject *req)
> +static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
> {
> Monitor *old_mon;
> QDict *rsp;
> QDict *error;
>
> old_mon = cur_mon;
> - cur_mon = mon;
> + cur_mon = &mon->common;
>
> - rsp = qmp_dispatch(mon->qmp.commands, req, qmp_oob_enabled(mon));
> + rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon));
>
> cur_mon = old_mon;
>
> - if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
> + if (mon->commands == &qmp_cap_negotiation_commands) {
> error = qdict_get_qdict(rsp, "error");
> if (error
> && !g_strcmp0(qdict_get_try_str(error, "class"),
> @@ -4166,24 +4178,30 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req)
> * 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
> + * be with qmp_mon->qmp_queue_lock held, and the caller is responsible
> * to release it.
> */
> static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
> {
> QMPRequest *req_obj = NULL;
> Monitor *mon;
> + MonitorQMP *qmp_mon;
>
> qemu_mutex_lock(&monitor_lock);
>
> QTAILQ_FOREACH(mon, &mon_list, entry) {
> - qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
> - req_obj = g_queue_pop_head(mon->qmp.qmp_requests);
> + if (!monitor_is_qmp(mon)) {
> + continue;
> + }
> +
> + qmp_mon = container_of(mon, MonitorQMP, common);
> + qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
> + req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
> if (req_obj) {
> /* With the lock of corresponding queue held */
> break;
> }
> - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
> + qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
> }
>
> if (req_obj) {
> @@ -4205,7 +4223,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
> QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
> QDict *rsp;
> bool need_resume;
> - Monitor *mon;
> + MonitorQMP *mon;
>
> if (!req_obj) {
> return;
> @@ -4214,8 +4232,8 @@ static void monitor_qmp_bh_dispatcher(void *data)
> mon = req_obj->mon;
> /* qmp_oob_enabled() might change after "qmp_capabilities" */
> 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);
> + 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;
> @@ -4231,7 +4249,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
>
> if (need_resume) {
> /* Pairs with the monitor_suspend() in handle_qmp_command() */
> - monitor_resume(mon);
> + monitor_resume(&mon->common);
> }
> qmp_request_free(req_obj);
>
> @@ -4241,7 +4259,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
>
> static void handle_qmp_command(void *opaque, QObject *req, Error *err)
> {
> - Monitor *mon = opaque;
> + MonitorQMP *mon = opaque;
> QObject *id = NULL;
> QDict *qdict;
> QMPRequest *req_obj;
> @@ -4273,7 +4291,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
> req_obj->err = err;
>
> /* Protect qmp_requests and fetching its length. */
> - qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
> + qemu_mutex_lock(&mon->qmp_queue_lock);
>
> /*
> * Suspend the monitor when we can't queue more requests after
> @@ -4282,8 +4300,8 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
> * command, for backward compatibility.
> */
> if (!qmp_oob_enabled(mon) ||
> - mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
> - monitor_suspend(mon);
> + mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
> + monitor_suspend(&mon->common);
> }
>
> /*
> @@ -4291,9 +4309,9 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
> * handled in time order. Ownership for req_obj, req,
> * 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);
> + assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
> + g_queue_push_tail(mon->qmp_requests, req_obj);
> + qemu_mutex_unlock(&mon->qmp_queue_lock);
>
> /* Kick the dispatcher routine */
> qemu_bh_schedule(qmp_dispatcher_bh);
> @@ -4301,9 +4319,9 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
>
> static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
> {
> - Monitor *mon = opaque;
> + MonitorQMP *mon = opaque;
>
> - json_message_parser_feed(&mon->qmp.parser, (const char *) buf, size);
> + json_message_parser_feed(&mon->parser, (const char *) buf, size);
> }
>
> static void monitor_read(void *opaque, const uint8_t *buf, int size)
> @@ -4389,7 +4407,7 @@ void monitor_resume(Monitor *mon)
> trace_monitor_suspend(mon, -1);
> }
>
> -static QDict *qmp_greeting(Monitor *mon)
> +static QDict *qmp_greeting(MonitorQMP *mon)
> {
> QList *cap_list = qlist_new();
> QObject *ver = NULL;
> @@ -4398,7 +4416,7 @@ static QDict *qmp_greeting(Monitor *mon)
> qmp_marshal_query_version(NULL, &ver, NULL);
>
> for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
> - if (mon->qmp.capab_offered[cap]) {
> + if (mon->capab_offered[cap]) {
> qlist_append_str(cap_list, QMPCapability_str(cap));
> }
> }
> @@ -4411,11 +4429,11 @@ static QDict *qmp_greeting(Monitor *mon)
> static void monitor_qmp_event(void *opaque, int event)
> {
> QDict *data;
> - Monitor *mon = opaque;
> + MonitorQMP *mon = opaque;
>
> switch (event) {
> case CHR_EVENT_OPENED:
> - mon->qmp.commands = &qmp_cap_negotiation_commands;
> + mon->commands = &qmp_cap_negotiation_commands;
> monitor_qmp_caps_reset(mon);
> data = qmp_greeting(mon);
> qmp_send_response(mon, data);
> @@ -4430,8 +4448,8 @@ static void monitor_qmp_event(void *opaque, int event)
> * is closed.
> */
> monitor_qmp_cleanup_queues(mon);
> - json_message_parser_destroy(&mon->qmp.parser);
> - json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
> + json_message_parser_destroy(&mon->parser);
> + json_message_parser_init(&mon->parser, handle_qmp_command,
> mon, NULL);
> mon_refcount--;
> monitor_fdsets_cleanup();
> @@ -4593,30 +4611,34 @@ static void monitor_list_append(Monitor *mon)
>
> static void monitor_qmp_setup_handlers_bh(void *opaque)
> {
> - Monitor *mon = opaque;
> + MonitorQMP *mon = opaque;
> GMainContext *context;
>
> - assert(mon->use_io_thread);
> + assert(mon->common.use_io_thread);
> context = iothread_get_g_main_context(mon_iothread);
> 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);
> + qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
> + monitor_qmp_read, monitor_qmp_event,
> + NULL, &mon->common, context, true);
> + monitor_list_append(&mon->common);
> }
>
> static void monitor_init_qmp(Chardev *chr, int flags)
> {
> - Monitor *mon = g_malloc(sizeof(*mon));
> + MonitorQMP *mon = g_malloc0(sizeof(*mon));
>
> /* Note: we run QMP monitor in I/O thread when @chr supports that */
> - monitor_data_init(mon, flags, false,
> + monitor_data_init(&mon->common, flags, false,
> qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
>
> - qemu_chr_fe_init(&mon->chr, chr, &error_abort);
> - qemu_chr_fe_set_echo(&mon->chr, true);
> + qemu_mutex_init(&mon->qmp_queue_lock);
> + mon->qmp_requests = g_queue_new();
>
> - json_message_parser_init(&mon->qmp.parser, handle_qmp_command, mon, NULL);
> - if (mon->use_io_thread) {
> + qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
> + qemu_chr_fe_set_echo(&mon->common.chr, true);
> +
> + json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
> + if (mon->common.use_io_thread) {
> /*
> * Make sure the old iowatch is gone. It's possible when
> * e.g. the chardev is in client mode, with wait=on.
> @@ -4631,10 +4653,10 @@ static void monitor_init_qmp(Chardev *chr, int flags)
> monitor_qmp_setup_handlers_bh, mon);
> /* The bottom half will add @mon to @mon_list */
> } else {
> - qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
> + qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
> monitor_qmp_read, monitor_qmp_event,
> - NULL, mon, NULL, true);
> - monitor_list_append(mon);
> + NULL, &mon->common, NULL, true);
> + monitor_list_append(&mon->common);
> }
> }
>
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 04/10] monitor: Create MonitorHMP with readline state
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (2 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 03/10] monitor: Make MonitorQMP a child class of Monitor Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 16:32 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 05/10] monitor: Move cmd_table to MonitorHMP Kevin Wolf
` (8 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
The ReadLineState in Monitor is only used for HMP monitors. Create
MonitorHMP and move it there.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/monitor/monitor.h | 5 +-
hmp.c | 4 +-
monitor.c | 123 +++++++++++++++++++++-----------------
3 files changed, 75 insertions(+), 57 deletions(-)
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 86656297f1..1ba354f811 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -7,6 +7,7 @@
#include "qemu/readline.h"
extern __thread Monitor *cur_mon;
+typedef struct MonitorHMP MonitorHMP;
/* flags for monitor_init */
/* 0x01 unused */
@@ -35,8 +36,8 @@ void monitor_flush(Monitor *mon);
int monitor_set_cpu(int cpu_index);
int monitor_get_cpu_index(void);
-void monitor_read_command(Monitor *mon, int show_prompt);
-int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
+void monitor_read_command(MonitorHMP *mon, int show_prompt);
+int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
void *opaque);
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
diff --git a/hmp.c b/hmp.c
index be5e345c6f..99414cd39c 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1943,6 +1943,8 @@ static void hmp_change_read_arg(void *opaque, const char *password,
void hmp_change(Monitor *mon, const QDict *qdict)
{
+ /* FIXME Make MonitorHMP public and use container_of */
+ MonitorHMP *hmp_mon = (MonitorHMP *) mon;
const char *device = qdict_get_str(qdict, "device");
const char *target = qdict_get_str(qdict, "target");
const char *arg = qdict_get_try_str(qdict, "arg");
@@ -1960,7 +1962,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
if (strcmp(target, "passwd") == 0 ||
strcmp(target, "password") == 0) {
if (!arg) {
- monitor_read_password(mon, hmp_change_read_arg, NULL);
+ monitor_read_password(hmp_mon, hmp_change_read_arg, NULL);
return;
}
}
diff --git a/monitor.c b/monitor.c
index d18cf18393..810f3dcf9c 100644
--- a/monitor.c
+++ b/monitor.c
@@ -190,14 +190,6 @@ struct Monitor {
bool skip_flush;
bool use_io_thread;
- /*
- * State used only in the thread "owning" the monitor.
- * If @use_io_thread, this is @mon_iothread.
- * Else, it's the main thread.
- * These members can be safely accessed without locks.
- */
- ReadLineState *rs;
-
gchar *mon_cpu_path;
mon_cmd_t *cmd_table;
QTAILQ_ENTRY(Monitor) entry;
@@ -218,6 +210,17 @@ struct Monitor {
int mux_out;
};
+struct MonitorHMP {
+ Monitor common;
+ /*
+ * State used only in the thread "owning" the monitor.
+ * If @use_io_thread, this is @mon_iothread.
+ * Else, it's the main thread.
+ * These members can be safely accessed without locks.
+ */
+ ReadLineState *rs;
+};
+
typedef struct {
Monitor common;
JSONMessageParser parser;
@@ -324,7 +327,7 @@ bool monitor_cur_is_qmp(void)
return cur_mon && monitor_is_qmp(cur_mon);
}
-void monitor_read_command(Monitor *mon, int show_prompt)
+void monitor_read_command(MonitorHMP *mon, int show_prompt)
{
if (!mon->rs)
return;
@@ -334,7 +337,7 @@ void monitor_read_command(Monitor *mon, int show_prompt)
readline_show_prompt(mon->rs);
}
-int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
+int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
void *opaque)
{
if (mon->rs) {
@@ -342,7 +345,8 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
/* prompt is printed on return from the command handler */
return 0;
} else {
- monitor_printf(mon, "terminal does not support password prompting\n");
+ monitor_printf(&mon->common,
+ "terminal does not support password prompting\n");
return -ENOTTY;
}
}
@@ -703,7 +707,7 @@ static void monitor_qapi_event_init(void)
qapi_event_throttle_equal);
}
-static void handle_hmp_command(Monitor *mon, const char *cmdline);
+static void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
static void monitor_iothread_init(void);
@@ -738,8 +742,10 @@ static void monitor_data_destroy(Monitor *mon)
if (monitor_is_qmp(mon)) {
MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
monitor_data_destroy_qmp(qmp_mon);
+ } else {
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+ readline_free(hmp_mon->rs);
}
- readline_free(mon->rs);
qobject_unref(mon->outbuf);
qemu_mutex_destroy(&mon->mon_lock);
}
@@ -748,12 +754,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, hmp;
+ Monitor *old_mon;
+ MonitorHMP hmp = {};
- monitor_data_init(&hmp, 0, true, false);
+ monitor_data_init(&hmp.common, 0, true, false);
old_mon = cur_mon;
- cur_mon = &hmp;
+ cur_mon = &hmp.common;
if (has_cpu_index) {
int ret = monitor_set_cpu(cpu_index);
@@ -768,16 +775,16 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
handle_hmp_command(&hmp, command_line);
cur_mon = old_mon;
- qemu_mutex_lock(&hmp.mon_lock);
- if (qstring_get_length(hmp.outbuf) > 0) {
- output = g_strdup(qstring_get_str(hmp.outbuf));
+ qemu_mutex_lock(&hmp.common.mon_lock);
+ if (qstring_get_length(hmp.common.outbuf) > 0) {
+ output = g_strdup(qstring_get_str(hmp.common.outbuf));
} else {
output = g_strdup("");
}
- qemu_mutex_unlock(&hmp.mon_lock);
+ qemu_mutex_unlock(&hmp.common.mon_lock);
out:
- monitor_data_destroy(&hmp);
+ monitor_data_destroy(&hmp.common);
return output;
}
@@ -1341,14 +1348,15 @@ static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict)
static void hmp_info_history(Monitor *mon, const QDict *qdict)
{
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
int i;
const char *str;
- if (!mon->rs)
+ if (!hmp_mon->rs)
return;
i = 0;
for(;;) {
- str = readline_get_history(mon->rs, i);
+ str = readline_get_history(hmp_mon->rs, i);
if (!str)
break;
monitor_printf(mon, "%d: '%s'\n", i, str);
@@ -3048,11 +3056,12 @@ static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
* Do not assume the return value points into @table! It doesn't when
* the command is found in a sub-command table.
*/
-static const mon_cmd_t *monitor_parse_command(Monitor *mon,
+static const mon_cmd_t *monitor_parse_command(MonitorHMP *hmp_mon,
const char *cmdp_start,
const char **cmdp,
mon_cmd_t *table)
{
+ Monitor *mon = &hmp_mon->common;
const char *p;
const mon_cmd_t *cmd;
char cmdname[256];
@@ -3083,7 +3092,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
*cmdp = p;
/* search sub command */
if (cmd->sub_table != NULL && *p != '\0') {
- return monitor_parse_command(mon, cmdp_start, cmdp, cmd->sub_table);
+ return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table);
}
return cmd;
@@ -3460,7 +3469,7 @@ fail:
return NULL;
}
-static void handle_hmp_command(Monitor *mon, const char *cmdline)
+static void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
{
QDict *qdict;
const mon_cmd_t *cmd;
@@ -3468,26 +3477,26 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline)
trace_handle_hmp_command(mon, cmdline);
- cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
+ cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->common.cmd_table);
if (!cmd) {
return;
}
- qdict = monitor_parse_arguments(mon, &cmdline, cmd);
+ qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd);
if (!qdict) {
while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) {
cmdline--;
}
- monitor_printf(mon, "Try \"help %.*s\" for more information\n",
+ monitor_printf(&mon->common, "Try \"help %.*s\" for more information\n",
(int)(cmdline - cmd_start), cmd_start);
return;
}
- cmd->cmd(mon, qdict);
+ cmd->cmd(&mon->common, qdict);
qobject_unref(qdict);
}
-static void cmd_completion(Monitor *mon, const char *name, const char *list)
+static void cmd_completion(MonitorHMP *mon, const char *name, const char *list)
{
const char *p, *pstart;
char cmd[128];
@@ -3511,7 +3520,7 @@ static void cmd_completion(Monitor *mon, const char *name, const char *list)
}
}
-static void file_completion(Monitor *mon, const char *input)
+static void file_completion(MonitorHMP *mon, const char *input)
{
DIR *ffs;
struct dirent *d;
@@ -4000,7 +4009,7 @@ void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
}
}
-static void monitor_find_completion_by_table(Monitor *mon,
+static void monitor_find_completion_by_table(MonitorHMP *mon,
const mon_cmd_t *cmd_table,
char **args,
int nb_args)
@@ -4095,7 +4104,7 @@ static void monitor_find_completion_by_table(Monitor *mon,
static void monitor_find_completion(void *opaque,
const char *cmdline)
{
- Monitor *mon = opaque;
+ MonitorHMP *mon = opaque;
char *args[MAX_ARGS];
int nb_args, len;
@@ -4115,7 +4124,7 @@ static void monitor_find_completion(void *opaque,
}
/* 2. auto complete according to args */
- monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
+ monitor_find_completion_by_table(mon, mon->common.cmd_table, args, nb_args);
cleanup:
free_cmdline_args(args, nb_args);
@@ -4326,19 +4335,21 @@ static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
static void monitor_read(void *opaque, const uint8_t *buf, int size)
{
+ MonitorHMP *mon;
Monitor *old_mon = cur_mon;
int i;
cur_mon = opaque;
+ mon = container_of(cur_mon, MonitorHMP, common);
- if (cur_mon->rs) {
+ if (mon->rs) {
for (i = 0; i < size; i++)
- readline_handle_byte(cur_mon->rs, buf[i]);
+ readline_handle_byte(mon->rs, buf[i]);
} else {
if (size == 0 || buf[size - 1] != 0)
monitor_printf(cur_mon, "corrupted command\n");
else
- handle_hmp_command(cur_mon, (char *)buf);
+ handle_hmp_command(mon, (char *)buf);
}
cur_mon = old_mon;
@@ -4347,11 +4358,11 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque)
{
- Monitor *mon = opaque;
+ MonitorHMP *mon = opaque;
- monitor_suspend(mon);
+ monitor_suspend(&mon->common);
handle_hmp_command(mon, cmdline);
- monitor_resume(mon);
+ monitor_resume(&mon->common);
}
int monitor_suspend(Monitor *mon)
@@ -4397,8 +4408,9 @@ void monitor_resume(Monitor *mon)
}
if (!monitor_is_qmp(mon)) {
- assert(mon->rs);
- readline_show_prompt(mon->rs);
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+ assert(hmp_mon->rs);
+ readline_show_prompt(hmp_mon->rs);
}
aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
@@ -4460,6 +4472,7 @@ static void monitor_qmp_event(void *opaque, int event)
static void monitor_event(void *opaque, int event)
{
Monitor *mon = opaque;
+ MonitorHMP *hmp_mon = container_of(cur_mon, MonitorHMP, common);
switch (event) {
case CHR_EVENT_MUX_IN:
@@ -4467,7 +4480,7 @@ static void monitor_event(void *opaque, int event)
mon->mux_out = 0;
qemu_mutex_unlock(&mon->mon_lock);
if (mon->reset_seen) {
- readline_restart(mon->rs);
+ readline_restart(hmp_mon->rs);
monitor_resume(mon);
monitor_flush(mon);
} else {
@@ -4494,8 +4507,8 @@ static void monitor_event(void *opaque, int event)
monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
"information\n", QEMU_VERSION);
if (!mon->mux_out) {
- readline_restart(mon->rs);
- readline_show_prompt(mon->rs);
+ readline_restart(hmp_mon->rs);
+ readline_show_prompt(hmp_mon->rs);
}
mon->reset_seen = 1;
mon_refcount++;
@@ -4556,15 +4569,17 @@ void monitor_init_globals(void)
static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
const char *fmt, ...)
{
+ MonitorHMP *mon = opaque;
va_list ap;
va_start(ap, fmt);
- monitor_vprintf(opaque, fmt, ap);
+ monitor_vprintf(&mon->common, fmt, ap);
va_end(ap);
}
static void monitor_readline_flush(void *opaque)
{
- monitor_flush(opaque);
+ MonitorHMP *mon = opaque;
+ monitor_flush(&mon->common);
}
/*
@@ -4662,11 +4677,11 @@ static void monitor_init_qmp(Chardev *chr, int flags)
static void monitor_init_hmp(Chardev *chr, int flags)
{
- Monitor *mon = g_malloc(sizeof(*mon));
+ MonitorHMP *mon = g_malloc0(sizeof(*mon));
bool use_readline = flags & MONITOR_USE_READLINE;
- monitor_data_init(mon, flags, false, false);
- qemu_chr_fe_init(&mon->chr, chr, &error_abort);
+ monitor_data_init(&mon->common, flags, false, false);
+ qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
if (use_readline) {
mon->rs = readline_init(monitor_readline_printf,
@@ -4676,9 +4691,9 @@ static void monitor_init_hmp(Chardev *chr, int flags)
monitor_read_command(mon, 0);
}
- qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
- monitor_event, NULL, mon, NULL, true);
- monitor_list_append(mon);
+ qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read, monitor_read,
+ monitor_event, NULL, &mon->common, NULL, true);
+ monitor_list_append(&mon->common);
}
void monitor_init(Chardev *chr, int flags)
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 04/10] monitor: Create MonitorHMP with readline state
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 04/10] monitor: Create MonitorHMP with readline state Kevin Wolf
@ 2019-06-07 16:32 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 16:32 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> The ReadLineState in Monitor is only used for HMP monitors. Create
> MonitorHMP and move it there.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> include/monitor/monitor.h | 5 +-
> hmp.c | 4 +-
> monitor.c | 123 +++++++++++++++++++++-----------------
> 3 files changed, 75 insertions(+), 57 deletions(-)
>
> diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
> index 86656297f1..1ba354f811 100644
> --- a/include/monitor/monitor.h
> +++ b/include/monitor/monitor.h
> @@ -7,6 +7,7 @@
> #include "qemu/readline.h"
>
> extern __thread Monitor *cur_mon;
> +typedef struct MonitorHMP MonitorHMP;
>
> /* flags for monitor_init */
> /* 0x01 unused */
> @@ -35,8 +36,8 @@ void monitor_flush(Monitor *mon);
> int monitor_set_cpu(int cpu_index);
> int monitor_get_cpu_index(void);
>
> -void monitor_read_command(Monitor *mon, int show_prompt);
> -int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
> +void monitor_read_command(MonitorHMP *mon, int show_prompt);
> +int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
> void *opaque);
>
> AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
> diff --git a/hmp.c b/hmp.c
> index be5e345c6f..99414cd39c 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1943,6 +1943,8 @@ static void hmp_change_read_arg(void *opaque, const char *password,
>
> void hmp_change(Monitor *mon, const QDict *qdict)
> {
> + /* FIXME Make MonitorHMP public and use container_of */
> + MonitorHMP *hmp_mon = (MonitorHMP *) mon;
> const char *device = qdict_get_str(qdict, "device");
> const char *target = qdict_get_str(qdict, "target");
> const char *arg = qdict_get_try_str(qdict, "arg");
> @@ -1960,7 +1962,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
> if (strcmp(target, "passwd") == 0 ||
> strcmp(target, "password") == 0) {
> if (!arg) {
> - monitor_read_password(mon, hmp_change_read_arg, NULL);
> + monitor_read_password(hmp_mon, hmp_change_read_arg, NULL);
> return;
> }
> }
> diff --git a/monitor.c b/monitor.c
> index d18cf18393..810f3dcf9c 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -190,14 +190,6 @@ struct Monitor {
> bool skip_flush;
> bool use_io_thread;
>
> - /*
> - * State used only in the thread "owning" the monitor.
> - * If @use_io_thread, this is @mon_iothread.
> - * Else, it's the main thread.
> - * These members can be safely accessed without locks.
> - */
> - ReadLineState *rs;
> -
> gchar *mon_cpu_path;
> mon_cmd_t *cmd_table;
> QTAILQ_ENTRY(Monitor) entry;
> @@ -218,6 +210,17 @@ struct Monitor {
> int mux_out;
> };
>
> +struct MonitorHMP {
> + Monitor common;
> + /*
> + * State used only in the thread "owning" the monitor.
> + * If @use_io_thread, this is @mon_iothread.
> + * Else, it's the main thread.
> + * These members can be safely accessed without locks.
> + */
> + ReadLineState *rs;
> +};
> +
> typedef struct {
> Monitor common;
> JSONMessageParser parser;
> @@ -324,7 +327,7 @@ bool monitor_cur_is_qmp(void)
> return cur_mon && monitor_is_qmp(cur_mon);
> }
>
> -void monitor_read_command(Monitor *mon, int show_prompt)
> +void monitor_read_command(MonitorHMP *mon, int show_prompt)
> {
> if (!mon->rs)
> return;
> @@ -334,7 +337,7 @@ void monitor_read_command(Monitor *mon, int show_prompt)
> readline_show_prompt(mon->rs);
> }
>
> -int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
> +int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
> void *opaque)
> {
> if (mon->rs) {
> @@ -342,7 +345,8 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
> /* prompt is printed on return from the command handler */
> return 0;
> } else {
> - monitor_printf(mon, "terminal does not support password prompting\n");
> + monitor_printf(&mon->common,
> + "terminal does not support password prompting\n");
> return -ENOTTY;
> }
> }
> @@ -703,7 +707,7 @@ static void monitor_qapi_event_init(void)
> qapi_event_throttle_equal);
> }
>
> -static void handle_hmp_command(Monitor *mon, const char *cmdline);
> +static void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
>
> static void monitor_iothread_init(void);
>
> @@ -738,8 +742,10 @@ static void monitor_data_destroy(Monitor *mon)
> if (monitor_is_qmp(mon)) {
> MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
> monitor_data_destroy_qmp(qmp_mon);
> + } else {
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> + readline_free(hmp_mon->rs);
> }
> - readline_free(mon->rs);
> qobject_unref(mon->outbuf);
> qemu_mutex_destroy(&mon->mon_lock);
> }
> @@ -748,12 +754,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, hmp;
> + Monitor *old_mon;
> + MonitorHMP hmp = {};
>
> - monitor_data_init(&hmp, 0, true, false);
> + monitor_data_init(&hmp.common, 0, true, false);
>
> old_mon = cur_mon;
> - cur_mon = &hmp;
> + cur_mon = &hmp.common;
>
> if (has_cpu_index) {
> int ret = monitor_set_cpu(cpu_index);
> @@ -768,16 +775,16 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
> handle_hmp_command(&hmp, command_line);
> cur_mon = old_mon;
>
> - qemu_mutex_lock(&hmp.mon_lock);
> - if (qstring_get_length(hmp.outbuf) > 0) {
> - output = g_strdup(qstring_get_str(hmp.outbuf));
> + qemu_mutex_lock(&hmp.common.mon_lock);
> + if (qstring_get_length(hmp.common.outbuf) > 0) {
> + output = g_strdup(qstring_get_str(hmp.common.outbuf));
> } else {
> output = g_strdup("");
> }
> - qemu_mutex_unlock(&hmp.mon_lock);
> + qemu_mutex_unlock(&hmp.common.mon_lock);
>
> out:
> - monitor_data_destroy(&hmp);
> + monitor_data_destroy(&hmp.common);
> return output;
> }
>
> @@ -1341,14 +1348,15 @@ static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict)
>
> static void hmp_info_history(Monitor *mon, const QDict *qdict)
> {
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> int i;
> const char *str;
>
> - if (!mon->rs)
> + if (!hmp_mon->rs)
> return;
> i = 0;
> for(;;) {
> - str = readline_get_history(mon->rs, i);
> + str = readline_get_history(hmp_mon->rs, i);
> if (!str)
> break;
> monitor_printf(mon, "%d: '%s'\n", i, str);
> @@ -3048,11 +3056,12 @@ static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
> * Do not assume the return value points into @table! It doesn't when
> * the command is found in a sub-command table.
> */
> -static const mon_cmd_t *monitor_parse_command(Monitor *mon,
> +static const mon_cmd_t *monitor_parse_command(MonitorHMP *hmp_mon,
> const char *cmdp_start,
> const char **cmdp,
> mon_cmd_t *table)
> {
> + Monitor *mon = &hmp_mon->common;
> const char *p;
> const mon_cmd_t *cmd;
> char cmdname[256];
> @@ -3083,7 +3092,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
> *cmdp = p;
> /* search sub command */
> if (cmd->sub_table != NULL && *p != '\0') {
> - return monitor_parse_command(mon, cmdp_start, cmdp, cmd->sub_table);
> + return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table);
> }
>
> return cmd;
> @@ -3460,7 +3469,7 @@ fail:
> return NULL;
> }
>
> -static void handle_hmp_command(Monitor *mon, const char *cmdline)
> +static void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
> {
> QDict *qdict;
> const mon_cmd_t *cmd;
> @@ -3468,26 +3477,26 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline)
>
> trace_handle_hmp_command(mon, cmdline);
>
> - cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
> + cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->common.cmd_table);
> if (!cmd) {
> return;
> }
>
> - qdict = monitor_parse_arguments(mon, &cmdline, cmd);
> + qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd);
> if (!qdict) {
> while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) {
> cmdline--;
> }
> - monitor_printf(mon, "Try \"help %.*s\" for more information\n",
> + monitor_printf(&mon->common, "Try \"help %.*s\" for more information\n",
> (int)(cmdline - cmd_start), cmd_start);
> return;
> }
>
> - cmd->cmd(mon, qdict);
> + cmd->cmd(&mon->common, qdict);
> qobject_unref(qdict);
> }
>
> -static void cmd_completion(Monitor *mon, const char *name, const char *list)
> +static void cmd_completion(MonitorHMP *mon, const char *name, const char *list)
> {
> const char *p, *pstart;
> char cmd[128];
> @@ -3511,7 +3520,7 @@ static void cmd_completion(Monitor *mon, const char *name, const char *list)
> }
> }
>
> -static void file_completion(Monitor *mon, const char *input)
> +static void file_completion(MonitorHMP *mon, const char *input)
> {
> DIR *ffs;
> struct dirent *d;
> @@ -4000,7 +4009,7 @@ void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
> }
> }
>
> -static void monitor_find_completion_by_table(Monitor *mon,
> +static void monitor_find_completion_by_table(MonitorHMP *mon,
> const mon_cmd_t *cmd_table,
> char **args,
> int nb_args)
> @@ -4095,7 +4104,7 @@ static void monitor_find_completion_by_table(Monitor *mon,
> static void monitor_find_completion(void *opaque,
> const char *cmdline)
> {
> - Monitor *mon = opaque;
> + MonitorHMP *mon = opaque;
> char *args[MAX_ARGS];
> int nb_args, len;
>
> @@ -4115,7 +4124,7 @@ static void monitor_find_completion(void *opaque,
> }
>
> /* 2. auto complete according to args */
> - monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
> + monitor_find_completion_by_table(mon, mon->common.cmd_table, args, nb_args);
>
> cleanup:
> free_cmdline_args(args, nb_args);
> @@ -4326,19 +4335,21 @@ static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
>
> static void monitor_read(void *opaque, const uint8_t *buf, int size)
> {
> + MonitorHMP *mon;
> Monitor *old_mon = cur_mon;
> int i;
>
> cur_mon = opaque;
> + mon = container_of(cur_mon, MonitorHMP, common);
>
> - if (cur_mon->rs) {
> + if (mon->rs) {
> for (i = 0; i < size; i++)
> - readline_handle_byte(cur_mon->rs, buf[i]);
> + readline_handle_byte(mon->rs, buf[i]);
> } else {
> if (size == 0 || buf[size - 1] != 0)
> monitor_printf(cur_mon, "corrupted command\n");
> else
> - handle_hmp_command(cur_mon, (char *)buf);
> + handle_hmp_command(mon, (char *)buf);
> }
>
> cur_mon = old_mon;
> @@ -4347,11 +4358,11 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
> static void monitor_command_cb(void *opaque, const char *cmdline,
> void *readline_opaque)
> {
> - Monitor *mon = opaque;
> + MonitorHMP *mon = opaque;
>
> - monitor_suspend(mon);
> + monitor_suspend(&mon->common);
> handle_hmp_command(mon, cmdline);
> - monitor_resume(mon);
> + monitor_resume(&mon->common);
> }
>
> int monitor_suspend(Monitor *mon)
> @@ -4397,8 +4408,9 @@ void monitor_resume(Monitor *mon)
> }
>
> if (!monitor_is_qmp(mon)) {
> - assert(mon->rs);
> - readline_show_prompt(mon->rs);
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> + assert(hmp_mon->rs);
> + readline_show_prompt(hmp_mon->rs);
> }
>
> aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
> @@ -4460,6 +4472,7 @@ static void monitor_qmp_event(void *opaque, int event)
> static void monitor_event(void *opaque, int event)
> {
> Monitor *mon = opaque;
> + MonitorHMP *hmp_mon = container_of(cur_mon, MonitorHMP, common);
>
> switch (event) {
> case CHR_EVENT_MUX_IN:
> @@ -4467,7 +4480,7 @@ static void monitor_event(void *opaque, int event)
> mon->mux_out = 0;
> qemu_mutex_unlock(&mon->mon_lock);
> if (mon->reset_seen) {
> - readline_restart(mon->rs);
> + readline_restart(hmp_mon->rs);
> monitor_resume(mon);
> monitor_flush(mon);
> } else {
> @@ -4494,8 +4507,8 @@ static void monitor_event(void *opaque, int event)
> monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
> "information\n", QEMU_VERSION);
> if (!mon->mux_out) {
> - readline_restart(mon->rs);
> - readline_show_prompt(mon->rs);
> + readline_restart(hmp_mon->rs);
> + readline_show_prompt(hmp_mon->rs);
> }
> mon->reset_seen = 1;
> mon_refcount++;
> @@ -4556,15 +4569,17 @@ void monitor_init_globals(void)
> static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
> const char *fmt, ...)
> {
> + MonitorHMP *mon = opaque;
> va_list ap;
> va_start(ap, fmt);
> - monitor_vprintf(opaque, fmt, ap);
> + monitor_vprintf(&mon->common, fmt, ap);
> va_end(ap);
> }
>
> static void monitor_readline_flush(void *opaque)
> {
> - monitor_flush(opaque);
> + MonitorHMP *mon = opaque;
> + monitor_flush(&mon->common);
> }
>
> /*
> @@ -4662,11 +4677,11 @@ static void monitor_init_qmp(Chardev *chr, int flags)
>
> static void monitor_init_hmp(Chardev *chr, int flags)
> {
> - Monitor *mon = g_malloc(sizeof(*mon));
> + MonitorHMP *mon = g_malloc0(sizeof(*mon));
> bool use_readline = flags & MONITOR_USE_READLINE;
>
> - monitor_data_init(mon, flags, false, false);
> - qemu_chr_fe_init(&mon->chr, chr, &error_abort);
> + monitor_data_init(&mon->common, flags, false, false);
> + qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
>
> if (use_readline) {
> mon->rs = readline_init(monitor_readline_printf,
> @@ -4676,9 +4691,9 @@ static void monitor_init_hmp(Chardev *chr, int flags)
> monitor_read_command(mon, 0);
> }
>
> - qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
> - monitor_event, NULL, mon, NULL, true);
> - monitor_list_append(mon);
> + qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read, monitor_read,
> + monitor_event, NULL, &mon->common, NULL, true);
> + monitor_list_append(&mon->common);
> }
>
> void monitor_init(Chardev *chr, int flags)
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 05/10] monitor: Move cmd_table to MonitorHMP
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (3 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 04/10] monitor: Create MonitorHMP with readline state Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 16:35 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 06/10] Move monitor.c to monitor/misc.c Kevin Wolf
` (7 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Monitor.cmd_table contains the handlers for HMP commands, so there is no
reason to keep it in the state shared with QMP. Move it to MonitorHMP.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
monitor.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/monitor.c b/monitor.c
index 810f3dcf9c..d964dd1969 100644
--- a/monitor.c
+++ b/monitor.c
@@ -191,7 +191,6 @@ struct Monitor {
bool use_io_thread;
gchar *mon_cpu_path;
- mon_cmd_t *cmd_table;
QTAILQ_ENTRY(Monitor) entry;
/*
@@ -219,6 +218,7 @@ struct MonitorHMP {
* These members can be safely accessed without locks.
*/
ReadLineState *rs;
+ mon_cmd_t *cmd_table;
};
typedef struct {
@@ -720,13 +720,19 @@ static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
memset(mon, 0, sizeof(Monitor));
qemu_mutex_init(&mon->mon_lock);
mon->outbuf = qstring_new();
- /* Use *mon_cmds by default. */
- mon->cmd_table = mon_cmds;
mon->skip_flush = skip_flush;
mon->use_io_thread = use_io_thread;
mon->flags = flags;
}
+static void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
+{
+ monitor_data_init(&mon->common, flags, skip_flush, false);
+
+ /* Use *mon_cmds by default. */
+ mon->cmd_table = mon_cmds;
+}
+
static void monitor_data_destroy_qmp(MonitorQMP *mon)
{
json_message_parser_destroy(&mon->parser);
@@ -757,7 +763,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
Monitor *old_mon;
MonitorHMP hmp = {};
- monitor_data_init(&hmp.common, 0, true, false);
+ monitor_data_init_hmp(&hmp, 0, true);
old_mon = cur_mon;
cur_mon = &hmp.common;
@@ -1002,6 +1008,7 @@ static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
static void help_cmd(Monitor *mon, const char *name)
{
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
char *args[MAX_ARGS];
int nb_args = 0;
@@ -1024,7 +1031,7 @@ static void help_cmd(Monitor *mon, const char *name)
}
/* 2. dump the contents according to parsed args */
- help_cmd_dump(mon, mon->cmd_table, args, nb_args, 0);
+ help_cmd_dump(mon, hmp_mon->cmd_table, args, nb_args, 0);
free_cmdline_args(args, nb_args);
}
@@ -3477,7 +3484,7 @@ static void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
trace_handle_hmp_command(mon, cmdline);
- cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->common.cmd_table);
+ cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
if (!cmd) {
return;
}
@@ -4124,7 +4131,7 @@ static void monitor_find_completion(void *opaque,
}
/* 2. auto complete according to args */
- monitor_find_completion_by_table(mon, mon->common.cmd_table, args, nb_args);
+ monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
cleanup:
free_cmdline_args(args, nb_args);
@@ -4680,7 +4687,7 @@ static void monitor_init_hmp(Chardev *chr, int flags)
MonitorHMP *mon = g_malloc0(sizeof(*mon));
bool use_readline = flags & MONITOR_USE_READLINE;
- monitor_data_init(&mon->common, flags, false, false);
+ monitor_data_init_hmp(mon, flags, false);
qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
if (use_readline) {
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 05/10] monitor: Move cmd_table to MonitorHMP
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 05/10] monitor: Move cmd_table to MonitorHMP Kevin Wolf
@ 2019-06-07 16:35 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 16:35 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Monitor.cmd_table contains the handlers for HMP commands, so there is no
> reason to keep it in the state shared with QMP. Move it to MonitorHMP.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> monitor.c | 23 +++++++++++++++--------
> 1 file changed, 15 insertions(+), 8 deletions(-)
>
> diff --git a/monitor.c b/monitor.c
> index 810f3dcf9c..d964dd1969 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -191,7 +191,6 @@ struct Monitor {
> bool use_io_thread;
>
> gchar *mon_cpu_path;
> - mon_cmd_t *cmd_table;
> QTAILQ_ENTRY(Monitor) entry;
>
> /*
> @@ -219,6 +218,7 @@ struct MonitorHMP {
> * These members can be safely accessed without locks.
> */
> ReadLineState *rs;
> + mon_cmd_t *cmd_table;
> };
>
> typedef struct {
> @@ -720,13 +720,19 @@ static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> memset(mon, 0, sizeof(Monitor));
> qemu_mutex_init(&mon->mon_lock);
> mon->outbuf = qstring_new();
> - /* Use *mon_cmds by default. */
> - mon->cmd_table = mon_cmds;
> mon->skip_flush = skip_flush;
> mon->use_io_thread = use_io_thread;
> mon->flags = flags;
> }
>
> +static void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
> +{
> + monitor_data_init(&mon->common, flags, skip_flush, false);
> +
> + /* Use *mon_cmds by default. */
> + mon->cmd_table = mon_cmds;
> +}
> +
> static void monitor_data_destroy_qmp(MonitorQMP *mon)
> {
> json_message_parser_destroy(&mon->parser);
> @@ -757,7 +763,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
> Monitor *old_mon;
> MonitorHMP hmp = {};
>
> - monitor_data_init(&hmp.common, 0, true, false);
> + monitor_data_init_hmp(&hmp, 0, true);
>
> old_mon = cur_mon;
> cur_mon = &hmp.common;
> @@ -1002,6 +1008,7 @@ static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
>
> static void help_cmd(Monitor *mon, const char *name)
> {
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> char *args[MAX_ARGS];
> int nb_args = 0;
>
> @@ -1024,7 +1031,7 @@ static void help_cmd(Monitor *mon, const char *name)
> }
>
> /* 2. dump the contents according to parsed args */
> - help_cmd_dump(mon, mon->cmd_table, args, nb_args, 0);
> + help_cmd_dump(mon, hmp_mon->cmd_table, args, nb_args, 0);
>
> free_cmdline_args(args, nb_args);
> }
> @@ -3477,7 +3484,7 @@ static void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
>
> trace_handle_hmp_command(mon, cmdline);
>
> - cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->common.cmd_table);
> + cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
> if (!cmd) {
> return;
> }
> @@ -4124,7 +4131,7 @@ static void monitor_find_completion(void *opaque,
> }
>
> /* 2. auto complete according to args */
> - monitor_find_completion_by_table(mon, mon->common.cmd_table, args, nb_args);
> + monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
>
> cleanup:
> free_cmdline_args(args, nb_args);
> @@ -4680,7 +4687,7 @@ static void monitor_init_hmp(Chardev *chr, int flags)
> MonitorHMP *mon = g_malloc0(sizeof(*mon));
> bool use_readline = flags & MONITOR_USE_READLINE;
>
> - monitor_data_init(&mon->common, flags, false, false);
> + monitor_data_init_hmp(mon, flags, false);
> qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
>
> if (use_readline) {
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 06/10] Move monitor.c to monitor/misc.c
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (4 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 05/10] monitor: Move cmd_table to MonitorHMP Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 16:39 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 07/10] monitor: Create monitor_int.h with common definitions Kevin Wolf
` (6 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Create a new monitor/ subdirectory and move monitor.c there. As the plan
is to move the monitor core into separate files, use the chance to
rename it to misc.c.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
monitor.c => monitor/misc.c | 0
Makefile.target | 3 ++-
monitor/Makefile.objs | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
rename monitor.c => monitor/misc.c (100%)
create mode 100644 monitor/Makefile.objs
diff --git a/monitor.c b/monitor/misc.c
similarity index 100%
rename from monitor.c
rename to monitor/misc.c
diff --git a/Makefile.target b/Makefile.target
index ecd856e3a3..72c267f7dc 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -148,9 +148,10 @@ endif #CONFIG_BSD_USER
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
-obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
+obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o numa.o
obj-y += qtest.o
obj-y += hw/
+obj-y += monitor/
obj-y += qapi/
obj-y += memory.o
obj-y += memory_mapping.o
diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
new file mode 100644
index 0000000000..e783b0616b
--- /dev/null
+++ b/monitor/Makefile.objs
@@ -0,0 +1 @@
+obj-y += misc.o
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 06/10] Move monitor.c to monitor/misc.c
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 06/10] Move monitor.c to monitor/misc.c Kevin Wolf
@ 2019-06-07 16:39 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 16:39 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Create a new monitor/ subdirectory and move monitor.c there. As the plan
> is to move the monitor core into separate files, use the chance to
> rename it to misc.c.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
> monitor.c => monitor/misc.c | 0
> Makefile.target | 3 ++-
> monitor/Makefile.objs | 1 +
This patch should probably also create monitor/trace-events
and move those events with it.
Dave
> 3 files changed, 3 insertions(+), 1 deletion(-)
> rename monitor.c => monitor/misc.c (100%)
> create mode 100644 monitor/Makefile.objs
>
> diff --git a/monitor.c b/monitor/misc.c
> similarity index 100%
> rename from monitor.c
> rename to monitor/misc.c
> diff --git a/Makefile.target b/Makefile.target
> index ecd856e3a3..72c267f7dc 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -148,9 +148,10 @@ endif #CONFIG_BSD_USER
> #########################################################
> # System emulator target
> ifdef CONFIG_SOFTMMU
> -obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
> +obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o numa.o
> obj-y += qtest.o
> obj-y += hw/
> +obj-y += monitor/
> obj-y += qapi/
> obj-y += memory.o
> obj-y += memory_mapping.o
> diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
> new file mode 100644
> index 0000000000..e783b0616b
> --- /dev/null
> +++ b/monitor/Makefile.objs
> @@ -0,0 +1 @@
> +obj-y += misc.o
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 07/10] monitor: Create monitor_int.h with common definitions
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (5 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 06/10] Move monitor.c to monitor/misc.c Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 16:52 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 08/10] monitor: Split out monitor/qmp.c Kevin Wolf
` (5 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Before we can split monitor.c, we need to create a header file that
contains the common definitions that will be used by multiple source
files.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
monitor/monitor_int.h | 147 ++++++++++++++++++++++++++++++++++++++++++
monitor/misc.c | 110 +------------------------------
2 files changed, 148 insertions(+), 109 deletions(-)
create mode 100644 monitor/monitor_int.h
diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
new file mode 100644
index 0000000000..ab87013b6f
--- /dev/null
+++ b/monitor/monitor_int.h
@@ -0,0 +1,147 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MONITOR_INT_H
+#define MONITOR_INT_H
+
+#include "qemu-common.h"
+#include "monitor/monitor.h"
+
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/json-parser.h"
+#include "qapi/qapi-commands.h"
+
+#include "qemu/readline.h"
+#include "chardev/char-fe.h"
+
+/*
+ * Supported types:
+ *
+ * 'F' filename
+ * 'B' block device name
+ * 's' string (accept optional quote)
+ * 'S' it just appends the rest of the string (accept optional quote)
+ * 'O' option string of the form NAME=VALUE,...
+ * parsed according to QemuOptsList given by its name
+ * Example: 'device:O' uses qemu_device_opts.
+ * Restriction: only lists with empty desc are supported
+ * TODO lift the restriction
+ * 'i' 32 bit integer
+ * 'l' target long (32 or 64 bit)
+ * 'M' Non-negative target long (32 or 64 bit), in user mode the
+ * value is multiplied by 2^20 (think Mebibyte)
+ * 'o' octets (aka bytes)
+ * user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
+ * K, k suffix, which multiplies the value by 2^60 for suffixes E
+ * and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
+ * 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
+ * 'T' double
+ * user mode accepts an optional ms, us, ns suffix,
+ * which divides the value by 1e3, 1e6, 1e9, respectively
+ * '/' optional gdb-like print format (like "/10x")
+ *
+ * '?' optional type (for all types, except '/')
+ * '.' other form of optional type (for 'i' and 'l')
+ * 'b' boolean
+ * user mode accepts "on" or "off"
+ * '-' optional parameter (eg. '-f')
+ *
+ */
+
+typedef struct mon_cmd_t {
+ const char *name;
+ const char *args_type;
+ const char *params;
+ const char *help;
+ const char *flags; /* p=preconfig */
+ void (*cmd)(Monitor *mon, const QDict *qdict);
+ /* @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
+ * used, and cmd of 1st level plays the role of help function.
+ */
+ struct mon_cmd_t *sub_table;
+ void (*command_completion)(ReadLineState *rs, int nb_args, const char *str);
+} mon_cmd_t;
+
+struct Monitor {
+ CharBackend chr;
+ int reset_seen;
+ int flags;
+ int suspend_cnt; /* Needs to be accessed atomically */
+ bool skip_flush;
+ bool use_io_thread;
+
+ gchar *mon_cpu_path;
+ QTAILQ_ENTRY(Monitor) entry;
+
+ /*
+ * The per-monitor lock. We can't access guest memory when holding
+ * the lock.
+ */
+ QemuMutex mon_lock;
+
+ /*
+ * Members that are protected by the per-monitor lock
+ */
+ QLIST_HEAD(, mon_fd_t) fds;
+ QString *outbuf;
+ guint out_watch;
+ /* Read under either BQL or mon_lock, written with BQL+mon_lock. */
+ int mux_out;
+};
+
+struct MonitorHMP {
+ Monitor common;
+ /*
+ * State used only in the thread "owning" the monitor.
+ * If @use_io_thread, this is @mon_iothread.
+ * Else, it's the main thread.
+ * These members can be safely accessed without locks.
+ */
+ ReadLineState *rs;
+ mon_cmd_t *cmd_table;
+};
+
+typedef struct {
+ Monitor common;
+ JSONMessageParser parser;
+ /*
+ * When a client connects, we're in capabilities negotiation mode.
+ * @commands is &qmp_cap_negotiation_commands then. When command
+ * qmp_capabilities succeeds, we go into command mode, and
+ * @command becomes &qmp_commands.
+ */
+ QmpCommandList *commands;
+ bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
+ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
+ /*
+ * Protects qmp request/response queue.
+ * Take monitor_lock first when you need both.
+ */
+ QemuMutex qmp_queue_lock;
+ /* Input queue that holds all the parsed QMP requests */
+ GQueue *qmp_requests;
+} MonitorQMP;
+
+#endif
diff --git a/monitor/misc.c b/monitor/misc.c
index d964dd1969..6ae7561105 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -23,6 +23,7 @@
*/
#include "qemu/osdep.h"
+#include "monitor_int.h"
#include "qemu/units.h"
#include <dirent.h>
#include "cpu.h"
@@ -91,55 +92,6 @@
#include "hw/s390x/storage-attributes.h"
#endif
-/*
- * Supported types:
- *
- * 'F' filename
- * 'B' block device name
- * 's' string (accept optional quote)
- * 'S' it just appends the rest of the string (accept optional quote)
- * 'O' option string of the form NAME=VALUE,...
- * parsed according to QemuOptsList given by its name
- * Example: 'device:O' uses qemu_device_opts.
- * Restriction: only lists with empty desc are supported
- * TODO lift the restriction
- * 'i' 32 bit integer
- * 'l' target long (32 or 64 bit)
- * 'M' Non-negative target long (32 or 64 bit), in user mode the
- * value is multiplied by 2^20 (think Mebibyte)
- * 'o' octets (aka bytes)
- * user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
- * K, k suffix, which multiplies the value by 2^60 for suffixes E
- * and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
- * 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
- * 'T' double
- * user mode accepts an optional ms, us, ns suffix,
- * which divides the value by 1e3, 1e6, 1e9, respectively
- * '/' optional gdb-like print format (like "/10x")
- *
- * '?' optional type (for all types, except '/')
- * '.' other form of optional type (for 'i' and 'l')
- * 'b' boolean
- * user mode accepts "on" or "off"
- * '-' optional parameter (eg. '-f')
- *
- */
-
-typedef struct mon_cmd_t {
- const char *name;
- const char *args_type;
- const char *params;
- const char *help;
- const char *flags; /* p=preconfig */
- void (*cmd)(Monitor *mon, const QDict *qdict);
- /* @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
- * used, and cmd of 1st level plays the role of help function.
- */
- struct mon_cmd_t *sub_table;
- void (*command_completion)(ReadLineState *rs, int nb_args, const char *str);
-} mon_cmd_t;
-
/* file descriptors passed via SCM_RIGHTS */
typedef struct mon_fd_t mon_fd_t;
struct mon_fd_t {
@@ -182,66 +134,6 @@ typedef struct {
int64_t rate; /* Minimum time (in ns) between two events */
} MonitorQAPIEventConf;
-struct Monitor {
- CharBackend chr;
- int reset_seen;
- int flags;
- int suspend_cnt; /* Needs to be accessed atomically */
- bool skip_flush;
- bool use_io_thread;
-
- gchar *mon_cpu_path;
- QTAILQ_ENTRY(Monitor) entry;
-
- /*
- * The per-monitor lock. We can't access guest memory when holding
- * the lock.
- */
- QemuMutex mon_lock;
-
- /*
- * Members that are protected by the per-monitor lock
- */
- QLIST_HEAD(, mon_fd_t) fds;
- QString *outbuf;
- guint out_watch;
- /* Read under either BQL or mon_lock, written with BQL+mon_lock. */
- int mux_out;
-};
-
-struct MonitorHMP {
- Monitor common;
- /*
- * State used only in the thread "owning" the monitor.
- * If @use_io_thread, this is @mon_iothread.
- * Else, it's the main thread.
- * These members can be safely accessed without locks.
- */
- ReadLineState *rs;
- mon_cmd_t *cmd_table;
-};
-
-typedef struct {
- Monitor common;
- JSONMessageParser parser;
- /*
- * When a client connects, we're in capabilities negotiation mode.
- * @commands is &qmp_cap_negotiation_commands then. When command
- * qmp_capabilities succeeds, we go into command mode, and
- * @command becomes &qmp_commands.
- */
- QmpCommandList *commands;
- bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
- bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
- /*
- * Protects qmp request/response queue.
- * Take monitor_lock first when you need both.
- */
- QemuMutex qmp_queue_lock;
- /* Input queue that holds all the parsed QMP requests */
- GQueue *qmp_requests;
-} MonitorQMP;
-
/* Shared monitor I/O thread */
IOThread *mon_iothread;
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 07/10] monitor: Create monitor_int.h with common definitions
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 07/10] monitor: Create monitor_int.h with common definitions Kevin Wolf
@ 2019-06-07 16:52 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 16:52 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Before we can split monitor.c, we need to create a header file that
> contains the common definitions that will be used by multiple source
> files.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> monitor/monitor_int.h | 147 ++++++++++++++++++++++++++++++++++++++++++
> monitor/misc.c | 110 +------------------------------
> 2 files changed, 148 insertions(+), 109 deletions(-)
> create mode 100644 monitor/monitor_int.h
>
> diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
> new file mode 100644
> index 0000000000..ab87013b6f
> --- /dev/null
> +++ b/monitor/monitor_int.h
> @@ -0,0 +1,147 @@
> +/*
> + * QEMU monitor
> + *
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#ifndef MONITOR_INT_H
> +#define MONITOR_INT_H
> +
> +#include "qemu-common.h"
> +#include "monitor/monitor.h"
> +
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/json-parser.h"
> +#include "qapi/qapi-commands.h"
> +
> +#include "qemu/readline.h"
> +#include "chardev/char-fe.h"
> +
> +/*
> + * Supported types:
> + *
> + * 'F' filename
> + * 'B' block device name
> + * 's' string (accept optional quote)
> + * 'S' it just appends the rest of the string (accept optional quote)
> + * 'O' option string of the form NAME=VALUE,...
> + * parsed according to QemuOptsList given by its name
> + * Example: 'device:O' uses qemu_device_opts.
> + * Restriction: only lists with empty desc are supported
> + * TODO lift the restriction
> + * 'i' 32 bit integer
> + * 'l' target long (32 or 64 bit)
> + * 'M' Non-negative target long (32 or 64 bit), in user mode the
> + * value is multiplied by 2^20 (think Mebibyte)
> + * 'o' octets (aka bytes)
> + * user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
> + * K, k suffix, which multiplies the value by 2^60 for suffixes E
> + * and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
> + * 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
> + * 'T' double
> + * user mode accepts an optional ms, us, ns suffix,
> + * which divides the value by 1e3, 1e6, 1e9, respectively
> + * '/' optional gdb-like print format (like "/10x")
> + *
> + * '?' optional type (for all types, except '/')
> + * '.' other form of optional type (for 'i' and 'l')
> + * 'b' boolean
> + * user mode accepts "on" or "off"
> + * '-' optional parameter (eg. '-f')
> + *
> + */
> +
> +typedef struct mon_cmd_t {
> + const char *name;
> + const char *args_type;
> + const char *params;
> + const char *help;
> + const char *flags; /* p=preconfig */
> + void (*cmd)(Monitor *mon, const QDict *qdict);
> + /* @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
> + * used, and cmd of 1st level plays the role of help function.
> + */
> + struct mon_cmd_t *sub_table;
> + void (*command_completion)(ReadLineState *rs, int nb_args, const char *str);
> +} mon_cmd_t;
> +
> +struct Monitor {
> + CharBackend chr;
> + int reset_seen;
> + int flags;
> + int suspend_cnt; /* Needs to be accessed atomically */
> + bool skip_flush;
> + bool use_io_thread;
> +
> + gchar *mon_cpu_path;
> + QTAILQ_ENTRY(Monitor) entry;
> +
> + /*
> + * The per-monitor lock. We can't access guest memory when holding
> + * the lock.
> + */
> + QemuMutex mon_lock;
> +
> + /*
> + * Members that are protected by the per-monitor lock
> + */
> + QLIST_HEAD(, mon_fd_t) fds;
> + QString *outbuf;
> + guint out_watch;
> + /* Read under either BQL or mon_lock, written with BQL+mon_lock. */
> + int mux_out;
> +};
> +
> +struct MonitorHMP {
> + Monitor common;
> + /*
> + * State used only in the thread "owning" the monitor.
> + * If @use_io_thread, this is @mon_iothread.
> + * Else, it's the main thread.
> + * These members can be safely accessed without locks.
> + */
> + ReadLineState *rs;
> + mon_cmd_t *cmd_table;
> +};
> +
> +typedef struct {
> + Monitor common;
> + JSONMessageParser parser;
> + /*
> + * When a client connects, we're in capabilities negotiation mode.
> + * @commands is &qmp_cap_negotiation_commands then. When command
> + * qmp_capabilities succeeds, we go into command mode, and
> + * @command becomes &qmp_commands.
> + */
> + QmpCommandList *commands;
> + bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
> + bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
> + /*
> + * Protects qmp request/response queue.
> + * Take monitor_lock first when you need both.
> + */
> + QemuMutex qmp_queue_lock;
> + /* Input queue that holds all the parsed QMP requests */
> + GQueue *qmp_requests;
> +} MonitorQMP;
> +
> +#endif
> diff --git a/monitor/misc.c b/monitor/misc.c
> index d964dd1969..6ae7561105 100644
> --- a/monitor/misc.c
> +++ b/monitor/misc.c
> @@ -23,6 +23,7 @@
> */
>
> #include "qemu/osdep.h"
> +#include "monitor_int.h"
> #include "qemu/units.h"
> #include <dirent.h>
> #include "cpu.h"
> @@ -91,55 +92,6 @@
> #include "hw/s390x/storage-attributes.h"
> #endif
>
> -/*
> - * Supported types:
> - *
> - * 'F' filename
> - * 'B' block device name
> - * 's' string (accept optional quote)
> - * 'S' it just appends the rest of the string (accept optional quote)
> - * 'O' option string of the form NAME=VALUE,...
> - * parsed according to QemuOptsList given by its name
> - * Example: 'device:O' uses qemu_device_opts.
> - * Restriction: only lists with empty desc are supported
> - * TODO lift the restriction
> - * 'i' 32 bit integer
> - * 'l' target long (32 or 64 bit)
> - * 'M' Non-negative target long (32 or 64 bit), in user mode the
> - * value is multiplied by 2^20 (think Mebibyte)
> - * 'o' octets (aka bytes)
> - * user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
> - * K, k suffix, which multiplies the value by 2^60 for suffixes E
> - * and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
> - * 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
> - * 'T' double
> - * user mode accepts an optional ms, us, ns suffix,
> - * which divides the value by 1e3, 1e6, 1e9, respectively
> - * '/' optional gdb-like print format (like "/10x")
> - *
> - * '?' optional type (for all types, except '/')
> - * '.' other form of optional type (for 'i' and 'l')
> - * 'b' boolean
> - * user mode accepts "on" or "off"
> - * '-' optional parameter (eg. '-f')
> - *
> - */
> -
> -typedef struct mon_cmd_t {
> - const char *name;
> - const char *args_type;
> - const char *params;
> - const char *help;
> - const char *flags; /* p=preconfig */
> - void (*cmd)(Monitor *mon, const QDict *qdict);
> - /* @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
> - * used, and cmd of 1st level plays the role of help function.
> - */
> - struct mon_cmd_t *sub_table;
> - void (*command_completion)(ReadLineState *rs, int nb_args, const char *str);
> -} mon_cmd_t;
> -
> /* file descriptors passed via SCM_RIGHTS */
> typedef struct mon_fd_t mon_fd_t;
> struct mon_fd_t {
> @@ -182,66 +134,6 @@ typedef struct {
> int64_t rate; /* Minimum time (in ns) between two events */
> } MonitorQAPIEventConf;
>
> -struct Monitor {
> - CharBackend chr;
> - int reset_seen;
> - int flags;
> - int suspend_cnt; /* Needs to be accessed atomically */
> - bool skip_flush;
> - bool use_io_thread;
> -
> - gchar *mon_cpu_path;
> - QTAILQ_ENTRY(Monitor) entry;
> -
> - /*
> - * The per-monitor lock. We can't access guest memory when holding
> - * the lock.
> - */
> - QemuMutex mon_lock;
> -
> - /*
> - * Members that are protected by the per-monitor lock
> - */
> - QLIST_HEAD(, mon_fd_t) fds;
> - QString *outbuf;
> - guint out_watch;
> - /* Read under either BQL or mon_lock, written with BQL+mon_lock. */
> - int mux_out;
> -};
> -
> -struct MonitorHMP {
> - Monitor common;
> - /*
> - * State used only in the thread "owning" the monitor.
> - * If @use_io_thread, this is @mon_iothread.
> - * Else, it's the main thread.
> - * These members can be safely accessed without locks.
> - */
> - ReadLineState *rs;
> - mon_cmd_t *cmd_table;
> -};
> -
> -typedef struct {
> - Monitor common;
> - JSONMessageParser parser;
> - /*
> - * When a client connects, we're in capabilities negotiation mode.
> - * @commands is &qmp_cap_negotiation_commands then. When command
> - * qmp_capabilities succeeds, we go into command mode, and
> - * @command becomes &qmp_commands.
> - */
> - QmpCommandList *commands;
> - bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
> - bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
> - /*
> - * Protects qmp request/response queue.
> - * Take monitor_lock first when you need both.
> - */
> - QemuMutex qmp_queue_lock;
> - /* Input queue that holds all the parsed QMP requests */
> - GQueue *qmp_requests;
> -} MonitorQMP;
> -
> /* Shared monitor I/O thread */
> IOThread *mon_iothread;
>
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 08/10] monitor: Split out monitor/qmp.c
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (6 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 07/10] monitor: Create monitor_int.h with common definitions Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 16:59 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 09/10] monitor: Split out monitor/hmp.c Kevin Wolf
` (4 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Move QMP infrastructure from monitor/misc.c to monitor/qmp.c. This is
code that can be shared for all targets, so compile it only once.
The amount of function and particularly extern variables in
monitor_int.h is probably a bit larger than it needs to be, but this way
no non-trivial code modifications are needed. The interfaces between QMP
and the monitor core can be cleaned up later.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/monitor/monitor.h | 1 +
monitor/monitor_int.h | 30 ++-
monitor/misc.c | 394 +------------------------------------
monitor/qmp.c | 404 ++++++++++++++++++++++++++++++++++++++
Makefile.objs | 1 +
monitor/Makefile.objs | 1 +
6 files changed, 445 insertions(+), 386 deletions(-)
create mode 100644 monitor/qmp.c
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 1ba354f811..7bbab05320 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -21,6 +21,7 @@ bool monitor_cur_is_qmp(void);
void monitor_init_globals(void);
void monitor_init(Chardev *chr, int flags);
+void monitor_init_qmp(Chardev *chr, int flags);
void monitor_cleanup(void);
int monitor_suspend(Monitor *mon);
diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
index ab87013b6f..487618392f 100644
--- a/monitor/monitor_int.h
+++ b/monitor/monitor_int.h
@@ -30,10 +30,11 @@
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/json-parser.h"
-#include "qapi/qapi-commands.h"
+#include "qapi/qmp/dispatch.h"
#include "qemu/readline.h"
#include "chardev/char-fe.h"
+#include "sysemu/iothread.h"
/*
* Supported types:
@@ -144,4 +145,31 @@ typedef struct {
GQueue *qmp_requests;
} MonitorQMP;
+/**
+ * Is @mon a QMP monitor?
+ */
+static inline bool monitor_is_qmp(const Monitor *mon)
+{
+ return (mon->flags & MONITOR_USE_CONTROL);
+}
+
+typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
+extern IOThread *mon_iothread;
+extern QEMUBH *qmp_dispatcher_bh;
+extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
+extern QemuMutex monitor_lock;
+extern MonitorList mon_list;
+extern int mon_refcount;
+
+int monitor_puts(Monitor *mon, const char *str);
+void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
+ bool use_io_thread);
+int monitor_can_read(void *opaque);
+void monitor_list_append(Monitor *mon);
+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);
+
#endif
diff --git a/monitor/misc.c b/monitor/misc.c
index 6ae7561105..6c67f0978c 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -140,51 +140,29 @@ IOThread *mon_iothread;
/* Bottom half to dispatch the requests received from I/O thread */
QEMUBH *qmp_dispatcher_bh;
-struct QMPRequest {
- /* Owner of the request */
- MonitorQMP *mon;
- /*
- * Request object to be handled or Error to be reported
- * (exactly one of them is non-null)
- */
- QObject *req;
- Error *err;
-};
-typedef struct QMPRequest QMPRequest;
-
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
-static QemuMutex monitor_lock;
+QemuMutex monitor_lock;
static GHashTable *monitor_qapi_event_state;
-static QTAILQ_HEAD(, Monitor) mon_list;
+MonitorList mon_list;
static bool monitor_destroyed;
/* Protects mon_fdsets */
static QemuMutex mon_fdsets_lock;
static QLIST_HEAD(, MonFdset) mon_fdsets;
-static int mon_refcount;
+int mon_refcount;
static mon_cmd_t mon_cmds[];
static mon_cmd_t info_cmds[];
-QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
-
__thread Monitor *cur_mon;
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque);
-/**
- * Is @mon a QMP monitor?
- */
-static inline bool monitor_is_qmp(const Monitor *mon)
-{
- return (mon->flags & MONITOR_USE_CONTROL);
-}
-
/**
* Is @mon is using readline?
* Note: not all HMP monitors use readline, e.g., gdbserver has a
@@ -243,28 +221,6 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
}
}
-static void qmp_request_free(QMPRequest *req)
-{
- qobject_unref(req->req);
- error_free(req->err);
- g_free(req);
-}
-
-/* Caller must hold mon->qmp.qmp_queue_lock */
-static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
-{
- while (!g_queue_is_empty(mon->qmp_requests)) {
- qmp_request_free(g_queue_pop_head(mon->qmp_requests));
- }
-}
-
-static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
-{
- qemu_mutex_lock(&mon->qmp_queue_lock);
- monitor_qmp_cleanup_req_queue_locked(mon);
- qemu_mutex_unlock(&mon->qmp_queue_lock);
-}
-
static void monitor_flush_locked(Monitor *mon);
@@ -324,7 +280,7 @@ void monitor_flush(Monitor *mon)
}
/* flush at every end of line */
-static int monitor_puts(Monitor *mon, const char *str)
+int monitor_puts(Monitor *mon, const char *str)
{
int i;
char c;
@@ -374,21 +330,6 @@ int monitor_printf(Monitor *mon, const char *fmt, ...)
return ret;
}
-static void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
-{
- const QObject *data = QOBJECT(rsp);
- QString *json;
-
- json = mon->common.flags & MONITOR_USE_PRETTY ?
- qobject_to_json_pretty(data) : qobject_to_json(data);
- assert(json != NULL);
-
- qstring_append_chr(json, '\n');
- monitor_puts(&mon->common, qstring_get_str(json));
-
- qobject_unref(json);
-}
-
static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
/* Limit guest-triggerable events to 1 per second */
[QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
@@ -603,8 +544,8 @@ static void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
static void monitor_iothread_init(void);
-static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
- bool use_io_thread)
+void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
+ bool use_io_thread)
{
if (use_io_thread && !mon_iothread) {
monitor_iothread_init();
@@ -625,14 +566,6 @@ static void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
mon->cmd_table = mon_cmds;
}
-static void monitor_data_destroy_qmp(MonitorQMP *mon)
-{
- json_message_parser_destroy(&mon->parser);
- qemu_mutex_destroy(&mon->qmp_queue_lock);
- monitor_qmp_cleanup_req_queue_locked(mon);
- g_queue_free(mon->qmp_requests);
-}
-
static void monitor_data_destroy(Monitor *mon)
{
g_free(mon->mon_cpu_path);
@@ -1069,18 +1002,6 @@ static void monitor_init_qmp_commands(void)
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}
-static bool qmp_oob_enabled(MonitorQMP *mon)
-{
- return mon->capab[QMP_CAPABILITY_OOB];
-}
-
-static void monitor_qmp_caps_reset(MonitorQMP *mon)
-{
- memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
- memset(mon->capab, 0, sizeof(mon->capab));
- mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
-}
-
/*
* Accept QMP capabilities in @list for @mon.
* On success, set mon->qmp.capab[], and return true.
@@ -2251,7 +2172,7 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset)
}
}
-static void monitor_fdsets_cleanup(void)
+void monitor_fdsets_cleanup(void)
{
MonFdset *mon_fdset;
MonFdset *mon_fdset_next;
@@ -4029,209 +3950,13 @@ cleanup:
free_cmdline_args(args, nb_args);
}
-static int monitor_can_read(void *opaque)
+int monitor_can_read(void *opaque)
{
Monitor *mon = opaque;
return !atomic_mb_read(&mon->suspend_cnt);
}
-/*
- * Emit QMP response @rsp with ID @id to @mon.
- * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
- * Nothing is emitted then.
- */
-static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
-{
- if (rsp) {
- qmp_send_response(mon, rsp);
- }
-}
-
-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;
-
- if (mon->commands == &qmp_cap_negotiation_commands) {
- error = qdict_get_qdict(rsp, "error");
- if (error
- && !g_strcmp0(qdict_get_try_str(error, "class"),
- QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
- /* Provide a more useful error message */
- qdict_del(error, "desc");
- qdict_put_str(error, "desc", "Expecting capabilities negotiation"
- " with 'qmp_capabilities'");
- }
- }
-
- monitor_qmp_respond(mon, rsp);
- qobject_unref(rsp);
-}
-
-/*
- * Pop a QMP request from a monitor request queue.
- * Return the request, or NULL all request queues are empty.
- * We are using round-robin fashion to pop the request, to avoid
- * 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 qmp_mon->qmp_queue_lock held, and the caller is responsible
- * to release it.
- */
-static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
-{
- QMPRequest *req_obj = NULL;
- Monitor *mon;
- MonitorQMP *qmp_mon;
-
- qemu_mutex_lock(&monitor_lock);
-
- QTAILQ_FOREACH(mon, &mon_list, entry) {
- if (!monitor_is_qmp(mon)) {
- continue;
- }
-
- qmp_mon = container_of(mon, MonitorQMP, common);
- qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
- req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
- if (req_obj) {
- /* With the lock of corresponding queue held */
- break;
- }
- qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
- }
-
- if (req_obj) {
- /*
- * We found one request on the monitor. Degrade this monitor's
- * priority to lowest by re-inserting it to end of queue.
- */
- QTAILQ_REMOVE(&mon_list, mon, entry);
- QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
- }
-
- qemu_mutex_unlock(&monitor_lock);
-
- return req_obj;
-}
-
-static void monitor_qmp_bh_dispatcher(void *data)
-{
- QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
- QDict *rsp;
- bool need_resume;
- MonitorQMP *mon;
-
- if (!req_obj) {
- return;
- }
-
- 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);
- }
-
- if (need_resume) {
- /* Pairs with the monitor_suspend() in handle_qmp_command() */
- monitor_resume(&mon->common);
- }
- qmp_request_free(req_obj);
-
- /* Reschedule instead of looping so the main loop stays responsive */
- qemu_bh_schedule(qmp_dispatcher_bh);
-}
-
-static void handle_qmp_command(void *opaque, QObject *req, Error *err)
-{
- MonitorQMP *mon = opaque;
- QObject *id = NULL;
- QDict *qdict;
- QMPRequest *req_obj;
-
- assert(!req != !err);
-
- qdict = qobject_to(QDict, req);
- if (qdict) {
- id = qdict_get(qdict, "id");
- } /* else will fail qmp_dispatch() */
-
- if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
- QString *req_json = qobject_to_json(req);
- trace_handle_qmp_command(mon, qstring_get_str(req_json));
- qobject_unref(req_json);
- }
-
- if (qdict && qmp_is_oob(qdict)) {
- /* OOB commands are executed immediately */
- trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
- monitor_qmp_dispatch(mon, req);
- qobject_unref(req);
- return;
- }
-
- req_obj = g_new0(QMPRequest, 1);
- req_obj->mon = mon;
- req_obj->req = req;
- req_obj->err = err;
-
- /* Protect qmp_requests and fetching its length. */
- qemu_mutex_lock(&mon->qmp_queue_lock);
-
- /*
- * 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) ||
- mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
- monitor_suspend(&mon->common);
- }
-
- /*
- * Put the request to the end of queue so that requests will be
- * handled in time order. Ownership for req_obj, req,
- * etc. will be delivered to the handler side.
- */
- assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
- g_queue_push_tail(mon->qmp_requests, req_obj);
- qemu_mutex_unlock(&mon->qmp_queue_lock);
-
- /* Kick the dispatcher routine */
- qemu_bh_schedule(qmp_dispatcher_bh);
-}
-
-static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
-{
- MonitorQMP *mon = opaque;
-
- json_message_parser_feed(&mon->parser, (const char *) buf, size);
-}
-
static void monitor_read(void *opaque, const uint8_t *buf, int size)
{
MonitorHMP *mon;
@@ -4318,56 +4043,6 @@ void monitor_resume(Monitor *mon)
trace_monitor_suspend(mon, -1);
}
-static QDict *qmp_greeting(MonitorQMP *mon)
-{
- QList *cap_list = qlist_new();
- QObject *ver = NULL;
- QMPCapability cap;
-
- qmp_marshal_query_version(NULL, &ver, NULL);
-
- for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
- if (mon->capab_offered[cap]) {
- qlist_append_str(cap_list, QMPCapability_str(cap));
- }
- }
-
- return qdict_from_jsonf_nofail(
- "{'QMP': {'version': %p, 'capabilities': %p}}",
- ver, cap_list);
-}
-
-static void monitor_qmp_event(void *opaque, int event)
-{
- QDict *data;
- MonitorQMP *mon = opaque;
-
- switch (event) {
- case CHR_EVENT_OPENED:
- mon->commands = &qmp_cap_negotiation_commands;
- monitor_qmp_caps_reset(mon);
- data = qmp_greeting(mon);
- qmp_send_response(mon, data);
- qobject_unref(data);
- mon_refcount++;
- break;
- case CHR_EVENT_CLOSED:
- /*
- * Note: this is only useful when the output of the chardev
- * backend is still open. For example, when the backend is
- * stdio, it's possible that stdout is still open when stdin
- * is closed.
- */
- monitor_qmp_cleanup_queues(mon);
- json_message_parser_destroy(&mon->parser);
- json_message_parser_init(&mon->parser, handle_qmp_command,
- mon, NULL);
- mon_refcount--;
- monitor_fdsets_cleanup();
- break;
- }
-}
-
static void monitor_event(void *opaque, int event)
{
Monitor *mon = opaque;
@@ -4503,7 +4178,7 @@ int error_vprintf_unless_qmp(const char *fmt, va_list ap)
return -1;
}
-static void monitor_list_append(Monitor *mon)
+void monitor_list_append(Monitor *mon)
{
qemu_mutex_lock(&monitor_lock);
/*
@@ -4523,57 +4198,6 @@ static void monitor_list_append(Monitor *mon)
}
}
-static void monitor_qmp_setup_handlers_bh(void *opaque)
-{
- MonitorQMP *mon = opaque;
- GMainContext *context;
-
- assert(mon->common.use_io_thread);
- context = iothread_get_g_main_context(mon_iothread);
- assert(context);
- qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
- monitor_qmp_read, monitor_qmp_event,
- NULL, &mon->common, context, true);
- monitor_list_append(&mon->common);
-}
-
-static void monitor_init_qmp(Chardev *chr, int flags)
-{
- MonitorQMP *mon = g_malloc0(sizeof(*mon));
-
- /* Note: we run QMP monitor in I/O thread when @chr supports that */
- monitor_data_init(&mon->common, flags, false,
- qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
-
- qemu_mutex_init(&mon->qmp_queue_lock);
- mon->qmp_requests = g_queue_new();
-
- qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
- qemu_chr_fe_set_echo(&mon->common.chr, true);
-
- json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
- if (mon->common.use_io_thread) {
- /*
- * Make sure the old iowatch is gone. It's possible when
- * e.g. the chardev is in client mode, with wait=on.
- */
- remove_fd_in_watch(chr);
- /*
- * We can't call qemu_chr_fe_set_handlers() directly here
- * since chardev might be running in the monitor I/O
- * thread. Schedule a bottom half.
- */
- aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
- monitor_qmp_setup_handlers_bh, mon);
- /* The bottom half will add @mon to @mon_list */
- } else {
- qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
- monitor_qmp_read, monitor_qmp_event,
- NULL, &mon->common, NULL, true);
- monitor_list_append(&mon->common);
- }
-}
-
static void monitor_init_hmp(Chardev *chr, int flags)
{
MonitorHMP *mon = g_malloc0(sizeof(*mon));
diff --git a/monitor/qmp.c b/monitor/qmp.c
new file mode 100644
index 0000000000..74e72cf5b8
--- /dev/null
+++ b/monitor/qmp.c
@@ -0,0 +1,404 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "monitor_int.h"
+
+#include "chardev/char-io.h"
+
+#include "qapi/error.h"
+#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qlist.h"
+#include "qapi/qapi-commands-misc.h"
+
+#include "trace-root.h"
+
+struct QMPRequest {
+ /* Owner of the request */
+ MonitorQMP *mon;
+ /*
+ * Request object to be handled or Error to be reported
+ * (exactly one of them is non-null)
+ */
+ QObject *req;
+ Error *err;
+};
+typedef struct QMPRequest QMPRequest;
+
+QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
+
+static bool qmp_oob_enabled(MonitorQMP *mon)
+{
+ return mon->capab[QMP_CAPABILITY_OOB];
+}
+
+static void monitor_qmp_caps_reset(MonitorQMP *mon)
+{
+ memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
+ memset(mon->capab, 0, sizeof(mon->capab));
+ mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
+}
+
+static void qmp_request_free(QMPRequest *req)
+{
+ qobject_unref(req->req);
+ error_free(req->err);
+ g_free(req);
+}
+
+/* Caller must hold mon->qmp.qmp_queue_lock */
+static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
+{
+ while (!g_queue_is_empty(mon->qmp_requests)) {
+ qmp_request_free(g_queue_pop_head(mon->qmp_requests));
+ }
+}
+
+static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
+{
+ qemu_mutex_lock(&mon->qmp_queue_lock);
+ monitor_qmp_cleanup_req_queue_locked(mon);
+ qemu_mutex_unlock(&mon->qmp_queue_lock);
+}
+
+void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
+{
+ const QObject *data = QOBJECT(rsp);
+ QString *json;
+
+ json = mon->common.flags & MONITOR_USE_PRETTY ?
+ qobject_to_json_pretty(data) : qobject_to_json(data);
+ assert(json != NULL);
+
+ qstring_append_chr(json, '\n');
+ monitor_puts(&mon->common, qstring_get_str(json));
+
+ qobject_unref(json);
+}
+
+/*
+ * Emit QMP response @rsp with ID @id to @mon.
+ * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
+ * Nothing is emitted then.
+ */
+static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
+{
+ if (rsp) {
+ qmp_send_response(mon, rsp);
+ }
+}
+
+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;
+
+ if (mon->commands == &qmp_cap_negotiation_commands) {
+ error = qdict_get_qdict(rsp, "error");
+ if (error
+ && !g_strcmp0(qdict_get_try_str(error, "class"),
+ QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
+ /* Provide a more useful error message */
+ qdict_del(error, "desc");
+ qdict_put_str(error, "desc", "Expecting capabilities negotiation"
+ " with 'qmp_capabilities'");
+ }
+ }
+
+ monitor_qmp_respond(mon, rsp);
+ qobject_unref(rsp);
+}
+
+/*
+ * Pop a QMP request from a monitor request queue.
+ * Return the request, or NULL all request queues are empty.
+ * We are using round-robin fashion to pop the request, to avoid
+ * 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 qmp_mon->qmp_queue_lock held, and the caller is responsible
+ * to release it.
+ */
+static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
+{
+ QMPRequest *req_obj = NULL;
+ Monitor *mon;
+ MonitorQMP *qmp_mon;
+
+ qemu_mutex_lock(&monitor_lock);
+
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
+ if (!monitor_is_qmp(mon)) {
+ continue;
+ }
+
+ qmp_mon = container_of(mon, MonitorQMP, common);
+ qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
+ req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
+ if (req_obj) {
+ /* With the lock of corresponding queue held */
+ break;
+ }
+ qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
+ }
+
+ if (req_obj) {
+ /*
+ * We found one request on the monitor. Degrade this monitor's
+ * priority to lowest by re-inserting it to end of queue.
+ */
+ QTAILQ_REMOVE(&mon_list, mon, entry);
+ QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
+ }
+
+ qemu_mutex_unlock(&monitor_lock);
+
+ return req_obj;
+}
+
+void monitor_qmp_bh_dispatcher(void *data)
+{
+ QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
+ QDict *rsp;
+ bool need_resume;
+ MonitorQMP *mon;
+
+ if (!req_obj) {
+ return;
+ }
+
+ 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);
+ }
+
+ if (need_resume) {
+ /* Pairs with the monitor_suspend() in handle_qmp_command() */
+ monitor_resume(&mon->common);
+ }
+ qmp_request_free(req_obj);
+
+ /* Reschedule instead of looping so the main loop stays responsive */
+ qemu_bh_schedule(qmp_dispatcher_bh);
+}
+
+static void handle_qmp_command(void *opaque, QObject *req, Error *err)
+{
+ MonitorQMP *mon = opaque;
+ QObject *id = NULL;
+ QDict *qdict;
+ QMPRequest *req_obj;
+
+ assert(!req != !err);
+
+ qdict = qobject_to(QDict, req);
+ if (qdict) {
+ id = qdict_get(qdict, "id");
+ } /* else will fail qmp_dispatch() */
+
+ if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
+ QString *req_json = qobject_to_json(req);
+ trace_handle_qmp_command(mon, qstring_get_str(req_json));
+ qobject_unref(req_json);
+ }
+
+ if (qdict && qmp_is_oob(qdict)) {
+ /* OOB commands are executed immediately */
+ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
+ monitor_qmp_dispatch(mon, req);
+ qobject_unref(req);
+ return;
+ }
+
+ req_obj = g_new0(QMPRequest, 1);
+ req_obj->mon = mon;
+ req_obj->req = req;
+ req_obj->err = err;
+
+ /* Protect qmp_requests and fetching its length. */
+ qemu_mutex_lock(&mon->qmp_queue_lock);
+
+ /*
+ * 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) ||
+ mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
+ monitor_suspend(&mon->common);
+ }
+
+ /*
+ * Put the request to the end of queue so that requests will be
+ * handled in time order. Ownership for req_obj, req,
+ * etc. will be delivered to the handler side.
+ */
+ assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
+ g_queue_push_tail(mon->qmp_requests, req_obj);
+ qemu_mutex_unlock(&mon->qmp_queue_lock);
+
+ /* Kick the dispatcher routine */
+ qemu_bh_schedule(qmp_dispatcher_bh);
+}
+
+static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
+{
+ MonitorQMP *mon = opaque;
+
+ json_message_parser_feed(&mon->parser, (const char *) buf, size);
+}
+
+static QDict *qmp_greeting(MonitorQMP *mon)
+{
+ QList *cap_list = qlist_new();
+ QObject *ver = NULL;
+ QMPCapability cap;
+
+ qmp_marshal_query_version(NULL, &ver, NULL);
+
+ for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
+ if (mon->capab_offered[cap]) {
+ qlist_append_str(cap_list, QMPCapability_str(cap));
+ }
+ }
+
+ return qdict_from_jsonf_nofail(
+ "{'QMP': {'version': %p, 'capabilities': %p}}",
+ ver, cap_list);
+}
+
+static void monitor_qmp_event(void *opaque, int event)
+{
+ QDict *data;
+ MonitorQMP *mon = opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ mon->commands = &qmp_cap_negotiation_commands;
+ monitor_qmp_caps_reset(mon);
+ data = qmp_greeting(mon);
+ qmp_send_response(mon, data);
+ qobject_unref(data);
+ mon_refcount++;
+ break;
+ case CHR_EVENT_CLOSED:
+ /*
+ * Note: this is only useful when the output of the chardev
+ * backend is still open. For example, when the backend is
+ * stdio, it's possible that stdout is still open when stdin
+ * is closed.
+ */
+ monitor_qmp_cleanup_queues(mon);
+ json_message_parser_destroy(&mon->parser);
+ json_message_parser_init(&mon->parser, handle_qmp_command,
+ mon, NULL);
+ mon_refcount--;
+ monitor_fdsets_cleanup();
+ break;
+ }
+}
+
+void monitor_data_destroy_qmp(MonitorQMP *mon)
+{
+ json_message_parser_destroy(&mon->parser);
+ qemu_mutex_destroy(&mon->qmp_queue_lock);
+ monitor_qmp_cleanup_req_queue_locked(mon);
+ g_queue_free(mon->qmp_requests);
+}
+
+static void monitor_qmp_setup_handlers_bh(void *opaque)
+{
+ MonitorQMP *mon = opaque;
+ GMainContext *context;
+
+ assert(mon->common.use_io_thread);
+ context = iothread_get_g_main_context(mon_iothread);
+ assert(context);
+ qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
+ monitor_qmp_read, monitor_qmp_event,
+ NULL, &mon->common, context, true);
+ monitor_list_append(&mon->common);
+}
+
+void monitor_init_qmp(Chardev *chr, int flags)
+{
+ MonitorQMP *mon = g_malloc0(sizeof(*mon));
+
+ /* Note: we run QMP monitor in I/O thread when @chr supports that */
+ monitor_data_init(&mon->common, flags, false,
+ qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
+
+ qemu_mutex_init(&mon->qmp_queue_lock);
+ mon->qmp_requests = g_queue_new();
+
+ qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
+ qemu_chr_fe_set_echo(&mon->common.chr, true);
+
+ json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
+ if (mon->common.use_io_thread) {
+ /*
+ * Make sure the old iowatch is gone. It's possible when
+ * e.g. the chardev is in client mode, with wait=on.
+ */
+ remove_fd_in_watch(chr);
+ /*
+ * We can't call qemu_chr_fe_set_handlers() directly here
+ * since chardev might be running in the monitor I/O
+ * thread. Schedule a bottom half.
+ */
+ aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
+ monitor_qmp_setup_handlers_bh, mon);
+ /* The bottom half will add @mon to @mon_list */
+ } else {
+ qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
+ monitor_qmp_read, monitor_qmp_event,
+ NULL, &mon->common, NULL, true);
+ monitor_list_append(&mon->common);
+ }
+}
diff --git a/Makefile.objs b/Makefile.objs
index c8337fa34b..76c5b525f9 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -46,6 +46,7 @@ ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o
common-obj-y += job-qmp.o
+common-obj-y += monitor/
common-obj-y += net/
common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o
diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
index e783b0616b..d04d58b583 100644
--- a/monitor/Makefile.objs
+++ b/monitor/Makefile.objs
@@ -1 +1,2 @@
obj-y += misc.o
+common-obj-y += qmp.o
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 08/10] monitor: Split out monitor/qmp.c
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 08/10] monitor: Split out monitor/qmp.c Kevin Wolf
@ 2019-06-07 16:59 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 16:59 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Move QMP infrastructure from monitor/misc.c to monitor/qmp.c. This is
> code that can be shared for all targets, so compile it only once.
>
> The amount of function and particularly extern variables in
> monitor_int.h is probably a bit larger than it needs to be, but this way
> no non-trivial code modifications are needed. The interfaces between QMP
> and the monitor core can be cleaned up later.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> include/monitor/monitor.h | 1 +
> monitor/monitor_int.h | 30 ++-
> monitor/misc.c | 394 +------------------------------------
> monitor/qmp.c | 404 ++++++++++++++++++++++++++++++++++++++
> Makefile.objs | 1 +
> monitor/Makefile.objs | 1 +
> 6 files changed, 445 insertions(+), 386 deletions(-)
> create mode 100644 monitor/qmp.c
>
> diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
> index 1ba354f811..7bbab05320 100644
> --- a/include/monitor/monitor.h
> +++ b/include/monitor/monitor.h
> @@ -21,6 +21,7 @@ bool monitor_cur_is_qmp(void);
>
> void monitor_init_globals(void);
> void monitor_init(Chardev *chr, int flags);
> +void monitor_init_qmp(Chardev *chr, int flags);
> void monitor_cleanup(void);
>
> int monitor_suspend(Monitor *mon);
> diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
> index ab87013b6f..487618392f 100644
> --- a/monitor/monitor_int.h
> +++ b/monitor/monitor_int.h
> @@ -30,10 +30,11 @@
>
> #include "qapi/qmp/qdict.h"
> #include "qapi/qmp/json-parser.h"
> -#include "qapi/qapi-commands.h"
> +#include "qapi/qmp/dispatch.h"
>
> #include "qemu/readline.h"
> #include "chardev/char-fe.h"
> +#include "sysemu/iothread.h"
>
> /*
> * Supported types:
> @@ -144,4 +145,31 @@ typedef struct {
> GQueue *qmp_requests;
> } MonitorQMP;
>
> +/**
> + * Is @mon a QMP monitor?
> + */
> +static inline bool monitor_is_qmp(const Monitor *mon)
> +{
> + return (mon->flags & MONITOR_USE_CONTROL);
> +}
> +
> +typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
> +extern IOThread *mon_iothread;
> +extern QEMUBH *qmp_dispatcher_bh;
> +extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
> +extern QemuMutex monitor_lock;
> +extern MonitorList mon_list;
> +extern int mon_refcount;
> +
> +int monitor_puts(Monitor *mon, const char *str);
> +void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> + bool use_io_thread);
> +int monitor_can_read(void *opaque);
> +void monitor_list_append(Monitor *mon);
> +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);
> +
> #endif
> diff --git a/monitor/misc.c b/monitor/misc.c
> index 6ae7561105..6c67f0978c 100644
> --- a/monitor/misc.c
> +++ b/monitor/misc.c
> @@ -140,51 +140,29 @@ IOThread *mon_iothread;
> /* Bottom half to dispatch the requests received from I/O thread */
> QEMUBH *qmp_dispatcher_bh;
>
> -struct QMPRequest {
> - /* Owner of the request */
> - MonitorQMP *mon;
> - /*
> - * Request object to be handled or Error to be reported
> - * (exactly one of them is non-null)
> - */
> - QObject *req;
> - Error *err;
> -};
> -typedef struct QMPRequest QMPRequest;
> -
> /* QMP checker flags */
> #define QMP_ACCEPT_UNKNOWNS 1
>
> /* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
> -static QemuMutex monitor_lock;
> +QemuMutex monitor_lock;
> static GHashTable *monitor_qapi_event_state;
> -static QTAILQ_HEAD(, Monitor) mon_list;
> +MonitorList mon_list;
> static bool monitor_destroyed;
>
> /* Protects mon_fdsets */
> static QemuMutex mon_fdsets_lock;
> static QLIST_HEAD(, MonFdset) mon_fdsets;
>
> -static int mon_refcount;
> +int mon_refcount;
>
> static mon_cmd_t mon_cmds[];
> static mon_cmd_t info_cmds[];
>
> -QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
> -
> __thread Monitor *cur_mon;
>
> static void monitor_command_cb(void *opaque, const char *cmdline,
> void *readline_opaque);
>
> -/**
> - * Is @mon a QMP monitor?
> - */
> -static inline bool monitor_is_qmp(const Monitor *mon)
> -{
> - return (mon->flags & MONITOR_USE_CONTROL);
> -}
> -
> /**
> * Is @mon is using readline?
> * Note: not all HMP monitors use readline, e.g., gdbserver has a
> @@ -243,28 +221,6 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
> }
> }
>
> -static void qmp_request_free(QMPRequest *req)
> -{
> - qobject_unref(req->req);
> - error_free(req->err);
> - g_free(req);
> -}
> -
> -/* Caller must hold mon->qmp.qmp_queue_lock */
> -static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
> -{
> - while (!g_queue_is_empty(mon->qmp_requests)) {
> - qmp_request_free(g_queue_pop_head(mon->qmp_requests));
> - }
> -}
> -
> -static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
> -{
> - qemu_mutex_lock(&mon->qmp_queue_lock);
> - monitor_qmp_cleanup_req_queue_locked(mon);
> - qemu_mutex_unlock(&mon->qmp_queue_lock);
> -}
> -
>
> static void monitor_flush_locked(Monitor *mon);
>
> @@ -324,7 +280,7 @@ void monitor_flush(Monitor *mon)
> }
>
> /* flush at every end of line */
> -static int monitor_puts(Monitor *mon, const char *str)
> +int monitor_puts(Monitor *mon, const char *str)
> {
> int i;
> char c;
> @@ -374,21 +330,6 @@ int monitor_printf(Monitor *mon, const char *fmt, ...)
> return ret;
> }
>
> -static void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
> -{
> - const QObject *data = QOBJECT(rsp);
> - QString *json;
> -
> - json = mon->common.flags & MONITOR_USE_PRETTY ?
> - qobject_to_json_pretty(data) : qobject_to_json(data);
> - assert(json != NULL);
> -
> - qstring_append_chr(json, '\n');
> - monitor_puts(&mon->common, qstring_get_str(json));
> -
> - qobject_unref(json);
> -}
> -
> static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
> /* Limit guest-triggerable events to 1 per second */
> [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
> @@ -603,8 +544,8 @@ static void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
>
> static void monitor_iothread_init(void);
>
> -static void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> - bool use_io_thread)
> +void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> + bool use_io_thread)
> {
> if (use_io_thread && !mon_iothread) {
> monitor_iothread_init();
> @@ -625,14 +566,6 @@ static void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
> mon->cmd_table = mon_cmds;
> }
>
> -static void monitor_data_destroy_qmp(MonitorQMP *mon)
> -{
> - json_message_parser_destroy(&mon->parser);
> - qemu_mutex_destroy(&mon->qmp_queue_lock);
> - monitor_qmp_cleanup_req_queue_locked(mon);
> - g_queue_free(mon->qmp_requests);
> -}
> -
> static void monitor_data_destroy(Monitor *mon)
> {
> g_free(mon->mon_cpu_path);
> @@ -1069,18 +1002,6 @@ static void monitor_init_qmp_commands(void)
> qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
> }
>
> -static bool qmp_oob_enabled(MonitorQMP *mon)
> -{
> - return mon->capab[QMP_CAPABILITY_OOB];
> -}
> -
> -static void monitor_qmp_caps_reset(MonitorQMP *mon)
> -{
> - memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
> - memset(mon->capab, 0, sizeof(mon->capab));
> - mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
> -}
> -
> /*
> * Accept QMP capabilities in @list for @mon.
> * On success, set mon->qmp.capab[], and return true.
> @@ -2251,7 +2172,7 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset)
> }
> }
>
> -static void monitor_fdsets_cleanup(void)
> +void monitor_fdsets_cleanup(void)
> {
> MonFdset *mon_fdset;
> MonFdset *mon_fdset_next;
> @@ -4029,209 +3950,13 @@ cleanup:
> free_cmdline_args(args, nb_args);
> }
>
> -static int monitor_can_read(void *opaque)
> +int monitor_can_read(void *opaque)
> {
> Monitor *mon = opaque;
>
> return !atomic_mb_read(&mon->suspend_cnt);
> }
>
> -/*
> - * Emit QMP response @rsp with ID @id to @mon.
> - * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
> - * Nothing is emitted then.
> - */
> -static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
> -{
> - if (rsp) {
> - qmp_send_response(mon, rsp);
> - }
> -}
> -
> -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;
> -
> - if (mon->commands == &qmp_cap_negotiation_commands) {
> - error = qdict_get_qdict(rsp, "error");
> - if (error
> - && !g_strcmp0(qdict_get_try_str(error, "class"),
> - QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
> - /* Provide a more useful error message */
> - qdict_del(error, "desc");
> - qdict_put_str(error, "desc", "Expecting capabilities negotiation"
> - " with 'qmp_capabilities'");
> - }
> - }
> -
> - monitor_qmp_respond(mon, rsp);
> - qobject_unref(rsp);
> -}
> -
> -/*
> - * Pop a QMP request from a monitor request queue.
> - * Return the request, or NULL all request queues are empty.
> - * We are using round-robin fashion to pop the request, to avoid
> - * 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 qmp_mon->qmp_queue_lock held, and the caller is responsible
> - * to release it.
> - */
> -static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
> -{
> - QMPRequest *req_obj = NULL;
> - Monitor *mon;
> - MonitorQMP *qmp_mon;
> -
> - qemu_mutex_lock(&monitor_lock);
> -
> - QTAILQ_FOREACH(mon, &mon_list, entry) {
> - if (!monitor_is_qmp(mon)) {
> - continue;
> - }
> -
> - qmp_mon = container_of(mon, MonitorQMP, common);
> - qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
> - req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
> - if (req_obj) {
> - /* With the lock of corresponding queue held */
> - break;
> - }
> - qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
> - }
> -
> - if (req_obj) {
> - /*
> - * We found one request on the monitor. Degrade this monitor's
> - * priority to lowest by re-inserting it to end of queue.
> - */
> - QTAILQ_REMOVE(&mon_list, mon, entry);
> - QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
> - }
> -
> - qemu_mutex_unlock(&monitor_lock);
> -
> - return req_obj;
> -}
> -
> -static void monitor_qmp_bh_dispatcher(void *data)
> -{
> - QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
> - QDict *rsp;
> - bool need_resume;
> - MonitorQMP *mon;
> -
> - if (!req_obj) {
> - return;
> - }
> -
> - 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);
> - }
> -
> - if (need_resume) {
> - /* Pairs with the monitor_suspend() in handle_qmp_command() */
> - monitor_resume(&mon->common);
> - }
> - qmp_request_free(req_obj);
> -
> - /* Reschedule instead of looping so the main loop stays responsive */
> - qemu_bh_schedule(qmp_dispatcher_bh);
> -}
> -
> -static void handle_qmp_command(void *opaque, QObject *req, Error *err)
> -{
> - MonitorQMP *mon = opaque;
> - QObject *id = NULL;
> - QDict *qdict;
> - QMPRequest *req_obj;
> -
> - assert(!req != !err);
> -
> - qdict = qobject_to(QDict, req);
> - if (qdict) {
> - id = qdict_get(qdict, "id");
> - } /* else will fail qmp_dispatch() */
> -
> - if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
> - QString *req_json = qobject_to_json(req);
> - trace_handle_qmp_command(mon, qstring_get_str(req_json));
> - qobject_unref(req_json);
> - }
> -
> - if (qdict && qmp_is_oob(qdict)) {
> - /* OOB commands are executed immediately */
> - trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
> - monitor_qmp_dispatch(mon, req);
> - qobject_unref(req);
> - return;
> - }
> -
> - req_obj = g_new0(QMPRequest, 1);
> - req_obj->mon = mon;
> - req_obj->req = req;
> - req_obj->err = err;
> -
> - /* Protect qmp_requests and fetching its length. */
> - qemu_mutex_lock(&mon->qmp_queue_lock);
> -
> - /*
> - * 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) ||
> - mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
> - monitor_suspend(&mon->common);
> - }
> -
> - /*
> - * Put the request to the end of queue so that requests will be
> - * handled in time order. Ownership for req_obj, req,
> - * etc. will be delivered to the handler side.
> - */
> - assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
> - g_queue_push_tail(mon->qmp_requests, req_obj);
> - qemu_mutex_unlock(&mon->qmp_queue_lock);
> -
> - /* Kick the dispatcher routine */
> - qemu_bh_schedule(qmp_dispatcher_bh);
> -}
> -
> -static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
> -{
> - MonitorQMP *mon = opaque;
> -
> - json_message_parser_feed(&mon->parser, (const char *) buf, size);
> -}
> -
> static void monitor_read(void *opaque, const uint8_t *buf, int size)
> {
> MonitorHMP *mon;
> @@ -4318,56 +4043,6 @@ void monitor_resume(Monitor *mon)
> trace_monitor_suspend(mon, -1);
> }
>
> -static QDict *qmp_greeting(MonitorQMP *mon)
> -{
> - QList *cap_list = qlist_new();
> - QObject *ver = NULL;
> - QMPCapability cap;
> -
> - qmp_marshal_query_version(NULL, &ver, NULL);
> -
> - for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
> - if (mon->capab_offered[cap]) {
> - qlist_append_str(cap_list, QMPCapability_str(cap));
> - }
> - }
> -
> - return qdict_from_jsonf_nofail(
> - "{'QMP': {'version': %p, 'capabilities': %p}}",
> - ver, cap_list);
> -}
> -
> -static void monitor_qmp_event(void *opaque, int event)
> -{
> - QDict *data;
> - MonitorQMP *mon = opaque;
> -
> - switch (event) {
> - case CHR_EVENT_OPENED:
> - mon->commands = &qmp_cap_negotiation_commands;
> - monitor_qmp_caps_reset(mon);
> - data = qmp_greeting(mon);
> - qmp_send_response(mon, data);
> - qobject_unref(data);
> - mon_refcount++;
> - break;
> - case CHR_EVENT_CLOSED:
> - /*
> - * Note: this is only useful when the output of the chardev
> - * backend is still open. For example, when the backend is
> - * stdio, it's possible that stdout is still open when stdin
> - * is closed.
> - */
> - monitor_qmp_cleanup_queues(mon);
> - json_message_parser_destroy(&mon->parser);
> - json_message_parser_init(&mon->parser, handle_qmp_command,
> - mon, NULL);
> - mon_refcount--;
> - monitor_fdsets_cleanup();
> - break;
> - }
> -}
> -
> static void monitor_event(void *opaque, int event)
> {
> Monitor *mon = opaque;
> @@ -4503,7 +4178,7 @@ int error_vprintf_unless_qmp(const char *fmt, va_list ap)
> return -1;
> }
>
> -static void monitor_list_append(Monitor *mon)
> +void monitor_list_append(Monitor *mon)
> {
> qemu_mutex_lock(&monitor_lock);
> /*
> @@ -4523,57 +4198,6 @@ static void monitor_list_append(Monitor *mon)
> }
> }
>
> -static void monitor_qmp_setup_handlers_bh(void *opaque)
> -{
> - MonitorQMP *mon = opaque;
> - GMainContext *context;
> -
> - assert(mon->common.use_io_thread);
> - context = iothread_get_g_main_context(mon_iothread);
> - assert(context);
> - qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
> - monitor_qmp_read, monitor_qmp_event,
> - NULL, &mon->common, context, true);
> - monitor_list_append(&mon->common);
> -}
> -
> -static void monitor_init_qmp(Chardev *chr, int flags)
> -{
> - MonitorQMP *mon = g_malloc0(sizeof(*mon));
> -
> - /* Note: we run QMP monitor in I/O thread when @chr supports that */
> - monitor_data_init(&mon->common, flags, false,
> - qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
> -
> - qemu_mutex_init(&mon->qmp_queue_lock);
> - mon->qmp_requests = g_queue_new();
> -
> - qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
> - qemu_chr_fe_set_echo(&mon->common.chr, true);
> -
> - json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
> - if (mon->common.use_io_thread) {
> - /*
> - * Make sure the old iowatch is gone. It's possible when
> - * e.g. the chardev is in client mode, with wait=on.
> - */
> - remove_fd_in_watch(chr);
> - /*
> - * We can't call qemu_chr_fe_set_handlers() directly here
> - * since chardev might be running in the monitor I/O
> - * thread. Schedule a bottom half.
> - */
> - aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> - monitor_qmp_setup_handlers_bh, mon);
> - /* The bottom half will add @mon to @mon_list */
> - } else {
> - qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
> - monitor_qmp_read, monitor_qmp_event,
> - NULL, &mon->common, NULL, true);
> - monitor_list_append(&mon->common);
> - }
> -}
> -
> static void monitor_init_hmp(Chardev *chr, int flags)
> {
> MonitorHMP *mon = g_malloc0(sizeof(*mon));
> diff --git a/monitor/qmp.c b/monitor/qmp.c
> new file mode 100644
> index 0000000000..74e72cf5b8
> --- /dev/null
> +++ b/monitor/qmp.c
> @@ -0,0 +1,404 @@
> +/*
> + * QEMU monitor
> + *
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "monitor_int.h"
> +
> +#include "chardev/char-io.h"
> +
> +#include "qapi/error.h"
> +#include "qapi/qmp/qjson.h"
> +#include "qapi/qmp/qstring.h"
> +#include "qapi/qmp/qlist.h"
> +#include "qapi/qapi-commands-misc.h"
> +
> +#include "trace-root.h"
> +
> +struct QMPRequest {
> + /* Owner of the request */
> + MonitorQMP *mon;
> + /*
> + * Request object to be handled or Error to be reported
> + * (exactly one of them is non-null)
> + */
> + QObject *req;
> + Error *err;
> +};
> +typedef struct QMPRequest QMPRequest;
> +
> +QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
> +
> +static bool qmp_oob_enabled(MonitorQMP *mon)
> +{
> + return mon->capab[QMP_CAPABILITY_OOB];
> +}
> +
> +static void monitor_qmp_caps_reset(MonitorQMP *mon)
> +{
> + memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
> + memset(mon->capab, 0, sizeof(mon->capab));
> + mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
> +}
> +
> +static void qmp_request_free(QMPRequest *req)
> +{
> + qobject_unref(req->req);
> + error_free(req->err);
> + g_free(req);
> +}
> +
> +/* Caller must hold mon->qmp.qmp_queue_lock */
> +static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
> +{
> + while (!g_queue_is_empty(mon->qmp_requests)) {
> + qmp_request_free(g_queue_pop_head(mon->qmp_requests));
> + }
> +}
> +
> +static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
> +{
> + qemu_mutex_lock(&mon->qmp_queue_lock);
> + monitor_qmp_cleanup_req_queue_locked(mon);
> + qemu_mutex_unlock(&mon->qmp_queue_lock);
> +}
> +
> +void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
> +{
> + const QObject *data = QOBJECT(rsp);
> + QString *json;
> +
> + json = mon->common.flags & MONITOR_USE_PRETTY ?
> + qobject_to_json_pretty(data) : qobject_to_json(data);
> + assert(json != NULL);
> +
> + qstring_append_chr(json, '\n');
> + monitor_puts(&mon->common, qstring_get_str(json));
> +
> + qobject_unref(json);
> +}
> +
> +/*
> + * Emit QMP response @rsp with ID @id to @mon.
> + * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
> + * Nothing is emitted then.
> + */
> +static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
> +{
> + if (rsp) {
> + qmp_send_response(mon, rsp);
> + }
> +}
> +
> +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;
> +
> + if (mon->commands == &qmp_cap_negotiation_commands) {
> + error = qdict_get_qdict(rsp, "error");
> + if (error
> + && !g_strcmp0(qdict_get_try_str(error, "class"),
> + QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
> + /* Provide a more useful error message */
> + qdict_del(error, "desc");
> + qdict_put_str(error, "desc", "Expecting capabilities negotiation"
> + " with 'qmp_capabilities'");
> + }
> + }
> +
> + monitor_qmp_respond(mon, rsp);
> + qobject_unref(rsp);
> +}
> +
> +/*
> + * Pop a QMP request from a monitor request queue.
> + * Return the request, or NULL all request queues are empty.
> + * We are using round-robin fashion to pop the request, to avoid
> + * 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 qmp_mon->qmp_queue_lock held, and the caller is responsible
> + * to release it.
> + */
> +static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
> +{
> + QMPRequest *req_obj = NULL;
> + Monitor *mon;
> + MonitorQMP *qmp_mon;
> +
> + qemu_mutex_lock(&monitor_lock);
> +
> + QTAILQ_FOREACH(mon, &mon_list, entry) {
> + if (!monitor_is_qmp(mon)) {
> + continue;
> + }
> +
> + qmp_mon = container_of(mon, MonitorQMP, common);
> + qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
> + req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
> + if (req_obj) {
> + /* With the lock of corresponding queue held */
> + break;
> + }
> + qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
> + }
> +
> + if (req_obj) {
> + /*
> + * We found one request on the monitor. Degrade this monitor's
> + * priority to lowest by re-inserting it to end of queue.
> + */
> + QTAILQ_REMOVE(&mon_list, mon, entry);
> + QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
> + }
> +
> + qemu_mutex_unlock(&monitor_lock);
> +
> + return req_obj;
> +}
> +
> +void monitor_qmp_bh_dispatcher(void *data)
> +{
> + QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
> + QDict *rsp;
> + bool need_resume;
> + MonitorQMP *mon;
> +
> + if (!req_obj) {
> + return;
> + }
> +
> + 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);
> + }
> +
> + if (need_resume) {
> + /* Pairs with the monitor_suspend() in handle_qmp_command() */
> + monitor_resume(&mon->common);
> + }
> + qmp_request_free(req_obj);
> +
> + /* Reschedule instead of looping so the main loop stays responsive */
> + qemu_bh_schedule(qmp_dispatcher_bh);
> +}
> +
> +static void handle_qmp_command(void *opaque, QObject *req, Error *err)
> +{
> + MonitorQMP *mon = opaque;
> + QObject *id = NULL;
> + QDict *qdict;
> + QMPRequest *req_obj;
> +
> + assert(!req != !err);
> +
> + qdict = qobject_to(QDict, req);
> + if (qdict) {
> + id = qdict_get(qdict, "id");
> + } /* else will fail qmp_dispatch() */
> +
> + if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
> + QString *req_json = qobject_to_json(req);
> + trace_handle_qmp_command(mon, qstring_get_str(req_json));
> + qobject_unref(req_json);
> + }
> +
> + if (qdict && qmp_is_oob(qdict)) {
> + /* OOB commands are executed immediately */
> + trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
> + monitor_qmp_dispatch(mon, req);
> + qobject_unref(req);
> + return;
> + }
> +
> + req_obj = g_new0(QMPRequest, 1);
> + req_obj->mon = mon;
> + req_obj->req = req;
> + req_obj->err = err;
> +
> + /* Protect qmp_requests and fetching its length. */
> + qemu_mutex_lock(&mon->qmp_queue_lock);
> +
> + /*
> + * 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) ||
> + mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
> + monitor_suspend(&mon->common);
> + }
> +
> + /*
> + * Put the request to the end of queue so that requests will be
> + * handled in time order. Ownership for req_obj, req,
> + * etc. will be delivered to the handler side.
> + */
> + assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
> + g_queue_push_tail(mon->qmp_requests, req_obj);
> + qemu_mutex_unlock(&mon->qmp_queue_lock);
> +
> + /* Kick the dispatcher routine */
> + qemu_bh_schedule(qmp_dispatcher_bh);
> +}
> +
> +static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
> +{
> + MonitorQMP *mon = opaque;
> +
> + json_message_parser_feed(&mon->parser, (const char *) buf, size);
> +}
> +
> +static QDict *qmp_greeting(MonitorQMP *mon)
> +{
> + QList *cap_list = qlist_new();
> + QObject *ver = NULL;
> + QMPCapability cap;
> +
> + qmp_marshal_query_version(NULL, &ver, NULL);
> +
> + for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
> + if (mon->capab_offered[cap]) {
> + qlist_append_str(cap_list, QMPCapability_str(cap));
> + }
> + }
> +
> + return qdict_from_jsonf_nofail(
> + "{'QMP': {'version': %p, 'capabilities': %p}}",
> + ver, cap_list);
> +}
> +
> +static void monitor_qmp_event(void *opaque, int event)
> +{
> + QDict *data;
> + MonitorQMP *mon = opaque;
> +
> + switch (event) {
> + case CHR_EVENT_OPENED:
> + mon->commands = &qmp_cap_negotiation_commands;
> + monitor_qmp_caps_reset(mon);
> + data = qmp_greeting(mon);
> + qmp_send_response(mon, data);
> + qobject_unref(data);
> + mon_refcount++;
> + break;
> + case CHR_EVENT_CLOSED:
> + /*
> + * Note: this is only useful when the output of the chardev
> + * backend is still open. For example, when the backend is
> + * stdio, it's possible that stdout is still open when stdin
> + * is closed.
> + */
> + monitor_qmp_cleanup_queues(mon);
> + json_message_parser_destroy(&mon->parser);
> + json_message_parser_init(&mon->parser, handle_qmp_command,
> + mon, NULL);
> + mon_refcount--;
> + monitor_fdsets_cleanup();
> + break;
> + }
> +}
> +
> +void monitor_data_destroy_qmp(MonitorQMP *mon)
> +{
> + json_message_parser_destroy(&mon->parser);
> + qemu_mutex_destroy(&mon->qmp_queue_lock);
> + monitor_qmp_cleanup_req_queue_locked(mon);
> + g_queue_free(mon->qmp_requests);
> +}
> +
> +static void monitor_qmp_setup_handlers_bh(void *opaque)
> +{
> + MonitorQMP *mon = opaque;
> + GMainContext *context;
> +
> + assert(mon->common.use_io_thread);
> + context = iothread_get_g_main_context(mon_iothread);
> + assert(context);
> + qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
> + monitor_qmp_read, monitor_qmp_event,
> + NULL, &mon->common, context, true);
> + monitor_list_append(&mon->common);
> +}
> +
> +void monitor_init_qmp(Chardev *chr, int flags)
> +{
> + MonitorQMP *mon = g_malloc0(sizeof(*mon));
> +
> + /* Note: we run QMP monitor in I/O thread when @chr supports that */
> + monitor_data_init(&mon->common, flags, false,
> + qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
> +
> + qemu_mutex_init(&mon->qmp_queue_lock);
> + mon->qmp_requests = g_queue_new();
> +
> + qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
> + qemu_chr_fe_set_echo(&mon->common.chr, true);
> +
> + json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
> + if (mon->common.use_io_thread) {
> + /*
> + * Make sure the old iowatch is gone. It's possible when
> + * e.g. the chardev is in client mode, with wait=on.
> + */
> + remove_fd_in_watch(chr);
> + /*
> + * We can't call qemu_chr_fe_set_handlers() directly here
> + * since chardev might be running in the monitor I/O
> + * thread. Schedule a bottom half.
> + */
> + aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> + monitor_qmp_setup_handlers_bh, mon);
> + /* The bottom half will add @mon to @mon_list */
> + } else {
> + qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
> + monitor_qmp_read, monitor_qmp_event,
> + NULL, &mon->common, NULL, true);
> + monitor_list_append(&mon->common);
> + }
> +}
> diff --git a/Makefile.objs b/Makefile.objs
> index c8337fa34b..76c5b525f9 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -46,6 +46,7 @@ ifeq ($(CONFIG_SOFTMMU),y)
> common-obj-y = blockdev.o blockdev-nbd.o block/
> common-obj-y += bootdevice.o iothread.o
> common-obj-y += job-qmp.o
> +common-obj-y += monitor/
> common-obj-y += net/
> common-obj-y += qdev-monitor.o device-hotplug.o
> common-obj-$(CONFIG_WIN32) += os-win32.o
> diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
> index e783b0616b..d04d58b583 100644
> --- a/monitor/Makefile.objs
> +++ b/monitor/Makefile.objs
> @@ -1 +1,2 @@
> obj-y += misc.o
> +common-obj-y += qmp.o
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 09/10] monitor: Split out monitor/hmp.c
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (7 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 08/10] monitor: Split out monitor/qmp.c Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 17:21 ` Dr. David Alan Gilbert
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c Kevin Wolf
` (3 subsequent siblings)
12 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Move HMP infrastructure from monitor/misc.c to monitor/hmp.c. This is
code that can be shared for all targets, so compile it only once.
The amount of function and particularly extern variables in
monitor_int.h is probably a bit larger than it needs to be, but this way
no non-trivial code modifications are needed. The interfaces between HMP
and the monitor core can be cleaned up later.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/monitor/monitor.h | 1 +
monitor/monitor_int.h | 31 +
monitor/hmp.c | 1351 +++++++++++++++++++++++++++++++++++++
monitor/misc.c | 1338 +-----------------------------------
monitor/Makefile.objs | 2 +-
5 files changed, 1390 insertions(+), 1333 deletions(-)
create mode 100644 monitor/hmp.c
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 7bbab05320..8547529e49 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -22,6 +22,7 @@ bool monitor_cur_is_qmp(void);
void monitor_init_globals(void);
void monitor_init(Chardev *chr, int flags);
void monitor_init_qmp(Chardev *chr, int flags);
+void monitor_init_hmp(Chardev *chr, int flags);
void monitor_cleanup(void);
int monitor_suspend(Monitor *mon);
diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
index 487618392f..8c5d95f942 100644
--- a/monitor/monitor_int.h
+++ b/monitor/monitor_int.h
@@ -27,6 +27,7 @@
#include "qemu-common.h"
#include "monitor/monitor.h"
+#include "qemu/cutils.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/json-parser.h"
@@ -153,6 +154,29 @@ static inline bool monitor_is_qmp(const Monitor *mon)
return (mon->flags & MONITOR_USE_CONTROL);
}
+/**
+ * Is @name in the '|' separated list of names @list?
+ */
+static inline int compare_cmd(const char *name, const char *list)
+{
+ const char *p, *pstart;
+ int len;
+ len = strlen(name);
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = qemu_strchrnul(p, '|');
+ if ((p - pstart) == len && !memcmp(pstart, name, len)) {
+ return 1;
+ }
+ if (*p == '\0') {
+ break;
+ }
+ p++;
+ }
+ return 0;
+}
+
typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
extern IOThread *mon_iothread;
extern QEMUBH *qmp_dispatcher_bh;
@@ -161,6 +185,8 @@ extern QemuMutex monitor_lock;
extern MonitorList mon_list;
extern int mon_refcount;
+extern mon_cmd_t mon_cmds[];
+
int monitor_puts(Monitor *mon, const char *str);
void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
bool use_io_thread);
@@ -172,4 +198,9 @@ void qmp_send_response(MonitorQMP *mon, const QDict *rsp);
void monitor_data_destroy_qmp(MonitorQMP *mon);
void monitor_qmp_bh_dispatcher(void *data);
+void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush);
+int get_monitor_def(int64_t *pval, const char *name);
+void help_cmd(Monitor *mon, const char *name);
+void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
+
#endif
diff --git a/monitor/hmp.c b/monitor/hmp.c
new file mode 100644
index 0000000000..2bc464a7fc
--- /dev/null
+++ b/monitor/hmp.c
@@ -0,0 +1,1351 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "monitor_int.h"
+
+#include "qapi/error.h"
+#include "qapi/qmp/qnum.h"
+
+#include "qemu/config-file.h"
+#include "qemu/log.h"
+#include "qemu/option.h"
+#include "qemu/units.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/sysemu.h"
+
+#include "trace-root.h"
+
+static int get_str(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+ int c;
+
+ q = buf;
+ p = *pp;
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ fail:
+ *q = '\0';
+ *pp = p;
+ return -1;
+ }
+ if (*p == '\"') {
+ p++;
+ while (*p != '\0' && *p != '\"') {
+ if (*p == '\\') {
+ p++;
+ c = *p++;
+ switch (c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '\\':
+ case '\'':
+ case '\"':
+ break;
+ default:
+ printf("unsupported escape code: '\\%c'\n", c);
+ goto fail;
+ }
+ if ((q - buf) < buf_size - 1) {
+ *q++ = c;
+ }
+ } else {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ if (*p != '\"') {
+ printf("unterminated string\n");
+ goto fail;
+ }
+ p++;
+ } else {
+ while (*p != '\0' && !qemu_isspace(*p)) {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ *q = '\0';
+ *pp = p;
+ return 0;
+}
+
+#define MAX_ARGS 16
+
+static void free_cmdline_args(char **args, int nb_args)
+{
+ int i;
+
+ assert(nb_args <= MAX_ARGS);
+
+ for (i = 0; i < nb_args; i++) {
+ g_free(args[i]);
+ }
+
+}
+
+/*
+ * Parse the command line to get valid args.
+ * @cmdline: command line to be parsed.
+ * @pnb_args: location to store the number of args, must NOT be NULL.
+ * @args: location to store the args, which should be freed by caller, must
+ * NOT be NULL.
+ *
+ * Returns 0 on success, negative on failure.
+ *
+ * NOTE: this parser is an approximate form of the real command parser. Number
+ * of args have a limit of MAX_ARGS. If cmdline contains more, it will
+ * return with failure.
+ */
+static int parse_cmdline(const char *cmdline,
+ int *pnb_args, char **args)
+{
+ const char *p;
+ int nb_args, ret;
+ char buf[1024];
+
+ p = cmdline;
+ nb_args = 0;
+ for (;;) {
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+ if (nb_args >= MAX_ARGS) {
+ goto fail;
+ }
+ ret = get_str(buf, sizeof(buf), &p);
+ if (ret < 0) {
+ goto fail;
+ }
+ args[nb_args] = g_strdup(buf);
+ nb_args++;
+ }
+ *pnb_args = nb_args;
+ return 0;
+
+ fail:
+ free_cmdline_args(args, nb_args);
+ return -1;
+}
+
+/*
+ * Can command @cmd be executed in preconfig state?
+ */
+static bool cmd_can_preconfig(const mon_cmd_t *cmd)
+{
+ if (!cmd->flags) {
+ return false;
+ }
+
+ return strchr(cmd->flags, 'p');
+}
+
+static void help_cmd_dump_one(Monitor *mon,
+ const mon_cmd_t *cmd,
+ char **prefix_args,
+ int prefix_args_nb)
+{
+ int i;
+
+ if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
+ return;
+ }
+
+ for (i = 0; i < prefix_args_nb; i++) {
+ monitor_printf(mon, "%s ", prefix_args[i]);
+ }
+ monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
+}
+
+/* @args[@arg_index] is the valid command need to find in @cmds */
+static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
+ char **args, int nb_args, int arg_index)
+{
+ const mon_cmd_t *cmd;
+ size_t i;
+
+ /* No valid arg need to compare with, dump all in *cmds */
+ if (arg_index >= nb_args) {
+ for (cmd = cmds; cmd->name != NULL; cmd++) {
+ help_cmd_dump_one(mon, cmd, args, arg_index);
+ }
+ return;
+ }
+
+ /* Find one entry to dump */
+ for (cmd = cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(args[arg_index], cmd->name) &&
+ ((!runstate_check(RUN_STATE_PRECONFIG) ||
+ cmd_can_preconfig(cmd)))) {
+ if (cmd->sub_table) {
+ /* continue with next arg */
+ help_cmd_dump(mon, cmd->sub_table,
+ args, nb_args, arg_index + 1);
+ } else {
+ help_cmd_dump_one(mon, cmd, args, arg_index);
+ }
+ return;
+ }
+ }
+
+ /* Command not found */
+ monitor_printf(mon, "unknown command: '");
+ for (i = 0; i <= arg_index; i++) {
+ monitor_printf(mon, "%s%s", args[i], i == arg_index ? "'\n" : " ");
+ }
+}
+
+void help_cmd(Monitor *mon, const char *name)
+{
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+ char *args[MAX_ARGS];
+ int nb_args = 0;
+
+ /* 1. parse user input */
+ if (name) {
+ /* special case for log, directly dump and return */
+ if (!strcmp(name, "log")) {
+ const QEMULogItem *item;
+ monitor_printf(mon, "Log items (comma separated):\n");
+ monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
+ for (item = qemu_log_items; item->mask != 0; item++) {
+ monitor_printf(mon, "%-10s %s\n", item->name, item->help);
+ }
+ return;
+ }
+
+ if (parse_cmdline(name, &nb_args, args) < 0) {
+ return;
+ }
+ }
+
+ /* 2. dump the contents according to parsed args */
+ help_cmd_dump(mon, hmp_mon->cmd_table, args, nb_args, 0);
+
+ free_cmdline_args(args, nb_args);
+}
+
+/*******************************************************************/
+
+static const char *pch;
+static sigjmp_buf expr_env;
+
+static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
+expr_error(Monitor *mon, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ monitor_vprintf(mon, fmt, ap);
+ monitor_printf(mon, "\n");
+ va_end(ap);
+ siglongjmp(expr_env, 1);
+}
+
+static void next(void)
+{
+ if (*pch != '\0') {
+ pch++;
+ while (qemu_isspace(*pch))
+ pch++;
+ }
+}
+
+static int64_t expr_sum(Monitor *mon);
+
+static int64_t expr_unary(Monitor *mon)
+{
+ int64_t n;
+ char *p;
+ int ret;
+
+ switch(*pch) {
+ case '+':
+ next();
+ n = expr_unary(mon);
+ break;
+ case '-':
+ next();
+ n = -expr_unary(mon);
+ break;
+ case '~':
+ next();
+ n = ~expr_unary(mon);
+ break;
+ case '(':
+ next();
+ n = expr_sum(mon);
+ if (*pch != ')') {
+ expr_error(mon, "')' expected");
+ }
+ next();
+ break;
+ case '\'':
+ pch++;
+ if (*pch == '\0')
+ expr_error(mon, "character constant expected");
+ n = *pch;
+ pch++;
+ if (*pch != '\'')
+ expr_error(mon, "missing terminating \' character");
+ next();
+ break;
+ case '$':
+ {
+ char buf[128], *q;
+ int64_t reg = 0;
+
+ pch++;
+ q = buf;
+ while ((*pch >= 'a' && *pch <= 'z') ||
+ (*pch >= 'A' && *pch <= 'Z') ||
+ (*pch >= '0' && *pch <= '9') ||
+ *pch == '_' || *pch == '.') {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = *pch;
+ pch++;
+ }
+ while (qemu_isspace(*pch))
+ pch++;
+ *q = 0;
+ ret = get_monitor_def(®, buf);
+ if (ret < 0)
+ expr_error(mon, "unknown register");
+ n = reg;
+ }
+ break;
+ case '\0':
+ expr_error(mon, "unexpected end of expression");
+ n = 0;
+ break;
+ default:
+ errno = 0;
+ n = strtoull(pch, &p, 0);
+ if (errno == ERANGE) {
+ expr_error(mon, "number too large");
+ }
+ if (pch == p) {
+ expr_error(mon, "invalid char '%c' in expression", *p);
+ }
+ pch = p;
+ while (qemu_isspace(*pch))
+ pch++;
+ break;
+ }
+ return n;
+}
+
+static int64_t expr_prod(Monitor *mon)
+{
+ int64_t val, val2;
+ int op;
+
+ val = expr_unary(mon);
+ for(;;) {
+ op = *pch;
+ if (op != '*' && op != '/' && op != '%')
+ break;
+ next();
+ val2 = expr_unary(mon);
+ switch(op) {
+ default:
+ case '*':
+ val *= val2;
+ break;
+ case '/':
+ case '%':
+ if (val2 == 0)
+ expr_error(mon, "division by zero");
+ if (op == '/')
+ val /= val2;
+ else
+ val %= val2;
+ break;
+ }
+ }
+ return val;
+}
+
+static int64_t expr_logic(Monitor *mon)
+{
+ int64_t val, val2;
+ int op;
+
+ val = expr_prod(mon);
+ for(;;) {
+ op = *pch;
+ if (op != '&' && op != '|' && op != '^')
+ break;
+ next();
+ val2 = expr_prod(mon);
+ switch(op) {
+ default:
+ case '&':
+ val &= val2;
+ break;
+ case '|':
+ val |= val2;
+ break;
+ case '^':
+ val ^= val2;
+ break;
+ }
+ }
+ return val;
+}
+
+static int64_t expr_sum(Monitor *mon)
+{
+ int64_t val, val2;
+ int op;
+
+ val = expr_logic(mon);
+ for(;;) {
+ op = *pch;
+ if (op != '+' && op != '-')
+ break;
+ next();
+ val2 = expr_logic(mon);
+ if (op == '+')
+ val += val2;
+ else
+ val -= val2;
+ }
+ return val;
+}
+
+static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
+{
+ pch = *pp;
+ if (sigsetjmp(expr_env, 0)) {
+ *pp = pch;
+ return -1;
+ }
+ while (qemu_isspace(*pch))
+ pch++;
+ *pval = expr_sum(mon);
+ *pp = pch;
+ return 0;
+}
+
+static int get_double(Monitor *mon, double *pval, const char **pp)
+{
+ const char *p = *pp;
+ char *tailp;
+ double d;
+
+ d = strtod(p, &tailp);
+ if (tailp == p) {
+ monitor_printf(mon, "Number expected\n");
+ return -1;
+ }
+ if (d != d || d - d != 0) {
+ /* NaN or infinity */
+ monitor_printf(mon, "Bad number\n");
+ return -1;
+ }
+ *pval = d;
+ *pp = tailp;
+ return 0;
+}
+
+/*
+ * Store the command-name in cmdname, and return a pointer to
+ * the remaining of the command string.
+ */
+static const char *get_command_name(const char *cmdline,
+ char *cmdname, size_t nlen)
+{
+ size_t len;
+ const char *p, *pstart;
+
+ p = cmdline;
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '\0')
+ return NULL;
+ pstart = p;
+ while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
+ p++;
+ len = p - pstart;
+ if (len > nlen - 1)
+ len = nlen - 1;
+ memcpy(cmdname, pstart, len);
+ cmdname[len] = '\0';
+ return p;
+}
+
+/**
+ * Read key of 'type' into 'key' and return the current
+ * 'type' pointer.
+ */
+static char *key_get_info(const char *type, char **key)
+{
+ size_t len;
+ char *p, *str;
+
+ if (*type == ',')
+ type++;
+
+ p = strchr(type, ':');
+ if (!p) {
+ *key = NULL;
+ return NULL;
+ }
+ len = p - type;
+
+ str = g_malloc(len + 1);
+ memcpy(str, type, len);
+ str[len] = '\0';
+
+ *key = str;
+ return ++p;
+}
+
+static int default_fmt_format = 'x';
+static int default_fmt_size = 4;
+
+static int is_valid_option(const char *c, const char *typestr)
+{
+ char option[3];
+
+ option[0] = '-';
+ option[1] = *c;
+ option[2] = '\0';
+
+ typestr = strstr(typestr, option);
+ return (typestr != NULL);
+}
+
+static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
+ const char *cmdname)
+{
+ const mon_cmd_t *cmd;
+
+ for (cmd = disp_table; cmd->name != NULL; cmd++) {
+ if (compare_cmd(cmdname, cmd->name)) {
+ return cmd;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Parse command name from @cmdp according to command table @table.
+ * If blank, return NULL.
+ * Else, if no valid command can be found, report to @mon, and return
+ * NULL.
+ * Else, change @cmdp to point right behind the name, and return its
+ * command table entry.
+ * Do not assume the return value points into @table! It doesn't when
+ * the command is found in a sub-command table.
+ */
+static const mon_cmd_t *monitor_parse_command(MonitorHMP *hmp_mon,
+ const char *cmdp_start,
+ const char **cmdp,
+ mon_cmd_t *table)
+{
+ Monitor *mon = &hmp_mon->common;
+ const char *p;
+ const mon_cmd_t *cmd;
+ char cmdname[256];
+
+ /* extract the command name */
+ p = get_command_name(*cmdp, cmdname, sizeof(cmdname));
+ if (!p) {
+ return NULL;
+ }
+
+ cmd = search_dispatch_table(table, cmdname);
+ if (!cmd) {
+ monitor_printf(mon, "unknown command: '%.*s'\n",
+ (int)(p - cmdp_start), cmdp_start);
+ return NULL;
+ }
+ if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
+ monitor_printf(mon, "Command '%.*s' not available with -preconfig "
+ "until after exit_preconfig.\n",
+ (int)(p - cmdp_start), cmdp_start);
+ return NULL;
+ }
+
+ /* filter out following useless space */
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+
+ *cmdp = p;
+ /* search sub command */
+ if (cmd->sub_table != NULL && *p != '\0') {
+ return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table);
+ }
+
+ return cmd;
+}
+
+/*
+ * Parse arguments for @cmd.
+ * If it can't be parsed, report to @mon, and return NULL.
+ * Else, insert command arguments into a QDict, and return it.
+ * Note: On success, caller has to free the QDict structure.
+ */
+static QDict *monitor_parse_arguments(Monitor *mon,
+ const char **endp,
+ const mon_cmd_t *cmd)
+{
+ const char *typestr;
+ char *key;
+ int c;
+ const char *p = *endp;
+ char buf[1024];
+ QDict *qdict = qdict_new();
+
+ /* parse the parameters */
+ typestr = cmd->args_type;
+ for(;;) {
+ typestr = key_get_info(typestr, &key);
+ if (!typestr) {
+ break;
+ }
+ c = *typestr;
+ typestr++;
+ switch(c) {
+ case 'F':
+ case 'B':
+ case 's':
+ {
+ int ret;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ /* no optional string: NULL argument */
+ break;
+ }
+ }
+ ret = get_str(buf, sizeof(buf), &p);
+ if (ret < 0) {
+ switch(c) {
+ case 'F':
+ monitor_printf(mon, "%s: filename expected\n",
+ cmd->name);
+ break;
+ case 'B':
+ monitor_printf(mon, "%s: block device name expected\n",
+ cmd->name);
+ break;
+ default:
+ monitor_printf(mon, "%s: string expected\n", cmd->name);
+ break;
+ }
+ goto fail;
+ }
+ qdict_put_str(qdict, key, buf);
+ }
+ break;
+ case 'O':
+ {
+ QemuOptsList *opts_list;
+ QemuOpts *opts;
+
+ opts_list = qemu_find_opts(key);
+ if (!opts_list || opts_list->desc->name) {
+ goto bad_type;
+ }
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (!*p) {
+ break;
+ }
+ if (get_str(buf, sizeof(buf), &p) < 0) {
+ goto fail;
+ }
+ opts = qemu_opts_parse_noisily(opts_list, buf, true);
+ if (!opts) {
+ goto fail;
+ }
+ qemu_opts_to_qdict(opts, qdict);
+ qemu_opts_del(opts);
+ }
+ break;
+ case '/':
+ {
+ int count, format, size;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '/') {
+ /* format found */
+ p++;
+ count = 1;
+ if (qemu_isdigit(*p)) {
+ count = 0;
+ while (qemu_isdigit(*p)) {
+ count = count * 10 + (*p - '0');
+ p++;
+ }
+ }
+ size = -1;
+ format = -1;
+ for(;;) {
+ switch(*p) {
+ case 'o':
+ case 'd':
+ case 'u':
+ case 'x':
+ case 'i':
+ case 'c':
+ format = *p++;
+ break;
+ case 'b':
+ size = 1;
+ p++;
+ break;
+ case 'h':
+ size = 2;
+ p++;
+ break;
+ case 'w':
+ size = 4;
+ p++;
+ break;
+ case 'g':
+ case 'L':
+ size = 8;
+ p++;
+ break;
+ default:
+ goto next;
+ }
+ }
+ next:
+ if (*p != '\0' && !qemu_isspace(*p)) {
+ monitor_printf(mon, "invalid char in format: '%c'\n",
+ *p);
+ goto fail;
+ }
+ if (format < 0) {
+ format = default_fmt_format;
+ }
+ if (format != 'i') {
+ /* for 'i', not specifying a size gives -1 as size */
+ if (size < 0) {
+ size = default_fmt_size;
+ }
+ default_fmt_size = size;
+ }
+ default_fmt_format = format;
+ } else {
+ count = 1;
+ format = default_fmt_format;
+ if (format != 'i') {
+ size = default_fmt_size;
+ } else {
+ size = -1;
+ }
+ }
+ qdict_put_int(qdict, "count", count);
+ qdict_put_int(qdict, "format", format);
+ qdict_put_int(qdict, "size", size);
+ }
+ break;
+ case 'i':
+ case 'l':
+ case 'M':
+ {
+ int64_t val;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*typestr == '?' || *typestr == '.') {
+ if (*typestr == '?') {
+ if (*p == '\0') {
+ typestr++;
+ break;
+ }
+ } else {
+ if (*p == '.') {
+ p++;
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ } else {
+ typestr++;
+ break;
+ }
+ }
+ typestr++;
+ }
+ if (get_expr(mon, &val, &p))
+ goto fail;
+ /* Check if 'i' is greater than 32-bit */
+ if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
+ monitor_printf(mon, "\'%s\' has failed: ", cmd->name);
+ monitor_printf(mon, "integer is for 32-bit values\n");
+ goto fail;
+ } else if (c == 'M') {
+ if (val < 0) {
+ monitor_printf(mon, "enter a positive value\n");
+ goto fail;
+ }
+ val *= MiB;
+ }
+ qdict_put_int(qdict, key, val);
+ }
+ break;
+ case 'o':
+ {
+ int ret;
+ uint64_t val;
+ const char *end;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ break;
+ }
+ }
+ ret = qemu_strtosz_MiB(p, &end, &val);
+ if (ret < 0 || val > INT64_MAX) {
+ monitor_printf(mon, "invalid size\n");
+ goto fail;
+ }
+ qdict_put_int(qdict, key, val);
+ p = end;
+ }
+ break;
+ case 'T':
+ {
+ double val;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ break;
+ }
+ }
+ if (get_double(mon, &val, &p) < 0) {
+ goto fail;
+ }
+ if (p[0] && p[1] == 's') {
+ switch (*p) {
+ case 'm':
+ val /= 1e3; p += 2; break;
+ case 'u':
+ val /= 1e6; p += 2; break;
+ case 'n':
+ val /= 1e9; p += 2; break;
+ }
+ }
+ if (*p && !qemu_isspace(*p)) {
+ monitor_printf(mon, "Unknown unit suffix\n");
+ goto fail;
+ }
+ qdict_put(qdict, key, qnum_from_double(val));
+ }
+ break;
+ case 'b':
+ {
+ const char *beg;
+ bool val;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ beg = p;
+ while (qemu_isgraph(*p)) {
+ p++;
+ }
+ if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
+ val = true;
+ } else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
+ val = false;
+ } else {
+ monitor_printf(mon, "Expected 'on' or 'off'\n");
+ goto fail;
+ }
+ qdict_put_bool(qdict, key, val);
+ }
+ break;
+ case '-':
+ {
+ const char *tmp = p;
+ int skip_key = 0;
+ /* option */
+
+ c = *typestr++;
+ if (c == '\0')
+ goto bad_type;
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '-') {
+ p++;
+ if(c != *p) {
+ if(!is_valid_option(p, typestr)) {
+ monitor_printf(mon, "%s: unsupported option -%c\n",
+ cmd->name, *p);
+ goto fail;
+ } else {
+ skip_key = 1;
+ }
+ }
+ if(skip_key) {
+ p = tmp;
+ } else {
+ /* has option */
+ p++;
+ qdict_put_bool(qdict, key, true);
+ }
+ }
+ }
+ break;
+ case 'S':
+ {
+ /* package all remaining string */
+ int len;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ /* no remaining string: NULL argument */
+ break;
+ }
+ }
+ len = strlen(p);
+ if (len <= 0) {
+ monitor_printf(mon, "%s: string expected\n",
+ cmd->name);
+ goto fail;
+ }
+ qdict_put_str(qdict, key, p);
+ p += len;
+ }
+ break;
+ default:
+ bad_type:
+ monitor_printf(mon, "%s: unknown type '%c'\n", cmd->name, c);
+ goto fail;
+ }
+ g_free(key);
+ key = NULL;
+ }
+ /* check that all arguments were parsed */
+ while (qemu_isspace(*p))
+ p++;
+ if (*p != '\0') {
+ monitor_printf(mon, "%s: extraneous characters at the end of line\n",
+ cmd->name);
+ goto fail;
+ }
+
+ return qdict;
+
+fail:
+ qobject_unref(qdict);
+ g_free(key);
+ return NULL;
+}
+
+void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
+{
+ QDict *qdict;
+ const mon_cmd_t *cmd;
+ const char *cmd_start = cmdline;
+
+ trace_handle_hmp_command(mon, cmdline);
+
+ cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
+ if (!cmd) {
+ return;
+ }
+
+ qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd);
+ if (!qdict) {
+ while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) {
+ cmdline--;
+ }
+ monitor_printf(&mon->common, "Try \"help %.*s\" for more information\n",
+ (int)(cmdline - cmd_start), cmd_start);
+ return;
+ }
+
+ cmd->cmd(&mon->common, qdict);
+ qobject_unref(qdict);
+}
+
+static void cmd_completion(MonitorHMP *mon, const char *name, const char *list)
+{
+ const char *p, *pstart;
+ char cmd[128];
+ int len;
+
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = qemu_strchrnul(p, '|');
+ len = p - pstart;
+ if (len > sizeof(cmd) - 2)
+ len = sizeof(cmd) - 2;
+ memcpy(cmd, pstart, len);
+ cmd[len] = '\0';
+ if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
+ readline_add_completion(mon->rs, cmd);
+ }
+ if (*p == '\0')
+ break;
+ p++;
+ }
+}
+
+static void file_completion(MonitorHMP *mon, const char *input)
+{
+ DIR *ffs;
+ struct dirent *d;
+ char path[1024];
+ char file[1024], file_prefix[1024];
+ int input_path_len;
+ const char *p;
+
+ p = strrchr(input, '/');
+ if (!p) {
+ input_path_len = 0;
+ pstrcpy(file_prefix, sizeof(file_prefix), input);
+ pstrcpy(path, sizeof(path), ".");
+ } else {
+ input_path_len = p - input + 1;
+ memcpy(path, input, input_path_len);
+ if (input_path_len > sizeof(path) - 1)
+ input_path_len = sizeof(path) - 1;
+ path[input_path_len] = '\0';
+ pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
+ }
+
+ ffs = opendir(path);
+ if (!ffs)
+ return;
+ for(;;) {
+ struct stat sb;
+ d = readdir(ffs);
+ if (!d)
+ break;
+
+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
+ continue;
+ }
+
+ if (strstart(d->d_name, file_prefix, NULL)) {
+ memcpy(file, input, input_path_len);
+ if (input_path_len < sizeof(file))
+ pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
+ d->d_name);
+ /* stat the file to find out if it's a directory.
+ * In that case add a slash to speed up typing long paths
+ */
+ if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ pstrcat(file, sizeof(file), "/");
+ }
+ readline_add_completion(mon->rs, file);
+ }
+ }
+ closedir(ffs);
+}
+
+static const char *next_arg_type(const char *typestr)
+{
+ const char *p = strchr(typestr, ':');
+ return (p != NULL ? ++p : typestr);
+}
+
+static void monitor_find_completion_by_table(MonitorHMP *mon,
+ const mon_cmd_t *cmd_table,
+ char **args,
+ int nb_args)
+{
+ const char *cmdname;
+ int i;
+ const char *ptype, *old_ptype, *str, *name;
+ const mon_cmd_t *cmd;
+ BlockBackend *blk = NULL;
+
+ if (nb_args <= 1) {
+ /* command completion */
+ if (nb_args == 0)
+ cmdname = "";
+ else
+ cmdname = args[0];
+ readline_set_completion_index(mon->rs, strlen(cmdname));
+ for (cmd = cmd_table; cmd->name != NULL; cmd++) {
+ if (!runstate_check(RUN_STATE_PRECONFIG) ||
+ cmd_can_preconfig(cmd)) {
+ cmd_completion(mon, cmdname, cmd->name);
+ }
+ }
+ } else {
+ /* find the command */
+ for (cmd = cmd_table; cmd->name != NULL; cmd++) {
+ if (compare_cmd(args[0], cmd->name) &&
+ (!runstate_check(RUN_STATE_PRECONFIG) ||
+ cmd_can_preconfig(cmd))) {
+ break;
+ }
+ }
+ if (!cmd->name) {
+ return;
+ }
+
+ if (cmd->sub_table) {
+ /* do the job again */
+ monitor_find_completion_by_table(mon, cmd->sub_table,
+ &args[1], nb_args - 1);
+ return;
+ }
+ if (cmd->command_completion) {
+ cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]);
+ return;
+ }
+
+ ptype = next_arg_type(cmd->args_type);
+ for(i = 0; i < nb_args - 2; i++) {
+ if (*ptype != '\0') {
+ ptype = next_arg_type(ptype);
+ while (*ptype == '?')
+ ptype = next_arg_type(ptype);
+ }
+ }
+ str = args[nb_args - 1];
+ old_ptype = NULL;
+ while (*ptype == '-' && old_ptype != ptype) {
+ old_ptype = ptype;
+ ptype = next_arg_type(ptype);
+ }
+ switch(*ptype) {
+ case 'F':
+ /* file completion */
+ readline_set_completion_index(mon->rs, strlen(str));
+ file_completion(mon, str);
+ break;
+ case 'B':
+ /* block device name completion */
+ readline_set_completion_index(mon->rs, strlen(str));
+ while ((blk = blk_next(blk)) != NULL) {
+ name = blk_name(blk);
+ if (str[0] == '\0' ||
+ !strncmp(name, str, strlen(str))) {
+ readline_add_completion(mon->rs, name);
+ }
+ }
+ break;
+ case 's':
+ case 'S':
+ if (!strcmp(cmd->name, "help|?")) {
+ monitor_find_completion_by_table(mon, cmd_table,
+ &args[1], nb_args - 1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void monitor_find_completion(void *opaque,
+ const char *cmdline)
+{
+ MonitorHMP *mon = opaque;
+ char *args[MAX_ARGS];
+ int nb_args, len;
+
+ /* 1. parse the cmdline */
+ if (parse_cmdline(cmdline, &nb_args, args) < 0) {
+ return;
+ }
+
+ /* if the line ends with a space, it means we want to complete the
+ next arg */
+ len = strlen(cmdline);
+ if (len > 0 && qemu_isspace(cmdline[len - 1])) {
+ if (nb_args >= MAX_ARGS) {
+ goto cleanup;
+ }
+ args[nb_args++] = g_strdup("");
+ }
+
+ /* 2. auto complete according to args */
+ monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
+
+cleanup:
+ free_cmdline_args(args, nb_args);
+}
+
+static void monitor_read(void *opaque, const uint8_t *buf, int size)
+{
+ MonitorHMP *mon;
+ Monitor *old_mon = cur_mon;
+ 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");
+ } else {
+ handle_hmp_command(mon, (char *)buf);
+ }
+ }
+
+ cur_mon = old_mon;
+}
+
+static void monitor_event(void *opaque, int event)
+{
+ Monitor *mon = opaque;
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+
+ switch (event) {
+ case CHR_EVENT_MUX_IN:
+ qemu_mutex_lock(&mon->mon_lock);
+ mon->mux_out = 0;
+ qemu_mutex_unlock(&mon->mon_lock);
+ if (mon->reset_seen) {
+ readline_restart(hmp_mon->rs);
+ monitor_resume(mon);
+ monitor_flush(mon);
+ } else {
+ atomic_mb_set(&mon->suspend_cnt, 0);
+ }
+ break;
+
+ case CHR_EVENT_MUX_OUT:
+ if (mon->reset_seen) {
+ if (atomic_mb_read(&mon->suspend_cnt) == 0) {
+ monitor_printf(mon, "\n");
+ }
+ monitor_flush(mon);
+ monitor_suspend(mon);
+ } else {
+ atomic_inc(&mon->suspend_cnt);
+ }
+ qemu_mutex_lock(&mon->mon_lock);
+ mon->mux_out = 1;
+ qemu_mutex_unlock(&mon->mon_lock);
+ break;
+
+ case CHR_EVENT_OPENED:
+ monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
+ "information\n", QEMU_VERSION);
+ if (!mon->mux_out) {
+ readline_restart(hmp_mon->rs);
+ readline_show_prompt(hmp_mon->rs);
+ }
+ mon->reset_seen = 1;
+ mon_refcount++;
+ break;
+
+ case CHR_EVENT_CLOSED:
+ mon_refcount--;
+ monitor_fdsets_cleanup();
+ break;
+ }
+}
+
+
+/* These functions just adapt the readline interface in a typesafe way. We
+ * could cast function pointers but that discards compiler checks.
+ */
+static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
+ const char *fmt, ...)
+{
+ MonitorHMP *mon = opaque;
+ va_list ap;
+ va_start(ap, fmt);
+ monitor_vprintf(&mon->common, fmt, ap);
+ va_end(ap);
+}
+
+static void monitor_readline_flush(void *opaque)
+{
+ MonitorHMP *mon = opaque;
+ monitor_flush(&mon->common);
+}
+
+void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
+{
+ monitor_data_init(&mon->common, flags, skip_flush, false);
+
+ /* Use *mon_cmds by default. */
+ mon->cmd_table = mon_cmds;
+}
+
+void monitor_init_hmp(Chardev *chr, int flags)
+{
+ MonitorHMP *mon = g_malloc0(sizeof(*mon));
+ bool use_readline = flags & MONITOR_USE_READLINE;
+
+ monitor_data_init_hmp(mon, flags, false);
+ qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
+
+ if (use_readline) {
+ mon->rs = readline_init(monitor_readline_printf,
+ monitor_readline_flush,
+ mon,
+ monitor_find_completion);
+ monitor_read_command(mon, 0);
+ }
+
+ qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read, monitor_read,
+ monitor_event, NULL, &mon->common, NULL, true);
+ monitor_list_append(&mon->common);
+}
diff --git a/monitor/misc.c b/monitor/misc.c
index 6c67f0978c..408d11e1fe 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -24,7 +24,6 @@
#include "qemu/osdep.h"
#include "monitor_int.h"
-#include "qemu/units.h"
#include <dirent.h>
#include "cpu.h"
#include "hw/hw.h"
@@ -155,7 +154,6 @@ static QLIST_HEAD(, MonFdset) mon_fdsets;
int mon_refcount;
-static mon_cmd_t mon_cmds[];
static mon_cmd_t info_cmds[];
__thread Monitor *cur_mon;
@@ -540,8 +538,6 @@ static void monitor_qapi_event_init(void)
qapi_event_throttle_equal);
}
-static void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
-
static void monitor_iothread_init(void);
void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
@@ -558,14 +554,6 @@ void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
mon->flags = flags;
}
-static void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
-{
- monitor_data_init(&mon->common, flags, skip_flush, false);
-
- /* Use *mon_cmds by default. */
- mon->cmd_table = mon_cmds;
-}
-
static void monitor_data_destroy(Monitor *mon)
{
g_free(mon->mon_cpu_path);
@@ -619,248 +607,6 @@ out:
return output;
}
-static int compare_cmd(const char *name, const char *list)
-{
- const char *p, *pstart;
- int len;
- len = strlen(name);
- p = list;
- for(;;) {
- pstart = p;
- p = qemu_strchrnul(p, '|');
- if ((p - pstart) == len && !memcmp(pstart, name, len))
- return 1;
- if (*p == '\0')
- break;
- p++;
- }
- return 0;
-}
-
-static int get_str(char *buf, int buf_size, const char **pp)
-{
- const char *p;
- char *q;
- int c;
-
- q = buf;
- p = *pp;
- while (qemu_isspace(*p)) {
- p++;
- }
- if (*p == '\0') {
- fail:
- *q = '\0';
- *pp = p;
- return -1;
- }
- if (*p == '\"') {
- p++;
- while (*p != '\0' && *p != '\"') {
- if (*p == '\\') {
- p++;
- c = *p++;
- switch (c) {
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case '\\':
- case '\'':
- case '\"':
- break;
- default:
- printf("unsupported escape code: '\\%c'\n", c);
- goto fail;
- }
- if ((q - buf) < buf_size - 1) {
- *q++ = c;
- }
- } else {
- if ((q - buf) < buf_size - 1) {
- *q++ = *p;
- }
- p++;
- }
- }
- if (*p != '\"') {
- printf("unterminated string\n");
- goto fail;
- }
- p++;
- } else {
- while (*p != '\0' && !qemu_isspace(*p)) {
- if ((q - buf) < buf_size - 1) {
- *q++ = *p;
- }
- p++;
- }
- }
- *q = '\0';
- *pp = p;
- return 0;
-}
-
-#define MAX_ARGS 16
-
-static void free_cmdline_args(char **args, int nb_args)
-{
- int i;
-
- assert(nb_args <= MAX_ARGS);
-
- for (i = 0; i < nb_args; i++) {
- g_free(args[i]);
- }
-
-}
-
-/*
- * Parse the command line to get valid args.
- * @cmdline: command line to be parsed.
- * @pnb_args: location to store the number of args, must NOT be NULL.
- * @args: location to store the args, which should be freed by caller, must
- * NOT be NULL.
- *
- * Returns 0 on success, negative on failure.
- *
- * NOTE: this parser is an approximate form of the real command parser. Number
- * of args have a limit of MAX_ARGS. If cmdline contains more, it will
- * return with failure.
- */
-static int parse_cmdline(const char *cmdline,
- int *pnb_args, char **args)
-{
- const char *p;
- int nb_args, ret;
- char buf[1024];
-
- p = cmdline;
- nb_args = 0;
- for (;;) {
- while (qemu_isspace(*p)) {
- p++;
- }
- if (*p == '\0') {
- break;
- }
- if (nb_args >= MAX_ARGS) {
- goto fail;
- }
- ret = get_str(buf, sizeof(buf), &p);
- if (ret < 0) {
- goto fail;
- }
- args[nb_args] = g_strdup(buf);
- nb_args++;
- }
- *pnb_args = nb_args;
- return 0;
-
- fail:
- free_cmdline_args(args, nb_args);
- return -1;
-}
-
-/*
- * Can command @cmd be executed in preconfig state?
- */
-static bool cmd_can_preconfig(const mon_cmd_t *cmd)
-{
- if (!cmd->flags) {
- return false;
- }
-
- return strchr(cmd->flags, 'p');
-}
-
-static void help_cmd_dump_one(Monitor *mon,
- const mon_cmd_t *cmd,
- char **prefix_args,
- int prefix_args_nb)
-{
- int i;
-
- if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
- return;
- }
-
- for (i = 0; i < prefix_args_nb; i++) {
- monitor_printf(mon, "%s ", prefix_args[i]);
- }
- monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
-}
-
-/* @args[@arg_index] is the valid command need to find in @cmds */
-static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
- char **args, int nb_args, int arg_index)
-{
- const mon_cmd_t *cmd;
- size_t i;
-
- /* No valid arg need to compare with, dump all in *cmds */
- if (arg_index >= nb_args) {
- for (cmd = cmds; cmd->name != NULL; cmd++) {
- help_cmd_dump_one(mon, cmd, args, arg_index);
- }
- return;
- }
-
- /* Find one entry to dump */
- for (cmd = cmds; cmd->name != NULL; cmd++) {
- if (compare_cmd(args[arg_index], cmd->name) &&
- ((!runstate_check(RUN_STATE_PRECONFIG) ||
- cmd_can_preconfig(cmd)))) {
- if (cmd->sub_table) {
- /* continue with next arg */
- help_cmd_dump(mon, cmd->sub_table,
- args, nb_args, arg_index + 1);
- } else {
- help_cmd_dump_one(mon, cmd, args, arg_index);
- }
- return;
- }
- }
-
- /* Command not found */
- monitor_printf(mon, "unknown command: '");
- for (i = 0; i <= arg_index; i++) {
- monitor_printf(mon, "%s%s", args[i], i == arg_index ? "'\n" : " ");
- }
-}
-
-static void help_cmd(Monitor *mon, const char *name)
-{
- MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
- char *args[MAX_ARGS];
- int nb_args = 0;
-
- /* 1. parse user input */
- if (name) {
- /* special case for log, directly dump and return */
- if (!strcmp(name, "log")) {
- const QEMULogItem *item;
- monitor_printf(mon, "Log items (comma separated):\n");
- monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
- for (item = qemu_log_items; item->mask != 0; item++) {
- monitor_printf(mon, "%-10s %s\n", item->name, item->help);
- }
- return;
- }
-
- if (parse_cmdline(name, &nb_args, args) < 0) {
- return;
- }
- }
-
- /* 2. dump the contents according to parsed args */
- help_cmd_dump(mon, hmp_mon->cmd_table, args, nb_args, 0);
-
- free_cmdline_args(args, nb_args);
-}
-
static void do_help_cmd(Monitor *mon, const QDict *qdict)
{
help_cmd(mon, qdict_get_try_str(qdict, "name"));
@@ -2510,30 +2256,16 @@ static mon_cmd_t info_cmds[] = {
};
/* mon_cmds and info_cmds would be sorted at runtime */
-static mon_cmd_t mon_cmds[] = {
+mon_cmd_t mon_cmds[] = {
#include "hmp-commands.h"
{ NULL, NULL, },
};
-/*******************************************************************/
-
-static const char *pch;
-static sigjmp_buf expr_env;
-
-
-static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
-expr_error(Monitor *mon, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- monitor_vprintf(mon, fmt, ap);
- monitor_printf(mon, "\n");
- va_end(ap);
- siglongjmp(expr_env, 1);
-}
-
-/* return 0 if OK, -1 if not found */
-static int get_monitor_def(target_long *pval, const char *name)
+/*
+ * Set @pval to the value in the register identified by @name.
+ * return 0 if OK, -1 if not found
+ */
+int get_monitor_def(int64_t *pval, const char *name)
{
const MonitorDef *md = target_monitor_defs();
CPUState *cs = mon_get_cpu();
@@ -2576,829 +2308,6 @@ static int get_monitor_def(target_long *pval, const char *name)
return ret;
}
-static void next(void)
-{
- if (*pch != '\0') {
- pch++;
- while (qemu_isspace(*pch))
- pch++;
- }
-}
-
-static int64_t expr_sum(Monitor *mon);
-
-static int64_t expr_unary(Monitor *mon)
-{
- int64_t n;
- char *p;
- int ret;
-
- switch(*pch) {
- case '+':
- next();
- n = expr_unary(mon);
- break;
- case '-':
- next();
- n = -expr_unary(mon);
- break;
- case '~':
- next();
- n = ~expr_unary(mon);
- break;
- case '(':
- next();
- n = expr_sum(mon);
- if (*pch != ')') {
- expr_error(mon, "')' expected");
- }
- next();
- break;
- case '\'':
- pch++;
- if (*pch == '\0')
- expr_error(mon, "character constant expected");
- n = *pch;
- pch++;
- if (*pch != '\'')
- expr_error(mon, "missing terminating \' character");
- next();
- break;
- case '$':
- {
- char buf[128], *q;
- target_long reg=0;
-
- pch++;
- q = buf;
- while ((*pch >= 'a' && *pch <= 'z') ||
- (*pch >= 'A' && *pch <= 'Z') ||
- (*pch >= '0' && *pch <= '9') ||
- *pch == '_' || *pch == '.') {
- if ((q - buf) < sizeof(buf) - 1)
- *q++ = *pch;
- pch++;
- }
- while (qemu_isspace(*pch))
- pch++;
- *q = 0;
- ret = get_monitor_def(®, buf);
- if (ret < 0)
- expr_error(mon, "unknown register");
- n = reg;
- }
- break;
- case '\0':
- expr_error(mon, "unexpected end of expression");
- n = 0;
- break;
- default:
- errno = 0;
- n = strtoull(pch, &p, 0);
- if (errno == ERANGE) {
- expr_error(mon, "number too large");
- }
- if (pch == p) {
- expr_error(mon, "invalid char '%c' in expression", *p);
- }
- pch = p;
- while (qemu_isspace(*pch))
- pch++;
- break;
- }
- return n;
-}
-
-
-static int64_t expr_prod(Monitor *mon)
-{
- int64_t val, val2;
- int op;
-
- val = expr_unary(mon);
- for(;;) {
- op = *pch;
- if (op != '*' && op != '/' && op != '%')
- break;
- next();
- val2 = expr_unary(mon);
- switch(op) {
- default:
- case '*':
- val *= val2;
- break;
- case '/':
- case '%':
- if (val2 == 0)
- expr_error(mon, "division by zero");
- if (op == '/')
- val /= val2;
- else
- val %= val2;
- break;
- }
- }
- return val;
-}
-
-static int64_t expr_logic(Monitor *mon)
-{
- int64_t val, val2;
- int op;
-
- val = expr_prod(mon);
- for(;;) {
- op = *pch;
- if (op != '&' && op != '|' && op != '^')
- break;
- next();
- val2 = expr_prod(mon);
- switch(op) {
- default:
- case '&':
- val &= val2;
- break;
- case '|':
- val |= val2;
- break;
- case '^':
- val ^= val2;
- break;
- }
- }
- return val;
-}
-
-static int64_t expr_sum(Monitor *mon)
-{
- int64_t val, val2;
- int op;
-
- val = expr_logic(mon);
- for(;;) {
- op = *pch;
- if (op != '+' && op != '-')
- break;
- next();
- val2 = expr_logic(mon);
- if (op == '+')
- val += val2;
- else
- val -= val2;
- }
- return val;
-}
-
-static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
-{
- pch = *pp;
- if (sigsetjmp(expr_env, 0)) {
- *pp = pch;
- return -1;
- }
- while (qemu_isspace(*pch))
- pch++;
- *pval = expr_sum(mon);
- *pp = pch;
- return 0;
-}
-
-static int get_double(Monitor *mon, double *pval, const char **pp)
-{
- const char *p = *pp;
- char *tailp;
- double d;
-
- d = strtod(p, &tailp);
- if (tailp == p) {
- monitor_printf(mon, "Number expected\n");
- return -1;
- }
- if (d != d || d - d != 0) {
- /* NaN or infinity */
- monitor_printf(mon, "Bad number\n");
- return -1;
- }
- *pval = d;
- *pp = tailp;
- return 0;
-}
-
-/*
- * Store the command-name in cmdname, and return a pointer to
- * the remaining of the command string.
- */
-static const char *get_command_name(const char *cmdline,
- char *cmdname, size_t nlen)
-{
- size_t len;
- const char *p, *pstart;
-
- p = cmdline;
- while (qemu_isspace(*p))
- p++;
- if (*p == '\0')
- return NULL;
- pstart = p;
- while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
- p++;
- len = p - pstart;
- if (len > nlen - 1)
- len = nlen - 1;
- memcpy(cmdname, pstart, len);
- cmdname[len] = '\0';
- return p;
-}
-
-/**
- * Read key of 'type' into 'key' and return the current
- * 'type' pointer.
- */
-static char *key_get_info(const char *type, char **key)
-{
- size_t len;
- char *p, *str;
-
- if (*type == ',')
- type++;
-
- p = strchr(type, ':');
- if (!p) {
- *key = NULL;
- return NULL;
- }
- len = p - type;
-
- str = g_malloc(len + 1);
- memcpy(str, type, len);
- str[len] = '\0';
-
- *key = str;
- return ++p;
-}
-
-static int default_fmt_format = 'x';
-static int default_fmt_size = 4;
-
-static int is_valid_option(const char *c, const char *typestr)
-{
- char option[3];
-
- option[0] = '-';
- option[1] = *c;
- option[2] = '\0';
-
- typestr = strstr(typestr, option);
- return (typestr != NULL);
-}
-
-static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
- const char *cmdname)
-{
- const mon_cmd_t *cmd;
-
- for (cmd = disp_table; cmd->name != NULL; cmd++) {
- if (compare_cmd(cmdname, cmd->name)) {
- return cmd;
- }
- }
-
- return NULL;
-}
-
-/*
- * Parse command name from @cmdp according to command table @table.
- * If blank, return NULL.
- * Else, if no valid command can be found, report to @mon, and return
- * NULL.
- * Else, change @cmdp to point right behind the name, and return its
- * command table entry.
- * Do not assume the return value points into @table! It doesn't when
- * the command is found in a sub-command table.
- */
-static const mon_cmd_t *monitor_parse_command(MonitorHMP *hmp_mon,
- const char *cmdp_start,
- const char **cmdp,
- mon_cmd_t *table)
-{
- Monitor *mon = &hmp_mon->common;
- const char *p;
- const mon_cmd_t *cmd;
- char cmdname[256];
-
- /* extract the command name */
- p = get_command_name(*cmdp, cmdname, sizeof(cmdname));
- if (!p)
- return NULL;
-
- cmd = search_dispatch_table(table, cmdname);
- if (!cmd) {
- monitor_printf(mon, "unknown command: '%.*s'\n",
- (int)(p - cmdp_start), cmdp_start);
- return NULL;
- }
- if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
- monitor_printf(mon, "Command '%.*s' not available with -preconfig "
- "until after exit_preconfig.\n",
- (int)(p - cmdp_start), cmdp_start);
- return NULL;
- }
-
- /* filter out following useless space */
- while (qemu_isspace(*p)) {
- p++;
- }
-
- *cmdp = p;
- /* search sub command */
- if (cmd->sub_table != NULL && *p != '\0') {
- return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table);
- }
-
- return cmd;
-}
-
-/*
- * Parse arguments for @cmd.
- * If it can't be parsed, report to @mon, and return NULL.
- * Else, insert command arguments into a QDict, and return it.
- * Note: On success, caller has to free the QDict structure.
- */
-
-static QDict *monitor_parse_arguments(Monitor *mon,
- const char **endp,
- const mon_cmd_t *cmd)
-{
- const char *typestr;
- char *key;
- int c;
- const char *p = *endp;
- char buf[1024];
- QDict *qdict = qdict_new();
-
- /* parse the parameters */
- typestr = cmd->args_type;
- for(;;) {
- typestr = key_get_info(typestr, &key);
- if (!typestr)
- break;
- c = *typestr;
- typestr++;
- switch(c) {
- case 'F':
- case 'B':
- case 's':
- {
- int ret;
-
- while (qemu_isspace(*p))
- p++;
- if (*typestr == '?') {
- typestr++;
- if (*p == '\0') {
- /* no optional string: NULL argument */
- break;
- }
- }
- ret = get_str(buf, sizeof(buf), &p);
- if (ret < 0) {
- switch(c) {
- case 'F':
- monitor_printf(mon, "%s: filename expected\n",
- cmd->name);
- break;
- case 'B':
- monitor_printf(mon, "%s: block device name expected\n",
- cmd->name);
- break;
- default:
- monitor_printf(mon, "%s: string expected\n", cmd->name);
- break;
- }
- goto fail;
- }
- qdict_put_str(qdict, key, buf);
- }
- break;
- case 'O':
- {
- QemuOptsList *opts_list;
- QemuOpts *opts;
-
- opts_list = qemu_find_opts(key);
- if (!opts_list || opts_list->desc->name) {
- goto bad_type;
- }
- while (qemu_isspace(*p)) {
- p++;
- }
- if (!*p)
- break;
- if (get_str(buf, sizeof(buf), &p) < 0) {
- goto fail;
- }
- opts = qemu_opts_parse_noisily(opts_list, buf, true);
- if (!opts) {
- goto fail;
- }
- qemu_opts_to_qdict(opts, qdict);
- qemu_opts_del(opts);
- }
- break;
- case '/':
- {
- int count, format, size;
-
- while (qemu_isspace(*p))
- p++;
- if (*p == '/') {
- /* format found */
- p++;
- count = 1;
- if (qemu_isdigit(*p)) {
- count = 0;
- while (qemu_isdigit(*p)) {
- count = count * 10 + (*p - '0');
- p++;
- }
- }
- size = -1;
- format = -1;
- for(;;) {
- switch(*p) {
- case 'o':
- case 'd':
- case 'u':
- case 'x':
- case 'i':
- case 'c':
- format = *p++;
- break;
- case 'b':
- size = 1;
- p++;
- break;
- case 'h':
- size = 2;
- p++;
- break;
- case 'w':
- size = 4;
- p++;
- break;
- case 'g':
- case 'L':
- size = 8;
- p++;
- break;
- default:
- goto next;
- }
- }
- next:
- if (*p != '\0' && !qemu_isspace(*p)) {
- monitor_printf(mon, "invalid char in format: '%c'\n",
- *p);
- goto fail;
- }
- if (format < 0)
- format = default_fmt_format;
- if (format != 'i') {
- /* for 'i', not specifying a size gives -1 as size */
- if (size < 0)
- size = default_fmt_size;
- default_fmt_size = size;
- }
- default_fmt_format = format;
- } else {
- count = 1;
- format = default_fmt_format;
- if (format != 'i') {
- size = default_fmt_size;
- } else {
- size = -1;
- }
- }
- qdict_put_int(qdict, "count", count);
- qdict_put_int(qdict, "format", format);
- qdict_put_int(qdict, "size", size);
- }
- break;
- case 'i':
- case 'l':
- case 'M':
- {
- int64_t val;
-
- while (qemu_isspace(*p))
- p++;
- if (*typestr == '?' || *typestr == '.') {
- if (*typestr == '?') {
- if (*p == '\0') {
- typestr++;
- break;
- }
- } else {
- if (*p == '.') {
- p++;
- while (qemu_isspace(*p))
- p++;
- } else {
- typestr++;
- break;
- }
- }
- typestr++;
- }
- if (get_expr(mon, &val, &p))
- goto fail;
- /* Check if 'i' is greater than 32-bit */
- if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
- monitor_printf(mon, "\'%s\' has failed: ", cmd->name);
- monitor_printf(mon, "integer is for 32-bit values\n");
- goto fail;
- } else if (c == 'M') {
- if (val < 0) {
- monitor_printf(mon, "enter a positive value\n");
- goto fail;
- }
- val *= MiB;
- }
- qdict_put_int(qdict, key, val);
- }
- break;
- case 'o':
- {
- int ret;
- uint64_t val;
- const char *end;
-
- while (qemu_isspace(*p)) {
- p++;
- }
- if (*typestr == '?') {
- typestr++;
- if (*p == '\0') {
- break;
- }
- }
- ret = qemu_strtosz_MiB(p, &end, &val);
- if (ret < 0 || val > INT64_MAX) {
- monitor_printf(mon, "invalid size\n");
- goto fail;
- }
- qdict_put_int(qdict, key, val);
- p = end;
- }
- break;
- case 'T':
- {
- double val;
-
- while (qemu_isspace(*p))
- p++;
- if (*typestr == '?') {
- typestr++;
- if (*p == '\0') {
- break;
- }
- }
- if (get_double(mon, &val, &p) < 0) {
- goto fail;
- }
- if (p[0] && p[1] == 's') {
- switch (*p) {
- case 'm':
- val /= 1e3; p += 2; break;
- case 'u':
- val /= 1e6; p += 2; break;
- case 'n':
- val /= 1e9; p += 2; break;
- }
- }
- if (*p && !qemu_isspace(*p)) {
- monitor_printf(mon, "Unknown unit suffix\n");
- goto fail;
- }
- qdict_put(qdict, key, qnum_from_double(val));
- }
- break;
- case 'b':
- {
- const char *beg;
- bool val;
-
- while (qemu_isspace(*p)) {
- p++;
- }
- beg = p;
- while (qemu_isgraph(*p)) {
- p++;
- }
- if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
- val = true;
- } else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
- val = false;
- } else {
- monitor_printf(mon, "Expected 'on' or 'off'\n");
- goto fail;
- }
- qdict_put_bool(qdict, key, val);
- }
- break;
- case '-':
- {
- const char *tmp = p;
- int skip_key = 0;
- /* option */
-
- c = *typestr++;
- if (c == '\0')
- goto bad_type;
- while (qemu_isspace(*p))
- p++;
- if (*p == '-') {
- p++;
- if(c != *p) {
- if(!is_valid_option(p, typestr)) {
-
- monitor_printf(mon, "%s: unsupported option -%c\n",
- cmd->name, *p);
- goto fail;
- } else {
- skip_key = 1;
- }
- }
- if(skip_key) {
- p = tmp;
- } else {
- /* has option */
- p++;
- qdict_put_bool(qdict, key, true);
- }
- }
- }
- break;
- case 'S':
- {
- /* package all remaining string */
- int len;
-
- while (qemu_isspace(*p)) {
- p++;
- }
- if (*typestr == '?') {
- typestr++;
- if (*p == '\0') {
- /* no remaining string: NULL argument */
- break;
- }
- }
- len = strlen(p);
- if (len <= 0) {
- monitor_printf(mon, "%s: string expected\n",
- cmd->name);
- goto fail;
- }
- qdict_put_str(qdict, key, p);
- p += len;
- }
- break;
- default:
- bad_type:
- monitor_printf(mon, "%s: unknown type '%c'\n", cmd->name, c);
- goto fail;
- }
- g_free(key);
- key = NULL;
- }
- /* check that all arguments were parsed */
- while (qemu_isspace(*p))
- p++;
- if (*p != '\0') {
- monitor_printf(mon, "%s: extraneous characters at the end of line\n",
- cmd->name);
- goto fail;
- }
-
- return qdict;
-
-fail:
- qobject_unref(qdict);
- g_free(key);
- return NULL;
-}
-
-static void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
-{
- QDict *qdict;
- const mon_cmd_t *cmd;
- const char *cmd_start = cmdline;
-
- trace_handle_hmp_command(mon, cmdline);
-
- cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
- if (!cmd) {
- return;
- }
-
- qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd);
- if (!qdict) {
- while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) {
- cmdline--;
- }
- monitor_printf(&mon->common, "Try \"help %.*s\" for more information\n",
- (int)(cmdline - cmd_start), cmd_start);
- return;
- }
-
- cmd->cmd(&mon->common, qdict);
- qobject_unref(qdict);
-}
-
-static void cmd_completion(MonitorHMP *mon, const char *name, const char *list)
-{
- const char *p, *pstart;
- char cmd[128];
- int len;
-
- p = list;
- for(;;) {
- pstart = p;
- p = qemu_strchrnul(p, '|');
- len = p - pstart;
- if (len > sizeof(cmd) - 2)
- len = sizeof(cmd) - 2;
- memcpy(cmd, pstart, len);
- cmd[len] = '\0';
- if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
- readline_add_completion(mon->rs, cmd);
- }
- if (*p == '\0')
- break;
- p++;
- }
-}
-
-static void file_completion(MonitorHMP *mon, const char *input)
-{
- DIR *ffs;
- struct dirent *d;
- char path[1024];
- char file[1024], file_prefix[1024];
- int input_path_len;
- const char *p;
-
- p = strrchr(input, '/');
- if (!p) {
- input_path_len = 0;
- pstrcpy(file_prefix, sizeof(file_prefix), input);
- pstrcpy(path, sizeof(path), ".");
- } else {
- input_path_len = p - input + 1;
- memcpy(path, input, input_path_len);
- if (input_path_len > sizeof(path) - 1)
- input_path_len = sizeof(path) - 1;
- path[input_path_len] = '\0';
- pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
- }
-
- ffs = opendir(path);
- if (!ffs)
- return;
- for(;;) {
- struct stat sb;
- d = readdir(ffs);
- if (!d)
- break;
-
- if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
- continue;
- }
-
- if (strstart(d->d_name, file_prefix, NULL)) {
- memcpy(file, input, input_path_len);
- if (input_path_len < sizeof(file))
- pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
- d->d_name);
- /* stat the file to find out if it's a directory.
- * In that case add a slash to speed up typing long paths
- */
- if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
- pstrcat(file, sizeof(file), "/");
- }
- readline_add_completion(mon->rs, file);
- }
- }
- closedir(ffs);
-}
-
-static const char *next_arg_type(const char *typestr)
-{
- const char *p = strchr(typestr, ':');
- return (p != NULL ? ++p : typestr);
-}
-
static void add_completion_option(ReadLineState *rs, const char *str,
const char *option)
{
@@ -3829,127 +2738,6 @@ void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
}
}
-static void monitor_find_completion_by_table(MonitorHMP *mon,
- const mon_cmd_t *cmd_table,
- char **args,
- int nb_args)
-{
- const char *cmdname;
- int i;
- const char *ptype, *old_ptype, *str, *name;
- const mon_cmd_t *cmd;
- BlockBackend *blk = NULL;
-
- if (nb_args <= 1) {
- /* command completion */
- if (nb_args == 0)
- cmdname = "";
- else
- cmdname = args[0];
- readline_set_completion_index(mon->rs, strlen(cmdname));
- for (cmd = cmd_table; cmd->name != NULL; cmd++) {
- if (!runstate_check(RUN_STATE_PRECONFIG) ||
- cmd_can_preconfig(cmd)) {
- cmd_completion(mon, cmdname, cmd->name);
- }
- }
- } else {
- /* find the command */
- for (cmd = cmd_table; cmd->name != NULL; cmd++) {
- if (compare_cmd(args[0], cmd->name) &&
- (!runstate_check(RUN_STATE_PRECONFIG) ||
- cmd_can_preconfig(cmd))) {
- break;
- }
- }
- if (!cmd->name) {
- return;
- }
-
- if (cmd->sub_table) {
- /* do the job again */
- monitor_find_completion_by_table(mon, cmd->sub_table,
- &args[1], nb_args - 1);
- return;
- }
- if (cmd->command_completion) {
- cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]);
- return;
- }
-
- ptype = next_arg_type(cmd->args_type);
- for(i = 0; i < nb_args - 2; i++) {
- if (*ptype != '\0') {
- ptype = next_arg_type(ptype);
- while (*ptype == '?')
- ptype = next_arg_type(ptype);
- }
- }
- str = args[nb_args - 1];
- old_ptype = NULL;
- while (*ptype == '-' && old_ptype != ptype) {
- old_ptype = ptype;
- ptype = next_arg_type(ptype);
- }
- switch(*ptype) {
- case 'F':
- /* file completion */
- readline_set_completion_index(mon->rs, strlen(str));
- file_completion(mon, str);
- break;
- case 'B':
- /* block device name completion */
- readline_set_completion_index(mon->rs, strlen(str));
- while ((blk = blk_next(blk)) != NULL) {
- name = blk_name(blk);
- if (str[0] == '\0' ||
- !strncmp(name, str, strlen(str))) {
- readline_add_completion(mon->rs, name);
- }
- }
- break;
- case 's':
- case 'S':
- if (!strcmp(cmd->name, "help|?")) {
- monitor_find_completion_by_table(mon, cmd_table,
- &args[1], nb_args - 1);
- }
- break;
- default:
- break;
- }
- }
-}
-
-static void monitor_find_completion(void *opaque,
- const char *cmdline)
-{
- MonitorHMP *mon = opaque;
- char *args[MAX_ARGS];
- int nb_args, len;
-
- /* 1. parse the cmdline */
- if (parse_cmdline(cmdline, &nb_args, args) < 0) {
- return;
- }
-
- /* if the line ends with a space, it means we want to complete the
- next arg */
- len = strlen(cmdline);
- if (len > 0 && qemu_isspace(cmdline[len - 1])) {
- if (nb_args >= MAX_ARGS) {
- goto cleanup;
- }
- args[nb_args++] = g_strdup("");
- }
-
- /* 2. auto complete according to args */
- monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
-
-cleanup:
- free_cmdline_args(args, nb_args);
-}
-
int monitor_can_read(void *opaque)
{
Monitor *mon = opaque;
@@ -3957,28 +2745,6 @@ int monitor_can_read(void *opaque)
return !atomic_mb_read(&mon->suspend_cnt);
}
-static void monitor_read(void *opaque, const uint8_t *buf, int size)
-{
- MonitorHMP *mon;
- Monitor *old_mon = cur_mon;
- 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");
- else
- handle_hmp_command(mon, (char *)buf);
- }
-
- cur_mon = old_mon;
-}
-
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque)
{
@@ -4043,58 +2809,6 @@ void monitor_resume(Monitor *mon)
trace_monitor_suspend(mon, -1);
}
-static void monitor_event(void *opaque, int event)
-{
- Monitor *mon = opaque;
- MonitorHMP *hmp_mon = container_of(cur_mon, MonitorHMP, common);
-
- switch (event) {
- case CHR_EVENT_MUX_IN:
- qemu_mutex_lock(&mon->mon_lock);
- mon->mux_out = 0;
- qemu_mutex_unlock(&mon->mon_lock);
- if (mon->reset_seen) {
- readline_restart(hmp_mon->rs);
- monitor_resume(mon);
- monitor_flush(mon);
- } else {
- atomic_mb_set(&mon->suspend_cnt, 0);
- }
- break;
-
- case CHR_EVENT_MUX_OUT:
- if (mon->reset_seen) {
- if (atomic_mb_read(&mon->suspend_cnt) == 0) {
- monitor_printf(mon, "\n");
- }
- monitor_flush(mon);
- monitor_suspend(mon);
- } else {
- atomic_inc(&mon->suspend_cnt);
- }
- qemu_mutex_lock(&mon->mon_lock);
- mon->mux_out = 1;
- qemu_mutex_unlock(&mon->mon_lock);
- break;
-
- case CHR_EVENT_OPENED:
- monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
- "information\n", QEMU_VERSION);
- if (!mon->mux_out) {
- readline_restart(hmp_mon->rs);
- readline_show_prompt(hmp_mon->rs);
- }
- mon->reset_seen = 1;
- mon_refcount++;
- break;
-
- case CHR_EVENT_CLOSED:
- mon_refcount--;
- monitor_fdsets_cleanup();
- break;
- }
-}
-
static int
compare_mon_cmd(const void *a, const void *b)
{
@@ -4137,25 +2851,6 @@ void monitor_init_globals(void)
NULL);
}
-/* These functions just adapt the readline interface in a typesafe way. We
- * could cast function pointers but that discards compiler checks.
- */
-static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
- const char *fmt, ...)
-{
- MonitorHMP *mon = opaque;
- va_list ap;
- va_start(ap, fmt);
- monitor_vprintf(&mon->common, fmt, ap);
- va_end(ap);
-}
-
-static void monitor_readline_flush(void *opaque)
-{
- MonitorHMP *mon = opaque;
- monitor_flush(&mon->common);
-}
-
/*
* Print to current monitor if we have one, else to stderr.
*/
@@ -4198,27 +2893,6 @@ void monitor_list_append(Monitor *mon)
}
}
-static void monitor_init_hmp(Chardev *chr, int flags)
-{
- MonitorHMP *mon = g_malloc0(sizeof(*mon));
- bool use_readline = flags & MONITOR_USE_READLINE;
-
- monitor_data_init_hmp(mon, flags, false);
- qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
-
- if (use_readline) {
- mon->rs = readline_init(monitor_readline_printf,
- monitor_readline_flush,
- mon,
- monitor_find_completion);
- monitor_read_command(mon, 0);
- }
-
- qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read, monitor_read,
- monitor_event, NULL, &mon->common, NULL, true);
- monitor_list_append(&mon->common);
-}
-
void monitor_init(Chardev *chr, int flags)
{
if (flags & MONITOR_USE_CONTROL) {
diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
index d04d58b583..48c73eed51 100644
--- a/monitor/Makefile.objs
+++ b/monitor/Makefile.objs
@@ -1,2 +1,2 @@
obj-y += misc.o
-common-obj-y += qmp.o
+common-obj-y += qmp.o hmp.o
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 09/10] monitor: Split out monitor/hmp.c
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 09/10] monitor: Split out monitor/hmp.c Kevin Wolf
@ 2019-06-07 17:21 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 17:21 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Move HMP infrastructure from monitor/misc.c to monitor/hmp.c. This is
> code that can be shared for all targets, so compile it only once.
>
> The amount of function and particularly extern variables in
> monitor_int.h is probably a bit larger than it needs to be, but this way
> no non-trivial code modifications are needed. The interfaces between HMP
> and the monitor core can be cleaned up later.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(The target_long change was well hidden!)
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> include/monitor/monitor.h | 1 +
> monitor/monitor_int.h | 31 +
> monitor/hmp.c | 1351 +++++++++++++++++++++++++++++++++++++
> monitor/misc.c | 1338 +-----------------------------------
> monitor/Makefile.objs | 2 +-
> 5 files changed, 1390 insertions(+), 1333 deletions(-)
> create mode 100644 monitor/hmp.c
>
> diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
> index 7bbab05320..8547529e49 100644
> --- a/include/monitor/monitor.h
> +++ b/include/monitor/monitor.h
> @@ -22,6 +22,7 @@ bool monitor_cur_is_qmp(void);
> void monitor_init_globals(void);
> void monitor_init(Chardev *chr, int flags);
> void monitor_init_qmp(Chardev *chr, int flags);
> +void monitor_init_hmp(Chardev *chr, int flags);
> void monitor_cleanup(void);
>
> int monitor_suspend(Monitor *mon);
> diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
> index 487618392f..8c5d95f942 100644
> --- a/monitor/monitor_int.h
> +++ b/monitor/monitor_int.h
> @@ -27,6 +27,7 @@
>
> #include "qemu-common.h"
> #include "monitor/monitor.h"
> +#include "qemu/cutils.h"
>
> #include "qapi/qmp/qdict.h"
> #include "qapi/qmp/json-parser.h"
> @@ -153,6 +154,29 @@ static inline bool monitor_is_qmp(const Monitor *mon)
> return (mon->flags & MONITOR_USE_CONTROL);
> }
>
> +/**
> + * Is @name in the '|' separated list of names @list?
> + */
> +static inline int compare_cmd(const char *name, const char *list)
> +{
> + const char *p, *pstart;
> + int len;
> + len = strlen(name);
> + p = list;
> + for(;;) {
> + pstart = p;
> + p = qemu_strchrnul(p, '|');
> + if ((p - pstart) == len && !memcmp(pstart, name, len)) {
> + return 1;
> + }
> + if (*p == '\0') {
> + break;
> + }
> + p++;
> + }
> + return 0;
> +}
> +
> typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
> extern IOThread *mon_iothread;
> extern QEMUBH *qmp_dispatcher_bh;
> @@ -161,6 +185,8 @@ extern QemuMutex monitor_lock;
> extern MonitorList mon_list;
> extern int mon_refcount;
>
> +extern mon_cmd_t mon_cmds[];
> +
> int monitor_puts(Monitor *mon, const char *str);
> void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> bool use_io_thread);
> @@ -172,4 +198,9 @@ void qmp_send_response(MonitorQMP *mon, const QDict *rsp);
> void monitor_data_destroy_qmp(MonitorQMP *mon);
> void monitor_qmp_bh_dispatcher(void *data);
>
> +void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush);
> +int get_monitor_def(int64_t *pval, const char *name);
> +void help_cmd(Monitor *mon, const char *name);
> +void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
> +
> #endif
> diff --git a/monitor/hmp.c b/monitor/hmp.c
> new file mode 100644
> index 0000000000..2bc464a7fc
> --- /dev/null
> +++ b/monitor/hmp.c
> @@ -0,0 +1,1351 @@
> +/*
> + * QEMU monitor
> + *
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "monitor_int.h"
> +
> +#include "qapi/error.h"
> +#include "qapi/qmp/qnum.h"
> +
> +#include "qemu/config-file.h"
> +#include "qemu/log.h"
> +#include "qemu/option.h"
> +#include "qemu/units.h"
> +#include "sysemu/block-backend.h"
> +#include "sysemu/sysemu.h"
> +
> +#include "trace-root.h"
> +
> +static int get_str(char *buf, int buf_size, const char **pp)
> +{
> + const char *p;
> + char *q;
> + int c;
> +
> + q = buf;
> + p = *pp;
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + if (*p == '\0') {
> + fail:
> + *q = '\0';
> + *pp = p;
> + return -1;
> + }
> + if (*p == '\"') {
> + p++;
> + while (*p != '\0' && *p != '\"') {
> + if (*p == '\\') {
> + p++;
> + c = *p++;
> + switch (c) {
> + case 'n':
> + c = '\n';
> + break;
> + case 'r':
> + c = '\r';
> + break;
> + case '\\':
> + case '\'':
> + case '\"':
> + break;
> + default:
> + printf("unsupported escape code: '\\%c'\n", c);
> + goto fail;
> + }
> + if ((q - buf) < buf_size - 1) {
> + *q++ = c;
> + }
> + } else {
> + if ((q - buf) < buf_size - 1) {
> + *q++ = *p;
> + }
> + p++;
> + }
> + }
> + if (*p != '\"') {
> + printf("unterminated string\n");
> + goto fail;
> + }
> + p++;
> + } else {
> + while (*p != '\0' && !qemu_isspace(*p)) {
> + if ((q - buf) < buf_size - 1) {
> + *q++ = *p;
> + }
> + p++;
> + }
> + }
> + *q = '\0';
> + *pp = p;
> + return 0;
> +}
> +
> +#define MAX_ARGS 16
> +
> +static void free_cmdline_args(char **args, int nb_args)
> +{
> + int i;
> +
> + assert(nb_args <= MAX_ARGS);
> +
> + for (i = 0; i < nb_args; i++) {
> + g_free(args[i]);
> + }
> +
> +}
> +
> +/*
> + * Parse the command line to get valid args.
> + * @cmdline: command line to be parsed.
> + * @pnb_args: location to store the number of args, must NOT be NULL.
> + * @args: location to store the args, which should be freed by caller, must
> + * NOT be NULL.
> + *
> + * Returns 0 on success, negative on failure.
> + *
> + * NOTE: this parser is an approximate form of the real command parser. Number
> + * of args have a limit of MAX_ARGS. If cmdline contains more, it will
> + * return with failure.
> + */
> +static int parse_cmdline(const char *cmdline,
> + int *pnb_args, char **args)
> +{
> + const char *p;
> + int nb_args, ret;
> + char buf[1024];
> +
> + p = cmdline;
> + nb_args = 0;
> + for (;;) {
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + if (*p == '\0') {
> + break;
> + }
> + if (nb_args >= MAX_ARGS) {
> + goto fail;
> + }
> + ret = get_str(buf, sizeof(buf), &p);
> + if (ret < 0) {
> + goto fail;
> + }
> + args[nb_args] = g_strdup(buf);
> + nb_args++;
> + }
> + *pnb_args = nb_args;
> + return 0;
> +
> + fail:
> + free_cmdline_args(args, nb_args);
> + return -1;
> +}
> +
> +/*
> + * Can command @cmd be executed in preconfig state?
> + */
> +static bool cmd_can_preconfig(const mon_cmd_t *cmd)
> +{
> + if (!cmd->flags) {
> + return false;
> + }
> +
> + return strchr(cmd->flags, 'p');
> +}
> +
> +static void help_cmd_dump_one(Monitor *mon,
> + const mon_cmd_t *cmd,
> + char **prefix_args,
> + int prefix_args_nb)
> +{
> + int i;
> +
> + if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
> + return;
> + }
> +
> + for (i = 0; i < prefix_args_nb; i++) {
> + monitor_printf(mon, "%s ", prefix_args[i]);
> + }
> + monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
> +}
> +
> +/* @args[@arg_index] is the valid command need to find in @cmds */
> +static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
> + char **args, int nb_args, int arg_index)
> +{
> + const mon_cmd_t *cmd;
> + size_t i;
> +
> + /* No valid arg need to compare with, dump all in *cmds */
> + if (arg_index >= nb_args) {
> + for (cmd = cmds; cmd->name != NULL; cmd++) {
> + help_cmd_dump_one(mon, cmd, args, arg_index);
> + }
> + return;
> + }
> +
> + /* Find one entry to dump */
> + for (cmd = cmds; cmd->name != NULL; cmd++) {
> + if (compare_cmd(args[arg_index], cmd->name) &&
> + ((!runstate_check(RUN_STATE_PRECONFIG) ||
> + cmd_can_preconfig(cmd)))) {
> + if (cmd->sub_table) {
> + /* continue with next arg */
> + help_cmd_dump(mon, cmd->sub_table,
> + args, nb_args, arg_index + 1);
> + } else {
> + help_cmd_dump_one(mon, cmd, args, arg_index);
> + }
> + return;
> + }
> + }
> +
> + /* Command not found */
> + monitor_printf(mon, "unknown command: '");
> + for (i = 0; i <= arg_index; i++) {
> + monitor_printf(mon, "%s%s", args[i], i == arg_index ? "'\n" : " ");
> + }
> +}
> +
> +void help_cmd(Monitor *mon, const char *name)
> +{
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> + char *args[MAX_ARGS];
> + int nb_args = 0;
> +
> + /* 1. parse user input */
> + if (name) {
> + /* special case for log, directly dump and return */
> + if (!strcmp(name, "log")) {
> + const QEMULogItem *item;
> + monitor_printf(mon, "Log items (comma separated):\n");
> + monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
> + for (item = qemu_log_items; item->mask != 0; item++) {
> + monitor_printf(mon, "%-10s %s\n", item->name, item->help);
> + }
> + return;
> + }
> +
> + if (parse_cmdline(name, &nb_args, args) < 0) {
> + return;
> + }
> + }
> +
> + /* 2. dump the contents according to parsed args */
> + help_cmd_dump(mon, hmp_mon->cmd_table, args, nb_args, 0);
> +
> + free_cmdline_args(args, nb_args);
> +}
> +
> +/*******************************************************************/
> +
> +static const char *pch;
> +static sigjmp_buf expr_env;
> +
> +static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
> +expr_error(Monitor *mon, const char *fmt, ...)
> +{
> + va_list ap;
> + va_start(ap, fmt);
> + monitor_vprintf(mon, fmt, ap);
> + monitor_printf(mon, "\n");
> + va_end(ap);
> + siglongjmp(expr_env, 1);
> +}
> +
> +static void next(void)
> +{
> + if (*pch != '\0') {
> + pch++;
> + while (qemu_isspace(*pch))
> + pch++;
> + }
> +}
> +
> +static int64_t expr_sum(Monitor *mon);
> +
> +static int64_t expr_unary(Monitor *mon)
> +{
> + int64_t n;
> + char *p;
> + int ret;
> +
> + switch(*pch) {
> + case '+':
> + next();
> + n = expr_unary(mon);
> + break;
> + case '-':
> + next();
> + n = -expr_unary(mon);
> + break;
> + case '~':
> + next();
> + n = ~expr_unary(mon);
> + break;
> + case '(':
> + next();
> + n = expr_sum(mon);
> + if (*pch != ')') {
> + expr_error(mon, "')' expected");
> + }
> + next();
> + break;
> + case '\'':
> + pch++;
> + if (*pch == '\0')
> + expr_error(mon, "character constant expected");
> + n = *pch;
> + pch++;
> + if (*pch != '\'')
> + expr_error(mon, "missing terminating \' character");
> + next();
> + break;
> + case '$':
> + {
> + char buf[128], *q;
> + int64_t reg = 0;
> +
> + pch++;
> + q = buf;
> + while ((*pch >= 'a' && *pch <= 'z') ||
> + (*pch >= 'A' && *pch <= 'Z') ||
> + (*pch >= '0' && *pch <= '9') ||
> + *pch == '_' || *pch == '.') {
> + if ((q - buf) < sizeof(buf) - 1)
> + *q++ = *pch;
> + pch++;
> + }
> + while (qemu_isspace(*pch))
> + pch++;
> + *q = 0;
> + ret = get_monitor_def(®, buf);
> + if (ret < 0)
> + expr_error(mon, "unknown register");
> + n = reg;
> + }
> + break;
> + case '\0':
> + expr_error(mon, "unexpected end of expression");
> + n = 0;
> + break;
> + default:
> + errno = 0;
> + n = strtoull(pch, &p, 0);
> + if (errno == ERANGE) {
> + expr_error(mon, "number too large");
> + }
> + if (pch == p) {
> + expr_error(mon, "invalid char '%c' in expression", *p);
> + }
> + pch = p;
> + while (qemu_isspace(*pch))
> + pch++;
> + break;
> + }
> + return n;
> +}
> +
> +static int64_t expr_prod(Monitor *mon)
> +{
> + int64_t val, val2;
> + int op;
> +
> + val = expr_unary(mon);
> + for(;;) {
> + op = *pch;
> + if (op != '*' && op != '/' && op != '%')
> + break;
> + next();
> + val2 = expr_unary(mon);
> + switch(op) {
> + default:
> + case '*':
> + val *= val2;
> + break;
> + case '/':
> + case '%':
> + if (val2 == 0)
> + expr_error(mon, "division by zero");
> + if (op == '/')
> + val /= val2;
> + else
> + val %= val2;
> + break;
> + }
> + }
> + return val;
> +}
> +
> +static int64_t expr_logic(Monitor *mon)
> +{
> + int64_t val, val2;
> + int op;
> +
> + val = expr_prod(mon);
> + for(;;) {
> + op = *pch;
> + if (op != '&' && op != '|' && op != '^')
> + break;
> + next();
> + val2 = expr_prod(mon);
> + switch(op) {
> + default:
> + case '&':
> + val &= val2;
> + break;
> + case '|':
> + val |= val2;
> + break;
> + case '^':
> + val ^= val2;
> + break;
> + }
> + }
> + return val;
> +}
> +
> +static int64_t expr_sum(Monitor *mon)
> +{
> + int64_t val, val2;
> + int op;
> +
> + val = expr_logic(mon);
> + for(;;) {
> + op = *pch;
> + if (op != '+' && op != '-')
> + break;
> + next();
> + val2 = expr_logic(mon);
> + if (op == '+')
> + val += val2;
> + else
> + val -= val2;
> + }
> + return val;
> +}
> +
> +static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
> +{
> + pch = *pp;
> + if (sigsetjmp(expr_env, 0)) {
> + *pp = pch;
> + return -1;
> + }
> + while (qemu_isspace(*pch))
> + pch++;
> + *pval = expr_sum(mon);
> + *pp = pch;
> + return 0;
> +}
> +
> +static int get_double(Monitor *mon, double *pval, const char **pp)
> +{
> + const char *p = *pp;
> + char *tailp;
> + double d;
> +
> + d = strtod(p, &tailp);
> + if (tailp == p) {
> + monitor_printf(mon, "Number expected\n");
> + return -1;
> + }
> + if (d != d || d - d != 0) {
> + /* NaN or infinity */
> + monitor_printf(mon, "Bad number\n");
> + return -1;
> + }
> + *pval = d;
> + *pp = tailp;
> + return 0;
> +}
> +
> +/*
> + * Store the command-name in cmdname, and return a pointer to
> + * the remaining of the command string.
> + */
> +static const char *get_command_name(const char *cmdline,
> + char *cmdname, size_t nlen)
> +{
> + size_t len;
> + const char *p, *pstart;
> +
> + p = cmdline;
> + while (qemu_isspace(*p))
> + p++;
> + if (*p == '\0')
> + return NULL;
> + pstart = p;
> + while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
> + p++;
> + len = p - pstart;
> + if (len > nlen - 1)
> + len = nlen - 1;
> + memcpy(cmdname, pstart, len);
> + cmdname[len] = '\0';
> + return p;
> +}
> +
> +/**
> + * Read key of 'type' into 'key' and return the current
> + * 'type' pointer.
> + */
> +static char *key_get_info(const char *type, char **key)
> +{
> + size_t len;
> + char *p, *str;
> +
> + if (*type == ',')
> + type++;
> +
> + p = strchr(type, ':');
> + if (!p) {
> + *key = NULL;
> + return NULL;
> + }
> + len = p - type;
> +
> + str = g_malloc(len + 1);
> + memcpy(str, type, len);
> + str[len] = '\0';
> +
> + *key = str;
> + return ++p;
> +}
> +
> +static int default_fmt_format = 'x';
> +static int default_fmt_size = 4;
> +
> +static int is_valid_option(const char *c, const char *typestr)
> +{
> + char option[3];
> +
> + option[0] = '-';
> + option[1] = *c;
> + option[2] = '\0';
> +
> + typestr = strstr(typestr, option);
> + return (typestr != NULL);
> +}
> +
> +static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
> + const char *cmdname)
> +{
> + const mon_cmd_t *cmd;
> +
> + for (cmd = disp_table; cmd->name != NULL; cmd++) {
> + if (compare_cmd(cmdname, cmd->name)) {
> + return cmd;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +/*
> + * Parse command name from @cmdp according to command table @table.
> + * If blank, return NULL.
> + * Else, if no valid command can be found, report to @mon, and return
> + * NULL.
> + * Else, change @cmdp to point right behind the name, and return its
> + * command table entry.
> + * Do not assume the return value points into @table! It doesn't when
> + * the command is found in a sub-command table.
> + */
> +static const mon_cmd_t *monitor_parse_command(MonitorHMP *hmp_mon,
> + const char *cmdp_start,
> + const char **cmdp,
> + mon_cmd_t *table)
> +{
> + Monitor *mon = &hmp_mon->common;
> + const char *p;
> + const mon_cmd_t *cmd;
> + char cmdname[256];
> +
> + /* extract the command name */
> + p = get_command_name(*cmdp, cmdname, sizeof(cmdname));
> + if (!p) {
> + return NULL;
> + }
> +
> + cmd = search_dispatch_table(table, cmdname);
> + if (!cmd) {
> + monitor_printf(mon, "unknown command: '%.*s'\n",
> + (int)(p - cmdp_start), cmdp_start);
> + return NULL;
> + }
> + if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
> + monitor_printf(mon, "Command '%.*s' not available with -preconfig "
> + "until after exit_preconfig.\n",
> + (int)(p - cmdp_start), cmdp_start);
> + return NULL;
> + }
> +
> + /* filter out following useless space */
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> +
> + *cmdp = p;
> + /* search sub command */
> + if (cmd->sub_table != NULL && *p != '\0') {
> + return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table);
> + }
> +
> + return cmd;
> +}
> +
> +/*
> + * Parse arguments for @cmd.
> + * If it can't be parsed, report to @mon, and return NULL.
> + * Else, insert command arguments into a QDict, and return it.
> + * Note: On success, caller has to free the QDict structure.
> + */
> +static QDict *monitor_parse_arguments(Monitor *mon,
> + const char **endp,
> + const mon_cmd_t *cmd)
> +{
> + const char *typestr;
> + char *key;
> + int c;
> + const char *p = *endp;
> + char buf[1024];
> + QDict *qdict = qdict_new();
> +
> + /* parse the parameters */
> + typestr = cmd->args_type;
> + for(;;) {
> + typestr = key_get_info(typestr, &key);
> + if (!typestr) {
> + break;
> + }
> + c = *typestr;
> + typestr++;
> + switch(c) {
> + case 'F':
> + case 'B':
> + case 's':
> + {
> + int ret;
> +
> + while (qemu_isspace(*p))
> + p++;
> + if (*typestr == '?') {
> + typestr++;
> + if (*p == '\0') {
> + /* no optional string: NULL argument */
> + break;
> + }
> + }
> + ret = get_str(buf, sizeof(buf), &p);
> + if (ret < 0) {
> + switch(c) {
> + case 'F':
> + monitor_printf(mon, "%s: filename expected\n",
> + cmd->name);
> + break;
> + case 'B':
> + monitor_printf(mon, "%s: block device name expected\n",
> + cmd->name);
> + break;
> + default:
> + monitor_printf(mon, "%s: string expected\n", cmd->name);
> + break;
> + }
> + goto fail;
> + }
> + qdict_put_str(qdict, key, buf);
> + }
> + break;
> + case 'O':
> + {
> + QemuOptsList *opts_list;
> + QemuOpts *opts;
> +
> + opts_list = qemu_find_opts(key);
> + if (!opts_list || opts_list->desc->name) {
> + goto bad_type;
> + }
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + if (!*p) {
> + break;
> + }
> + if (get_str(buf, sizeof(buf), &p) < 0) {
> + goto fail;
> + }
> + opts = qemu_opts_parse_noisily(opts_list, buf, true);
> + if (!opts) {
> + goto fail;
> + }
> + qemu_opts_to_qdict(opts, qdict);
> + qemu_opts_del(opts);
> + }
> + break;
> + case '/':
> + {
> + int count, format, size;
> +
> + while (qemu_isspace(*p))
> + p++;
> + if (*p == '/') {
> + /* format found */
> + p++;
> + count = 1;
> + if (qemu_isdigit(*p)) {
> + count = 0;
> + while (qemu_isdigit(*p)) {
> + count = count * 10 + (*p - '0');
> + p++;
> + }
> + }
> + size = -1;
> + format = -1;
> + for(;;) {
> + switch(*p) {
> + case 'o':
> + case 'd':
> + case 'u':
> + case 'x':
> + case 'i':
> + case 'c':
> + format = *p++;
> + break;
> + case 'b':
> + size = 1;
> + p++;
> + break;
> + case 'h':
> + size = 2;
> + p++;
> + break;
> + case 'w':
> + size = 4;
> + p++;
> + break;
> + case 'g':
> + case 'L':
> + size = 8;
> + p++;
> + break;
> + default:
> + goto next;
> + }
> + }
> + next:
> + if (*p != '\0' && !qemu_isspace(*p)) {
> + monitor_printf(mon, "invalid char in format: '%c'\n",
> + *p);
> + goto fail;
> + }
> + if (format < 0) {
> + format = default_fmt_format;
> + }
> + if (format != 'i') {
> + /* for 'i', not specifying a size gives -1 as size */
> + if (size < 0) {
> + size = default_fmt_size;
> + }
> + default_fmt_size = size;
> + }
> + default_fmt_format = format;
> + } else {
> + count = 1;
> + format = default_fmt_format;
> + if (format != 'i') {
> + size = default_fmt_size;
> + } else {
> + size = -1;
> + }
> + }
> + qdict_put_int(qdict, "count", count);
> + qdict_put_int(qdict, "format", format);
> + qdict_put_int(qdict, "size", size);
> + }
> + break;
> + case 'i':
> + case 'l':
> + case 'M':
> + {
> + int64_t val;
> +
> + while (qemu_isspace(*p))
> + p++;
> + if (*typestr == '?' || *typestr == '.') {
> + if (*typestr == '?') {
> + if (*p == '\0') {
> + typestr++;
> + break;
> + }
> + } else {
> + if (*p == '.') {
> + p++;
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + } else {
> + typestr++;
> + break;
> + }
> + }
> + typestr++;
> + }
> + if (get_expr(mon, &val, &p))
> + goto fail;
> + /* Check if 'i' is greater than 32-bit */
> + if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
> + monitor_printf(mon, "\'%s\' has failed: ", cmd->name);
> + monitor_printf(mon, "integer is for 32-bit values\n");
> + goto fail;
> + } else if (c == 'M') {
> + if (val < 0) {
> + monitor_printf(mon, "enter a positive value\n");
> + goto fail;
> + }
> + val *= MiB;
> + }
> + qdict_put_int(qdict, key, val);
> + }
> + break;
> + case 'o':
> + {
> + int ret;
> + uint64_t val;
> + const char *end;
> +
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + if (*typestr == '?') {
> + typestr++;
> + if (*p == '\0') {
> + break;
> + }
> + }
> + ret = qemu_strtosz_MiB(p, &end, &val);
> + if (ret < 0 || val > INT64_MAX) {
> + monitor_printf(mon, "invalid size\n");
> + goto fail;
> + }
> + qdict_put_int(qdict, key, val);
> + p = end;
> + }
> + break;
> + case 'T':
> + {
> + double val;
> +
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + if (*typestr == '?') {
> + typestr++;
> + if (*p == '\0') {
> + break;
> + }
> + }
> + if (get_double(mon, &val, &p) < 0) {
> + goto fail;
> + }
> + if (p[0] && p[1] == 's') {
> + switch (*p) {
> + case 'm':
> + val /= 1e3; p += 2; break;
> + case 'u':
> + val /= 1e6; p += 2; break;
> + case 'n':
> + val /= 1e9; p += 2; break;
> + }
> + }
> + if (*p && !qemu_isspace(*p)) {
> + monitor_printf(mon, "Unknown unit suffix\n");
> + goto fail;
> + }
> + qdict_put(qdict, key, qnum_from_double(val));
> + }
> + break;
> + case 'b':
> + {
> + const char *beg;
> + bool val;
> +
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + beg = p;
> + while (qemu_isgraph(*p)) {
> + p++;
> + }
> + if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
> + val = true;
> + } else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
> + val = false;
> + } else {
> + monitor_printf(mon, "Expected 'on' or 'off'\n");
> + goto fail;
> + }
> + qdict_put_bool(qdict, key, val);
> + }
> + break;
> + case '-':
> + {
> + const char *tmp = p;
> + int skip_key = 0;
> + /* option */
> +
> + c = *typestr++;
> + if (c == '\0')
> + goto bad_type;
> + while (qemu_isspace(*p))
> + p++;
> + if (*p == '-') {
> + p++;
> + if(c != *p) {
> + if(!is_valid_option(p, typestr)) {
> + monitor_printf(mon, "%s: unsupported option -%c\n",
> + cmd->name, *p);
> + goto fail;
> + } else {
> + skip_key = 1;
> + }
> + }
> + if(skip_key) {
> + p = tmp;
> + } else {
> + /* has option */
> + p++;
> + qdict_put_bool(qdict, key, true);
> + }
> + }
> + }
> + break;
> + case 'S':
> + {
> + /* package all remaining string */
> + int len;
> +
> + while (qemu_isspace(*p)) {
> + p++;
> + }
> + if (*typestr == '?') {
> + typestr++;
> + if (*p == '\0') {
> + /* no remaining string: NULL argument */
> + break;
> + }
> + }
> + len = strlen(p);
> + if (len <= 0) {
> + monitor_printf(mon, "%s: string expected\n",
> + cmd->name);
> + goto fail;
> + }
> + qdict_put_str(qdict, key, p);
> + p += len;
> + }
> + break;
> + default:
> + bad_type:
> + monitor_printf(mon, "%s: unknown type '%c'\n", cmd->name, c);
> + goto fail;
> + }
> + g_free(key);
> + key = NULL;
> + }
> + /* check that all arguments were parsed */
> + while (qemu_isspace(*p))
> + p++;
> + if (*p != '\0') {
> + monitor_printf(mon, "%s: extraneous characters at the end of line\n",
> + cmd->name);
> + goto fail;
> + }
> +
> + return qdict;
> +
> +fail:
> + qobject_unref(qdict);
> + g_free(key);
> + return NULL;
> +}
> +
> +void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
> +{
> + QDict *qdict;
> + const mon_cmd_t *cmd;
> + const char *cmd_start = cmdline;
> +
> + trace_handle_hmp_command(mon, cmdline);
> +
> + cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
> + if (!cmd) {
> + return;
> + }
> +
> + qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd);
> + if (!qdict) {
> + while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) {
> + cmdline--;
> + }
> + monitor_printf(&mon->common, "Try \"help %.*s\" for more information\n",
> + (int)(cmdline - cmd_start), cmd_start);
> + return;
> + }
> +
> + cmd->cmd(&mon->common, qdict);
> + qobject_unref(qdict);
> +}
> +
> +static void cmd_completion(MonitorHMP *mon, const char *name, const char *list)
> +{
> + const char *p, *pstart;
> + char cmd[128];
> + int len;
> +
> + p = list;
> + for(;;) {
> + pstart = p;
> + p = qemu_strchrnul(p, '|');
> + len = p - pstart;
> + if (len > sizeof(cmd) - 2)
> + len = sizeof(cmd) - 2;
> + memcpy(cmd, pstart, len);
> + cmd[len] = '\0';
> + if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
> + readline_add_completion(mon->rs, cmd);
> + }
> + if (*p == '\0')
> + break;
> + p++;
> + }
> +}
> +
> +static void file_completion(MonitorHMP *mon, const char *input)
> +{
> + DIR *ffs;
> + struct dirent *d;
> + char path[1024];
> + char file[1024], file_prefix[1024];
> + int input_path_len;
> + const char *p;
> +
> + p = strrchr(input, '/');
> + if (!p) {
> + input_path_len = 0;
> + pstrcpy(file_prefix, sizeof(file_prefix), input);
> + pstrcpy(path, sizeof(path), ".");
> + } else {
> + input_path_len = p - input + 1;
> + memcpy(path, input, input_path_len);
> + if (input_path_len > sizeof(path) - 1)
> + input_path_len = sizeof(path) - 1;
> + path[input_path_len] = '\0';
> + pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
> + }
> +
> + ffs = opendir(path);
> + if (!ffs)
> + return;
> + for(;;) {
> + struct stat sb;
> + d = readdir(ffs);
> + if (!d)
> + break;
> +
> + if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
> + continue;
> + }
> +
> + if (strstart(d->d_name, file_prefix, NULL)) {
> + memcpy(file, input, input_path_len);
> + if (input_path_len < sizeof(file))
> + pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
> + d->d_name);
> + /* stat the file to find out if it's a directory.
> + * In that case add a slash to speed up typing long paths
> + */
> + if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
> + pstrcat(file, sizeof(file), "/");
> + }
> + readline_add_completion(mon->rs, file);
> + }
> + }
> + closedir(ffs);
> +}
> +
> +static const char *next_arg_type(const char *typestr)
> +{
> + const char *p = strchr(typestr, ':');
> + return (p != NULL ? ++p : typestr);
> +}
> +
> +static void monitor_find_completion_by_table(MonitorHMP *mon,
> + const mon_cmd_t *cmd_table,
> + char **args,
> + int nb_args)
> +{
> + const char *cmdname;
> + int i;
> + const char *ptype, *old_ptype, *str, *name;
> + const mon_cmd_t *cmd;
> + BlockBackend *blk = NULL;
> +
> + if (nb_args <= 1) {
> + /* command completion */
> + if (nb_args == 0)
> + cmdname = "";
> + else
> + cmdname = args[0];
> + readline_set_completion_index(mon->rs, strlen(cmdname));
> + for (cmd = cmd_table; cmd->name != NULL; cmd++) {
> + if (!runstate_check(RUN_STATE_PRECONFIG) ||
> + cmd_can_preconfig(cmd)) {
> + cmd_completion(mon, cmdname, cmd->name);
> + }
> + }
> + } else {
> + /* find the command */
> + for (cmd = cmd_table; cmd->name != NULL; cmd++) {
> + if (compare_cmd(args[0], cmd->name) &&
> + (!runstate_check(RUN_STATE_PRECONFIG) ||
> + cmd_can_preconfig(cmd))) {
> + break;
> + }
> + }
> + if (!cmd->name) {
> + return;
> + }
> +
> + if (cmd->sub_table) {
> + /* do the job again */
> + monitor_find_completion_by_table(mon, cmd->sub_table,
> + &args[1], nb_args - 1);
> + return;
> + }
> + if (cmd->command_completion) {
> + cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]);
> + return;
> + }
> +
> + ptype = next_arg_type(cmd->args_type);
> + for(i = 0; i < nb_args - 2; i++) {
> + if (*ptype != '\0') {
> + ptype = next_arg_type(ptype);
> + while (*ptype == '?')
> + ptype = next_arg_type(ptype);
> + }
> + }
> + str = args[nb_args - 1];
> + old_ptype = NULL;
> + while (*ptype == '-' && old_ptype != ptype) {
> + old_ptype = ptype;
> + ptype = next_arg_type(ptype);
> + }
> + switch(*ptype) {
> + case 'F':
> + /* file completion */
> + readline_set_completion_index(mon->rs, strlen(str));
> + file_completion(mon, str);
> + break;
> + case 'B':
> + /* block device name completion */
> + readline_set_completion_index(mon->rs, strlen(str));
> + while ((blk = blk_next(blk)) != NULL) {
> + name = blk_name(blk);
> + if (str[0] == '\0' ||
> + !strncmp(name, str, strlen(str))) {
> + readline_add_completion(mon->rs, name);
> + }
> + }
> + break;
> + case 's':
> + case 'S':
> + if (!strcmp(cmd->name, "help|?")) {
> + monitor_find_completion_by_table(mon, cmd_table,
> + &args[1], nb_args - 1);
> + }
> + break;
> + default:
> + break;
> + }
> + }
> +}
> +
> +static void monitor_find_completion(void *opaque,
> + const char *cmdline)
> +{
> + MonitorHMP *mon = opaque;
> + char *args[MAX_ARGS];
> + int nb_args, len;
> +
> + /* 1. parse the cmdline */
> + if (parse_cmdline(cmdline, &nb_args, args) < 0) {
> + return;
> + }
> +
> + /* if the line ends with a space, it means we want to complete the
> + next arg */
> + len = strlen(cmdline);
> + if (len > 0 && qemu_isspace(cmdline[len - 1])) {
> + if (nb_args >= MAX_ARGS) {
> + goto cleanup;
> + }
> + args[nb_args++] = g_strdup("");
> + }
> +
> + /* 2. auto complete according to args */
> + monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
> +
> +cleanup:
> + free_cmdline_args(args, nb_args);
> +}
> +
> +static void monitor_read(void *opaque, const uint8_t *buf, int size)
> +{
> + MonitorHMP *mon;
> + Monitor *old_mon = cur_mon;
> + 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");
> + } else {
> + handle_hmp_command(mon, (char *)buf);
> + }
> + }
> +
> + cur_mon = old_mon;
> +}
> +
> +static void monitor_event(void *opaque, int event)
> +{
> + Monitor *mon = opaque;
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> +
> + switch (event) {
> + case CHR_EVENT_MUX_IN:
> + qemu_mutex_lock(&mon->mon_lock);
> + mon->mux_out = 0;
> + qemu_mutex_unlock(&mon->mon_lock);
> + if (mon->reset_seen) {
> + readline_restart(hmp_mon->rs);
> + monitor_resume(mon);
> + monitor_flush(mon);
> + } else {
> + atomic_mb_set(&mon->suspend_cnt, 0);
> + }
> + break;
> +
> + case CHR_EVENT_MUX_OUT:
> + if (mon->reset_seen) {
> + if (atomic_mb_read(&mon->suspend_cnt) == 0) {
> + monitor_printf(mon, "\n");
> + }
> + monitor_flush(mon);
> + monitor_suspend(mon);
> + } else {
> + atomic_inc(&mon->suspend_cnt);
> + }
> + qemu_mutex_lock(&mon->mon_lock);
> + mon->mux_out = 1;
> + qemu_mutex_unlock(&mon->mon_lock);
> + break;
> +
> + case CHR_EVENT_OPENED:
> + monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
> + "information\n", QEMU_VERSION);
> + if (!mon->mux_out) {
> + readline_restart(hmp_mon->rs);
> + readline_show_prompt(hmp_mon->rs);
> + }
> + mon->reset_seen = 1;
> + mon_refcount++;
> + break;
> +
> + case CHR_EVENT_CLOSED:
> + mon_refcount--;
> + monitor_fdsets_cleanup();
> + break;
> + }
> +}
> +
> +
> +/* These functions just adapt the readline interface in a typesafe way. We
> + * could cast function pointers but that discards compiler checks.
> + */
> +static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
> + const char *fmt, ...)
> +{
> + MonitorHMP *mon = opaque;
> + va_list ap;
> + va_start(ap, fmt);
> + monitor_vprintf(&mon->common, fmt, ap);
> + va_end(ap);
> +}
> +
> +static void monitor_readline_flush(void *opaque)
> +{
> + MonitorHMP *mon = opaque;
> + monitor_flush(&mon->common);
> +}
> +
> +void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
> +{
> + monitor_data_init(&mon->common, flags, skip_flush, false);
> +
> + /* Use *mon_cmds by default. */
> + mon->cmd_table = mon_cmds;
> +}
> +
> +void monitor_init_hmp(Chardev *chr, int flags)
> +{
> + MonitorHMP *mon = g_malloc0(sizeof(*mon));
> + bool use_readline = flags & MONITOR_USE_READLINE;
> +
> + monitor_data_init_hmp(mon, flags, false);
> + qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
> +
> + if (use_readline) {
> + mon->rs = readline_init(monitor_readline_printf,
> + monitor_readline_flush,
> + mon,
> + monitor_find_completion);
> + monitor_read_command(mon, 0);
> + }
> +
> + qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read, monitor_read,
> + monitor_event, NULL, &mon->common, NULL, true);
> + monitor_list_append(&mon->common);
> +}
> diff --git a/monitor/misc.c b/monitor/misc.c
> index 6c67f0978c..408d11e1fe 100644
> --- a/monitor/misc.c
> +++ b/monitor/misc.c
> @@ -24,7 +24,6 @@
>
> #include "qemu/osdep.h"
> #include "monitor_int.h"
> -#include "qemu/units.h"
> #include <dirent.h>
> #include "cpu.h"
> #include "hw/hw.h"
> @@ -155,7 +154,6 @@ static QLIST_HEAD(, MonFdset) mon_fdsets;
>
> int mon_refcount;
>
> -static mon_cmd_t mon_cmds[];
> static mon_cmd_t info_cmds[];
>
> __thread Monitor *cur_mon;
> @@ -540,8 +538,6 @@ static void monitor_qapi_event_init(void)
> qapi_event_throttle_equal);
> }
>
> -static void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
> -
> static void monitor_iothread_init(void);
>
> void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> @@ -558,14 +554,6 @@ void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> mon->flags = flags;
> }
>
> -static void monitor_data_init_hmp(MonitorHMP *mon, int flags, bool skip_flush)
> -{
> - monitor_data_init(&mon->common, flags, skip_flush, false);
> -
> - /* Use *mon_cmds by default. */
> - mon->cmd_table = mon_cmds;
> -}
> -
> static void monitor_data_destroy(Monitor *mon)
> {
> g_free(mon->mon_cpu_path);
> @@ -619,248 +607,6 @@ out:
> return output;
> }
>
> -static int compare_cmd(const char *name, const char *list)
> -{
> - const char *p, *pstart;
> - int len;
> - len = strlen(name);
> - p = list;
> - for(;;) {
> - pstart = p;
> - p = qemu_strchrnul(p, '|');
> - if ((p - pstart) == len && !memcmp(pstart, name, len))
> - return 1;
> - if (*p == '\0')
> - break;
> - p++;
> - }
> - return 0;
> -}
> -
> -static int get_str(char *buf, int buf_size, const char **pp)
> -{
> - const char *p;
> - char *q;
> - int c;
> -
> - q = buf;
> - p = *pp;
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> - if (*p == '\0') {
> - fail:
> - *q = '\0';
> - *pp = p;
> - return -1;
> - }
> - if (*p == '\"') {
> - p++;
> - while (*p != '\0' && *p != '\"') {
> - if (*p == '\\') {
> - p++;
> - c = *p++;
> - switch (c) {
> - case 'n':
> - c = '\n';
> - break;
> - case 'r':
> - c = '\r';
> - break;
> - case '\\':
> - case '\'':
> - case '\"':
> - break;
> - default:
> - printf("unsupported escape code: '\\%c'\n", c);
> - goto fail;
> - }
> - if ((q - buf) < buf_size - 1) {
> - *q++ = c;
> - }
> - } else {
> - if ((q - buf) < buf_size - 1) {
> - *q++ = *p;
> - }
> - p++;
> - }
> - }
> - if (*p != '\"') {
> - printf("unterminated string\n");
> - goto fail;
> - }
> - p++;
> - } else {
> - while (*p != '\0' && !qemu_isspace(*p)) {
> - if ((q - buf) < buf_size - 1) {
> - *q++ = *p;
> - }
> - p++;
> - }
> - }
> - *q = '\0';
> - *pp = p;
> - return 0;
> -}
> -
> -#define MAX_ARGS 16
> -
> -static void free_cmdline_args(char **args, int nb_args)
> -{
> - int i;
> -
> - assert(nb_args <= MAX_ARGS);
> -
> - for (i = 0; i < nb_args; i++) {
> - g_free(args[i]);
> - }
> -
> -}
> -
> -/*
> - * Parse the command line to get valid args.
> - * @cmdline: command line to be parsed.
> - * @pnb_args: location to store the number of args, must NOT be NULL.
> - * @args: location to store the args, which should be freed by caller, must
> - * NOT be NULL.
> - *
> - * Returns 0 on success, negative on failure.
> - *
> - * NOTE: this parser is an approximate form of the real command parser. Number
> - * of args have a limit of MAX_ARGS. If cmdline contains more, it will
> - * return with failure.
> - */
> -static int parse_cmdline(const char *cmdline,
> - int *pnb_args, char **args)
> -{
> - const char *p;
> - int nb_args, ret;
> - char buf[1024];
> -
> - p = cmdline;
> - nb_args = 0;
> - for (;;) {
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> - if (*p == '\0') {
> - break;
> - }
> - if (nb_args >= MAX_ARGS) {
> - goto fail;
> - }
> - ret = get_str(buf, sizeof(buf), &p);
> - if (ret < 0) {
> - goto fail;
> - }
> - args[nb_args] = g_strdup(buf);
> - nb_args++;
> - }
> - *pnb_args = nb_args;
> - return 0;
> -
> - fail:
> - free_cmdline_args(args, nb_args);
> - return -1;
> -}
> -
> -/*
> - * Can command @cmd be executed in preconfig state?
> - */
> -static bool cmd_can_preconfig(const mon_cmd_t *cmd)
> -{
> - if (!cmd->flags) {
> - return false;
> - }
> -
> - return strchr(cmd->flags, 'p');
> -}
> -
> -static void help_cmd_dump_one(Monitor *mon,
> - const mon_cmd_t *cmd,
> - char **prefix_args,
> - int prefix_args_nb)
> -{
> - int i;
> -
> - if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
> - return;
> - }
> -
> - for (i = 0; i < prefix_args_nb; i++) {
> - monitor_printf(mon, "%s ", prefix_args[i]);
> - }
> - monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
> -}
> -
> -/* @args[@arg_index] is the valid command need to find in @cmds */
> -static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
> - char **args, int nb_args, int arg_index)
> -{
> - const mon_cmd_t *cmd;
> - size_t i;
> -
> - /* No valid arg need to compare with, dump all in *cmds */
> - if (arg_index >= nb_args) {
> - for (cmd = cmds; cmd->name != NULL; cmd++) {
> - help_cmd_dump_one(mon, cmd, args, arg_index);
> - }
> - return;
> - }
> -
> - /* Find one entry to dump */
> - for (cmd = cmds; cmd->name != NULL; cmd++) {
> - if (compare_cmd(args[arg_index], cmd->name) &&
> - ((!runstate_check(RUN_STATE_PRECONFIG) ||
> - cmd_can_preconfig(cmd)))) {
> - if (cmd->sub_table) {
> - /* continue with next arg */
> - help_cmd_dump(mon, cmd->sub_table,
> - args, nb_args, arg_index + 1);
> - } else {
> - help_cmd_dump_one(mon, cmd, args, arg_index);
> - }
> - return;
> - }
> - }
> -
> - /* Command not found */
> - monitor_printf(mon, "unknown command: '");
> - for (i = 0; i <= arg_index; i++) {
> - monitor_printf(mon, "%s%s", args[i], i == arg_index ? "'\n" : " ");
> - }
> -}
> -
> -static void help_cmd(Monitor *mon, const char *name)
> -{
> - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> - char *args[MAX_ARGS];
> - int nb_args = 0;
> -
> - /* 1. parse user input */
> - if (name) {
> - /* special case for log, directly dump and return */
> - if (!strcmp(name, "log")) {
> - const QEMULogItem *item;
> - monitor_printf(mon, "Log items (comma separated):\n");
> - monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
> - for (item = qemu_log_items; item->mask != 0; item++) {
> - monitor_printf(mon, "%-10s %s\n", item->name, item->help);
> - }
> - return;
> - }
> -
> - if (parse_cmdline(name, &nb_args, args) < 0) {
> - return;
> - }
> - }
> -
> - /* 2. dump the contents according to parsed args */
> - help_cmd_dump(mon, hmp_mon->cmd_table, args, nb_args, 0);
> -
> - free_cmdline_args(args, nb_args);
> -}
> -
> static void do_help_cmd(Monitor *mon, const QDict *qdict)
> {
> help_cmd(mon, qdict_get_try_str(qdict, "name"));
> @@ -2510,30 +2256,16 @@ static mon_cmd_t info_cmds[] = {
> };
>
> /* mon_cmds and info_cmds would be sorted at runtime */
> -static mon_cmd_t mon_cmds[] = {
> +mon_cmd_t mon_cmds[] = {
> #include "hmp-commands.h"
> { NULL, NULL, },
> };
>
> -/*******************************************************************/
> -
> -static const char *pch;
> -static sigjmp_buf expr_env;
> -
> -
> -static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
> -expr_error(Monitor *mon, const char *fmt, ...)
> -{
> - va_list ap;
> - va_start(ap, fmt);
> - monitor_vprintf(mon, fmt, ap);
> - monitor_printf(mon, "\n");
> - va_end(ap);
> - siglongjmp(expr_env, 1);
> -}
> -
> -/* return 0 if OK, -1 if not found */
> -static int get_monitor_def(target_long *pval, const char *name)
> +/*
> + * Set @pval to the value in the register identified by @name.
> + * return 0 if OK, -1 if not found
> + */
> +int get_monitor_def(int64_t *pval, const char *name)
> {
> const MonitorDef *md = target_monitor_defs();
> CPUState *cs = mon_get_cpu();
> @@ -2576,829 +2308,6 @@ static int get_monitor_def(target_long *pval, const char *name)
> return ret;
> }
>
> -static void next(void)
> -{
> - if (*pch != '\0') {
> - pch++;
> - while (qemu_isspace(*pch))
> - pch++;
> - }
> -}
> -
> -static int64_t expr_sum(Monitor *mon);
> -
> -static int64_t expr_unary(Monitor *mon)
> -{
> - int64_t n;
> - char *p;
> - int ret;
> -
> - switch(*pch) {
> - case '+':
> - next();
> - n = expr_unary(mon);
> - break;
> - case '-':
> - next();
> - n = -expr_unary(mon);
> - break;
> - case '~':
> - next();
> - n = ~expr_unary(mon);
> - break;
> - case '(':
> - next();
> - n = expr_sum(mon);
> - if (*pch != ')') {
> - expr_error(mon, "')' expected");
> - }
> - next();
> - break;
> - case '\'':
> - pch++;
> - if (*pch == '\0')
> - expr_error(mon, "character constant expected");
> - n = *pch;
> - pch++;
> - if (*pch != '\'')
> - expr_error(mon, "missing terminating \' character");
> - next();
> - break;
> - case '$':
> - {
> - char buf[128], *q;
> - target_long reg=0;
> -
> - pch++;
> - q = buf;
> - while ((*pch >= 'a' && *pch <= 'z') ||
> - (*pch >= 'A' && *pch <= 'Z') ||
> - (*pch >= '0' && *pch <= '9') ||
> - *pch == '_' || *pch == '.') {
> - if ((q - buf) < sizeof(buf) - 1)
> - *q++ = *pch;
> - pch++;
> - }
> - while (qemu_isspace(*pch))
> - pch++;
> - *q = 0;
> - ret = get_monitor_def(®, buf);
> - if (ret < 0)
> - expr_error(mon, "unknown register");
> - n = reg;
> - }
> - break;
> - case '\0':
> - expr_error(mon, "unexpected end of expression");
> - n = 0;
> - break;
> - default:
> - errno = 0;
> - n = strtoull(pch, &p, 0);
> - if (errno == ERANGE) {
> - expr_error(mon, "number too large");
> - }
> - if (pch == p) {
> - expr_error(mon, "invalid char '%c' in expression", *p);
> - }
> - pch = p;
> - while (qemu_isspace(*pch))
> - pch++;
> - break;
> - }
> - return n;
> -}
> -
> -
> -static int64_t expr_prod(Monitor *mon)
> -{
> - int64_t val, val2;
> - int op;
> -
> - val = expr_unary(mon);
> - for(;;) {
> - op = *pch;
> - if (op != '*' && op != '/' && op != '%')
> - break;
> - next();
> - val2 = expr_unary(mon);
> - switch(op) {
> - default:
> - case '*':
> - val *= val2;
> - break;
> - case '/':
> - case '%':
> - if (val2 == 0)
> - expr_error(mon, "division by zero");
> - if (op == '/')
> - val /= val2;
> - else
> - val %= val2;
> - break;
> - }
> - }
> - return val;
> -}
> -
> -static int64_t expr_logic(Monitor *mon)
> -{
> - int64_t val, val2;
> - int op;
> -
> - val = expr_prod(mon);
> - for(;;) {
> - op = *pch;
> - if (op != '&' && op != '|' && op != '^')
> - break;
> - next();
> - val2 = expr_prod(mon);
> - switch(op) {
> - default:
> - case '&':
> - val &= val2;
> - break;
> - case '|':
> - val |= val2;
> - break;
> - case '^':
> - val ^= val2;
> - break;
> - }
> - }
> - return val;
> -}
> -
> -static int64_t expr_sum(Monitor *mon)
> -{
> - int64_t val, val2;
> - int op;
> -
> - val = expr_logic(mon);
> - for(;;) {
> - op = *pch;
> - if (op != '+' && op != '-')
> - break;
> - next();
> - val2 = expr_logic(mon);
> - if (op == '+')
> - val += val2;
> - else
> - val -= val2;
> - }
> - return val;
> -}
> -
> -static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
> -{
> - pch = *pp;
> - if (sigsetjmp(expr_env, 0)) {
> - *pp = pch;
> - return -1;
> - }
> - while (qemu_isspace(*pch))
> - pch++;
> - *pval = expr_sum(mon);
> - *pp = pch;
> - return 0;
> -}
> -
> -static int get_double(Monitor *mon, double *pval, const char **pp)
> -{
> - const char *p = *pp;
> - char *tailp;
> - double d;
> -
> - d = strtod(p, &tailp);
> - if (tailp == p) {
> - monitor_printf(mon, "Number expected\n");
> - return -1;
> - }
> - if (d != d || d - d != 0) {
> - /* NaN or infinity */
> - monitor_printf(mon, "Bad number\n");
> - return -1;
> - }
> - *pval = d;
> - *pp = tailp;
> - return 0;
> -}
> -
> -/*
> - * Store the command-name in cmdname, and return a pointer to
> - * the remaining of the command string.
> - */
> -static const char *get_command_name(const char *cmdline,
> - char *cmdname, size_t nlen)
> -{
> - size_t len;
> - const char *p, *pstart;
> -
> - p = cmdline;
> - while (qemu_isspace(*p))
> - p++;
> - if (*p == '\0')
> - return NULL;
> - pstart = p;
> - while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
> - p++;
> - len = p - pstart;
> - if (len > nlen - 1)
> - len = nlen - 1;
> - memcpy(cmdname, pstart, len);
> - cmdname[len] = '\0';
> - return p;
> -}
> -
> -/**
> - * Read key of 'type' into 'key' and return the current
> - * 'type' pointer.
> - */
> -static char *key_get_info(const char *type, char **key)
> -{
> - size_t len;
> - char *p, *str;
> -
> - if (*type == ',')
> - type++;
> -
> - p = strchr(type, ':');
> - if (!p) {
> - *key = NULL;
> - return NULL;
> - }
> - len = p - type;
> -
> - str = g_malloc(len + 1);
> - memcpy(str, type, len);
> - str[len] = '\0';
> -
> - *key = str;
> - return ++p;
> -}
> -
> -static int default_fmt_format = 'x';
> -static int default_fmt_size = 4;
> -
> -static int is_valid_option(const char *c, const char *typestr)
> -{
> - char option[3];
> -
> - option[0] = '-';
> - option[1] = *c;
> - option[2] = '\0';
> -
> - typestr = strstr(typestr, option);
> - return (typestr != NULL);
> -}
> -
> -static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
> - const char *cmdname)
> -{
> - const mon_cmd_t *cmd;
> -
> - for (cmd = disp_table; cmd->name != NULL; cmd++) {
> - if (compare_cmd(cmdname, cmd->name)) {
> - return cmd;
> - }
> - }
> -
> - return NULL;
> -}
> -
> -/*
> - * Parse command name from @cmdp according to command table @table.
> - * If blank, return NULL.
> - * Else, if no valid command can be found, report to @mon, and return
> - * NULL.
> - * Else, change @cmdp to point right behind the name, and return its
> - * command table entry.
> - * Do not assume the return value points into @table! It doesn't when
> - * the command is found in a sub-command table.
> - */
> -static const mon_cmd_t *monitor_parse_command(MonitorHMP *hmp_mon,
> - const char *cmdp_start,
> - const char **cmdp,
> - mon_cmd_t *table)
> -{
> - Monitor *mon = &hmp_mon->common;
> - const char *p;
> - const mon_cmd_t *cmd;
> - char cmdname[256];
> -
> - /* extract the command name */
> - p = get_command_name(*cmdp, cmdname, sizeof(cmdname));
> - if (!p)
> - return NULL;
> -
> - cmd = search_dispatch_table(table, cmdname);
> - if (!cmd) {
> - monitor_printf(mon, "unknown command: '%.*s'\n",
> - (int)(p - cmdp_start), cmdp_start);
> - return NULL;
> - }
> - if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) {
> - monitor_printf(mon, "Command '%.*s' not available with -preconfig "
> - "until after exit_preconfig.\n",
> - (int)(p - cmdp_start), cmdp_start);
> - return NULL;
> - }
> -
> - /* filter out following useless space */
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> -
> - *cmdp = p;
> - /* search sub command */
> - if (cmd->sub_table != NULL && *p != '\0') {
> - return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table);
> - }
> -
> - return cmd;
> -}
> -
> -/*
> - * Parse arguments for @cmd.
> - * If it can't be parsed, report to @mon, and return NULL.
> - * Else, insert command arguments into a QDict, and return it.
> - * Note: On success, caller has to free the QDict structure.
> - */
> -
> -static QDict *monitor_parse_arguments(Monitor *mon,
> - const char **endp,
> - const mon_cmd_t *cmd)
> -{
> - const char *typestr;
> - char *key;
> - int c;
> - const char *p = *endp;
> - char buf[1024];
> - QDict *qdict = qdict_new();
> -
> - /* parse the parameters */
> - typestr = cmd->args_type;
> - for(;;) {
> - typestr = key_get_info(typestr, &key);
> - if (!typestr)
> - break;
> - c = *typestr;
> - typestr++;
> - switch(c) {
> - case 'F':
> - case 'B':
> - case 's':
> - {
> - int ret;
> -
> - while (qemu_isspace(*p))
> - p++;
> - if (*typestr == '?') {
> - typestr++;
> - if (*p == '\0') {
> - /* no optional string: NULL argument */
> - break;
> - }
> - }
> - ret = get_str(buf, sizeof(buf), &p);
> - if (ret < 0) {
> - switch(c) {
> - case 'F':
> - monitor_printf(mon, "%s: filename expected\n",
> - cmd->name);
> - break;
> - case 'B':
> - monitor_printf(mon, "%s: block device name expected\n",
> - cmd->name);
> - break;
> - default:
> - monitor_printf(mon, "%s: string expected\n", cmd->name);
> - break;
> - }
> - goto fail;
> - }
> - qdict_put_str(qdict, key, buf);
> - }
> - break;
> - case 'O':
> - {
> - QemuOptsList *opts_list;
> - QemuOpts *opts;
> -
> - opts_list = qemu_find_opts(key);
> - if (!opts_list || opts_list->desc->name) {
> - goto bad_type;
> - }
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> - if (!*p)
> - break;
> - if (get_str(buf, sizeof(buf), &p) < 0) {
> - goto fail;
> - }
> - opts = qemu_opts_parse_noisily(opts_list, buf, true);
> - if (!opts) {
> - goto fail;
> - }
> - qemu_opts_to_qdict(opts, qdict);
> - qemu_opts_del(opts);
> - }
> - break;
> - case '/':
> - {
> - int count, format, size;
> -
> - while (qemu_isspace(*p))
> - p++;
> - if (*p == '/') {
> - /* format found */
> - p++;
> - count = 1;
> - if (qemu_isdigit(*p)) {
> - count = 0;
> - while (qemu_isdigit(*p)) {
> - count = count * 10 + (*p - '0');
> - p++;
> - }
> - }
> - size = -1;
> - format = -1;
> - for(;;) {
> - switch(*p) {
> - case 'o':
> - case 'd':
> - case 'u':
> - case 'x':
> - case 'i':
> - case 'c':
> - format = *p++;
> - break;
> - case 'b':
> - size = 1;
> - p++;
> - break;
> - case 'h':
> - size = 2;
> - p++;
> - break;
> - case 'w':
> - size = 4;
> - p++;
> - break;
> - case 'g':
> - case 'L':
> - size = 8;
> - p++;
> - break;
> - default:
> - goto next;
> - }
> - }
> - next:
> - if (*p != '\0' && !qemu_isspace(*p)) {
> - monitor_printf(mon, "invalid char in format: '%c'\n",
> - *p);
> - goto fail;
> - }
> - if (format < 0)
> - format = default_fmt_format;
> - if (format != 'i') {
> - /* for 'i', not specifying a size gives -1 as size */
> - if (size < 0)
> - size = default_fmt_size;
> - default_fmt_size = size;
> - }
> - default_fmt_format = format;
> - } else {
> - count = 1;
> - format = default_fmt_format;
> - if (format != 'i') {
> - size = default_fmt_size;
> - } else {
> - size = -1;
> - }
> - }
> - qdict_put_int(qdict, "count", count);
> - qdict_put_int(qdict, "format", format);
> - qdict_put_int(qdict, "size", size);
> - }
> - break;
> - case 'i':
> - case 'l':
> - case 'M':
> - {
> - int64_t val;
> -
> - while (qemu_isspace(*p))
> - p++;
> - if (*typestr == '?' || *typestr == '.') {
> - if (*typestr == '?') {
> - if (*p == '\0') {
> - typestr++;
> - break;
> - }
> - } else {
> - if (*p == '.') {
> - p++;
> - while (qemu_isspace(*p))
> - p++;
> - } else {
> - typestr++;
> - break;
> - }
> - }
> - typestr++;
> - }
> - if (get_expr(mon, &val, &p))
> - goto fail;
> - /* Check if 'i' is greater than 32-bit */
> - if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
> - monitor_printf(mon, "\'%s\' has failed: ", cmd->name);
> - monitor_printf(mon, "integer is for 32-bit values\n");
> - goto fail;
> - } else if (c == 'M') {
> - if (val < 0) {
> - monitor_printf(mon, "enter a positive value\n");
> - goto fail;
> - }
> - val *= MiB;
> - }
> - qdict_put_int(qdict, key, val);
> - }
> - break;
> - case 'o':
> - {
> - int ret;
> - uint64_t val;
> - const char *end;
> -
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> - if (*typestr == '?') {
> - typestr++;
> - if (*p == '\0') {
> - break;
> - }
> - }
> - ret = qemu_strtosz_MiB(p, &end, &val);
> - if (ret < 0 || val > INT64_MAX) {
> - monitor_printf(mon, "invalid size\n");
> - goto fail;
> - }
> - qdict_put_int(qdict, key, val);
> - p = end;
> - }
> - break;
> - case 'T':
> - {
> - double val;
> -
> - while (qemu_isspace(*p))
> - p++;
> - if (*typestr == '?') {
> - typestr++;
> - if (*p == '\0') {
> - break;
> - }
> - }
> - if (get_double(mon, &val, &p) < 0) {
> - goto fail;
> - }
> - if (p[0] && p[1] == 's') {
> - switch (*p) {
> - case 'm':
> - val /= 1e3; p += 2; break;
> - case 'u':
> - val /= 1e6; p += 2; break;
> - case 'n':
> - val /= 1e9; p += 2; break;
> - }
> - }
> - if (*p && !qemu_isspace(*p)) {
> - monitor_printf(mon, "Unknown unit suffix\n");
> - goto fail;
> - }
> - qdict_put(qdict, key, qnum_from_double(val));
> - }
> - break;
> - case 'b':
> - {
> - const char *beg;
> - bool val;
> -
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> - beg = p;
> - while (qemu_isgraph(*p)) {
> - p++;
> - }
> - if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
> - val = true;
> - } else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
> - val = false;
> - } else {
> - monitor_printf(mon, "Expected 'on' or 'off'\n");
> - goto fail;
> - }
> - qdict_put_bool(qdict, key, val);
> - }
> - break;
> - case '-':
> - {
> - const char *tmp = p;
> - int skip_key = 0;
> - /* option */
> -
> - c = *typestr++;
> - if (c == '\0')
> - goto bad_type;
> - while (qemu_isspace(*p))
> - p++;
> - if (*p == '-') {
> - p++;
> - if(c != *p) {
> - if(!is_valid_option(p, typestr)) {
> -
> - monitor_printf(mon, "%s: unsupported option -%c\n",
> - cmd->name, *p);
> - goto fail;
> - } else {
> - skip_key = 1;
> - }
> - }
> - if(skip_key) {
> - p = tmp;
> - } else {
> - /* has option */
> - p++;
> - qdict_put_bool(qdict, key, true);
> - }
> - }
> - }
> - break;
> - case 'S':
> - {
> - /* package all remaining string */
> - int len;
> -
> - while (qemu_isspace(*p)) {
> - p++;
> - }
> - if (*typestr == '?') {
> - typestr++;
> - if (*p == '\0') {
> - /* no remaining string: NULL argument */
> - break;
> - }
> - }
> - len = strlen(p);
> - if (len <= 0) {
> - monitor_printf(mon, "%s: string expected\n",
> - cmd->name);
> - goto fail;
> - }
> - qdict_put_str(qdict, key, p);
> - p += len;
> - }
> - break;
> - default:
> - bad_type:
> - monitor_printf(mon, "%s: unknown type '%c'\n", cmd->name, c);
> - goto fail;
> - }
> - g_free(key);
> - key = NULL;
> - }
> - /* check that all arguments were parsed */
> - while (qemu_isspace(*p))
> - p++;
> - if (*p != '\0') {
> - monitor_printf(mon, "%s: extraneous characters at the end of line\n",
> - cmd->name);
> - goto fail;
> - }
> -
> - return qdict;
> -
> -fail:
> - qobject_unref(qdict);
> - g_free(key);
> - return NULL;
> -}
> -
> -static void handle_hmp_command(MonitorHMP *mon, const char *cmdline)
> -{
> - QDict *qdict;
> - const mon_cmd_t *cmd;
> - const char *cmd_start = cmdline;
> -
> - trace_handle_hmp_command(mon, cmdline);
> -
> - cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
> - if (!cmd) {
> - return;
> - }
> -
> - qdict = monitor_parse_arguments(&mon->common, &cmdline, cmd);
> - if (!qdict) {
> - while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) {
> - cmdline--;
> - }
> - monitor_printf(&mon->common, "Try \"help %.*s\" for more information\n",
> - (int)(cmdline - cmd_start), cmd_start);
> - return;
> - }
> -
> - cmd->cmd(&mon->common, qdict);
> - qobject_unref(qdict);
> -}
> -
> -static void cmd_completion(MonitorHMP *mon, const char *name, const char *list)
> -{
> - const char *p, *pstart;
> - char cmd[128];
> - int len;
> -
> - p = list;
> - for(;;) {
> - pstart = p;
> - p = qemu_strchrnul(p, '|');
> - len = p - pstart;
> - if (len > sizeof(cmd) - 2)
> - len = sizeof(cmd) - 2;
> - memcpy(cmd, pstart, len);
> - cmd[len] = '\0';
> - if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
> - readline_add_completion(mon->rs, cmd);
> - }
> - if (*p == '\0')
> - break;
> - p++;
> - }
> -}
> -
> -static void file_completion(MonitorHMP *mon, const char *input)
> -{
> - DIR *ffs;
> - struct dirent *d;
> - char path[1024];
> - char file[1024], file_prefix[1024];
> - int input_path_len;
> - const char *p;
> -
> - p = strrchr(input, '/');
> - if (!p) {
> - input_path_len = 0;
> - pstrcpy(file_prefix, sizeof(file_prefix), input);
> - pstrcpy(path, sizeof(path), ".");
> - } else {
> - input_path_len = p - input + 1;
> - memcpy(path, input, input_path_len);
> - if (input_path_len > sizeof(path) - 1)
> - input_path_len = sizeof(path) - 1;
> - path[input_path_len] = '\0';
> - pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
> - }
> -
> - ffs = opendir(path);
> - if (!ffs)
> - return;
> - for(;;) {
> - struct stat sb;
> - d = readdir(ffs);
> - if (!d)
> - break;
> -
> - if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
> - continue;
> - }
> -
> - if (strstart(d->d_name, file_prefix, NULL)) {
> - memcpy(file, input, input_path_len);
> - if (input_path_len < sizeof(file))
> - pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
> - d->d_name);
> - /* stat the file to find out if it's a directory.
> - * In that case add a slash to speed up typing long paths
> - */
> - if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
> - pstrcat(file, sizeof(file), "/");
> - }
> - readline_add_completion(mon->rs, file);
> - }
> - }
> - closedir(ffs);
> -}
> -
> -static const char *next_arg_type(const char *typestr)
> -{
> - const char *p = strchr(typestr, ':');
> - return (p != NULL ? ++p : typestr);
> -}
> -
> static void add_completion_option(ReadLineState *rs, const char *str,
> const char *option)
> {
> @@ -3829,127 +2738,6 @@ void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
> }
> }
>
> -static void monitor_find_completion_by_table(MonitorHMP *mon,
> - const mon_cmd_t *cmd_table,
> - char **args,
> - int nb_args)
> -{
> - const char *cmdname;
> - int i;
> - const char *ptype, *old_ptype, *str, *name;
> - const mon_cmd_t *cmd;
> - BlockBackend *blk = NULL;
> -
> - if (nb_args <= 1) {
> - /* command completion */
> - if (nb_args == 0)
> - cmdname = "";
> - else
> - cmdname = args[0];
> - readline_set_completion_index(mon->rs, strlen(cmdname));
> - for (cmd = cmd_table; cmd->name != NULL; cmd++) {
> - if (!runstate_check(RUN_STATE_PRECONFIG) ||
> - cmd_can_preconfig(cmd)) {
> - cmd_completion(mon, cmdname, cmd->name);
> - }
> - }
> - } else {
> - /* find the command */
> - for (cmd = cmd_table; cmd->name != NULL; cmd++) {
> - if (compare_cmd(args[0], cmd->name) &&
> - (!runstate_check(RUN_STATE_PRECONFIG) ||
> - cmd_can_preconfig(cmd))) {
> - break;
> - }
> - }
> - if (!cmd->name) {
> - return;
> - }
> -
> - if (cmd->sub_table) {
> - /* do the job again */
> - monitor_find_completion_by_table(mon, cmd->sub_table,
> - &args[1], nb_args - 1);
> - return;
> - }
> - if (cmd->command_completion) {
> - cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]);
> - return;
> - }
> -
> - ptype = next_arg_type(cmd->args_type);
> - for(i = 0; i < nb_args - 2; i++) {
> - if (*ptype != '\0') {
> - ptype = next_arg_type(ptype);
> - while (*ptype == '?')
> - ptype = next_arg_type(ptype);
> - }
> - }
> - str = args[nb_args - 1];
> - old_ptype = NULL;
> - while (*ptype == '-' && old_ptype != ptype) {
> - old_ptype = ptype;
> - ptype = next_arg_type(ptype);
> - }
> - switch(*ptype) {
> - case 'F':
> - /* file completion */
> - readline_set_completion_index(mon->rs, strlen(str));
> - file_completion(mon, str);
> - break;
> - case 'B':
> - /* block device name completion */
> - readline_set_completion_index(mon->rs, strlen(str));
> - while ((blk = blk_next(blk)) != NULL) {
> - name = blk_name(blk);
> - if (str[0] == '\0' ||
> - !strncmp(name, str, strlen(str))) {
> - readline_add_completion(mon->rs, name);
> - }
> - }
> - break;
> - case 's':
> - case 'S':
> - if (!strcmp(cmd->name, "help|?")) {
> - monitor_find_completion_by_table(mon, cmd_table,
> - &args[1], nb_args - 1);
> - }
> - break;
> - default:
> - break;
> - }
> - }
> -}
> -
> -static void monitor_find_completion(void *opaque,
> - const char *cmdline)
> -{
> - MonitorHMP *mon = opaque;
> - char *args[MAX_ARGS];
> - int nb_args, len;
> -
> - /* 1. parse the cmdline */
> - if (parse_cmdline(cmdline, &nb_args, args) < 0) {
> - return;
> - }
> -
> - /* if the line ends with a space, it means we want to complete the
> - next arg */
> - len = strlen(cmdline);
> - if (len > 0 && qemu_isspace(cmdline[len - 1])) {
> - if (nb_args >= MAX_ARGS) {
> - goto cleanup;
> - }
> - args[nb_args++] = g_strdup("");
> - }
> -
> - /* 2. auto complete according to args */
> - monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
> -
> -cleanup:
> - free_cmdline_args(args, nb_args);
> -}
> -
> int monitor_can_read(void *opaque)
> {
> Monitor *mon = opaque;
> @@ -3957,28 +2745,6 @@ int monitor_can_read(void *opaque)
> return !atomic_mb_read(&mon->suspend_cnt);
> }
>
> -static void monitor_read(void *opaque, const uint8_t *buf, int size)
> -{
> - MonitorHMP *mon;
> - Monitor *old_mon = cur_mon;
> - 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");
> - else
> - handle_hmp_command(mon, (char *)buf);
> - }
> -
> - cur_mon = old_mon;
> -}
> -
> static void monitor_command_cb(void *opaque, const char *cmdline,
> void *readline_opaque)
> {
> @@ -4043,58 +2809,6 @@ void monitor_resume(Monitor *mon)
> trace_monitor_suspend(mon, -1);
> }
>
> -static void monitor_event(void *opaque, int event)
> -{
> - Monitor *mon = opaque;
> - MonitorHMP *hmp_mon = container_of(cur_mon, MonitorHMP, common);
> -
> - switch (event) {
> - case CHR_EVENT_MUX_IN:
> - qemu_mutex_lock(&mon->mon_lock);
> - mon->mux_out = 0;
> - qemu_mutex_unlock(&mon->mon_lock);
> - if (mon->reset_seen) {
> - readline_restart(hmp_mon->rs);
> - monitor_resume(mon);
> - monitor_flush(mon);
> - } else {
> - atomic_mb_set(&mon->suspend_cnt, 0);
> - }
> - break;
> -
> - case CHR_EVENT_MUX_OUT:
> - if (mon->reset_seen) {
> - if (atomic_mb_read(&mon->suspend_cnt) == 0) {
> - monitor_printf(mon, "\n");
> - }
> - monitor_flush(mon);
> - monitor_suspend(mon);
> - } else {
> - atomic_inc(&mon->suspend_cnt);
> - }
> - qemu_mutex_lock(&mon->mon_lock);
> - mon->mux_out = 1;
> - qemu_mutex_unlock(&mon->mon_lock);
> - break;
> -
> - case CHR_EVENT_OPENED:
> - monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
> - "information\n", QEMU_VERSION);
> - if (!mon->mux_out) {
> - readline_restart(hmp_mon->rs);
> - readline_show_prompt(hmp_mon->rs);
> - }
> - mon->reset_seen = 1;
> - mon_refcount++;
> - break;
> -
> - case CHR_EVENT_CLOSED:
> - mon_refcount--;
> - monitor_fdsets_cleanup();
> - break;
> - }
> -}
> -
> static int
> compare_mon_cmd(const void *a, const void *b)
> {
> @@ -4137,25 +2851,6 @@ void monitor_init_globals(void)
> NULL);
> }
>
> -/* These functions just adapt the readline interface in a typesafe way. We
> - * could cast function pointers but that discards compiler checks.
> - */
> -static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
> - const char *fmt, ...)
> -{
> - MonitorHMP *mon = opaque;
> - va_list ap;
> - va_start(ap, fmt);
> - monitor_vprintf(&mon->common, fmt, ap);
> - va_end(ap);
> -}
> -
> -static void monitor_readline_flush(void *opaque)
> -{
> - MonitorHMP *mon = opaque;
> - monitor_flush(&mon->common);
> -}
> -
> /*
> * Print to current monitor if we have one, else to stderr.
> */
> @@ -4198,27 +2893,6 @@ void monitor_list_append(Monitor *mon)
> }
> }
>
> -static void monitor_init_hmp(Chardev *chr, int flags)
> -{
> - MonitorHMP *mon = g_malloc0(sizeof(*mon));
> - bool use_readline = flags & MONITOR_USE_READLINE;
> -
> - monitor_data_init_hmp(mon, flags, false);
> - qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
> -
> - if (use_readline) {
> - mon->rs = readline_init(monitor_readline_printf,
> - monitor_readline_flush,
> - mon,
> - monitor_find_completion);
> - monitor_read_command(mon, 0);
> - }
> -
> - qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read, monitor_read,
> - monitor_event, NULL, &mon->common, NULL, true);
> - monitor_list_append(&mon->common);
> -}
> -
> void monitor_init(Chardev *chr, int flags)
> {
> if (flags & MONITOR_USE_CONTROL) {
> diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
> index d04d58b583..48c73eed51 100644
> --- a/monitor/Makefile.objs
> +++ b/monitor/Makefile.objs
> @@ -1,2 +1,2 @@
> obj-y += misc.o
> -common-obj-y += qmp.o
> +common-obj-y += qmp.o hmp.o
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (8 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 09/10] monitor: Split out monitor/hmp.c Kevin Wolf
@ 2019-06-07 13:54 ` Kevin Wolf
2019-06-07 17:29 ` Dr. David Alan Gilbert
2019-06-07 17:30 ` Dr. David Alan Gilbert
2019-06-07 14:03 ` [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Daniel P. Berrangé
` (2 subsequent siblings)
12 siblings, 2 replies; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 13:54 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, armbru, qemu-block, dgilbert
Move the monitor core infrastructure from monitor/misc.c to
monitor/core.c. This is code that can be shared for all targets, so
compile it only once.
What remains in monitor/misc.c after this patch is mostly monitor
command implementations and code that requires a system emulator or is
even target-dependent.
The amount of function and particularly extern variables in
monitor_int.h is probably a bit larger than it needs to be, but this way
no non-trivial code modifications are needed. The interfaces between all
monitor parts can be cleaned up later.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/monitor/monitor.h | 1 +
monitor/monitor_int.h | 1 +
monitor/core.c | 604 ++++++++++++++++++++++++++++++++++++++
monitor/misc.c | 567 +----------------------------------
monitor/Makefile.objs | 2 +-
5 files changed, 608 insertions(+), 567 deletions(-)
create mode 100644 monitor/core.c
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 8547529e49..b9f8d175ed 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -20,6 +20,7 @@ typedef struct MonitorHMP MonitorHMP;
bool monitor_cur_is_qmp(void);
void monitor_init_globals(void);
+void monitor_init_globals_core(void);
void monitor_init(Chardev *chr, int flags);
void monitor_init_qmp(Chardev *chr, int flags);
void monitor_init_hmp(Chardev *chr, int flags);
diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
index 8c5d95f942..1a7af6a223 100644
--- a/monitor/monitor_int.h
+++ b/monitor/monitor_int.h
@@ -190,6 +190,7 @@ extern mon_cmd_t mon_cmds[];
int monitor_puts(Monitor *mon, const char *str);
void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
bool use_io_thread);
+void monitor_data_destroy(Monitor *mon);
int monitor_can_read(void *opaque);
void monitor_list_append(Monitor *mon);
void monitor_fdsets_cleanup(void);
diff --git a/monitor/core.c b/monitor/core.c
new file mode 100644
index 0000000000..1cea56054b
--- /dev/null
+++ b/monitor/core.c
@@ -0,0 +1,604 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "monitor_int.h"
+
+#include "qapi/error.h"
+#include "qapi/qapi-emit-events.h"
+#include "qapi/qmp/qstring.h"
+
+#include "qemu/option.h"
+#include "sysemu/qtest.h"
+
+#include "trace-root.h"
+
+/*
+ * To prevent flooding clients, events can be throttled. The
+ * throttling is calculated globally, rather than per-Monitor
+ * instance.
+ */
+typedef struct MonitorQAPIEventState {
+ QAPIEvent event; /* Throttling state for this event type and... */
+ QDict *data; /* ... data, see qapi_event_throttle_equal() */
+ QEMUTimer *timer; /* Timer for handling delayed events */
+ QDict *qdict; /* Delayed event (if any) */
+} MonitorQAPIEventState;
+
+typedef struct {
+ int64_t rate; /* Minimum time (in ns) between two events */
+} MonitorQAPIEventConf;
+
+/* Shared monitor I/O thread */
+IOThread *mon_iothread;
+
+/* Bottom half to dispatch the requests received from I/O thread */
+QEMUBH *qmp_dispatcher_bh;
+
+/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
+QemuMutex monitor_lock;
+static GHashTable *monitor_qapi_event_state;
+
+MonitorList mon_list;
+int mon_refcount;
+static bool monitor_destroyed;
+
+/**
+ * Is @mon is using readline?
+ * Note: not all HMP monitors use readline, e.g., gdbserver has a
+ * non-interactive HMP monitor, so readline is not used there.
+ */
+static inline bool monitor_uses_readline(const Monitor *mon)
+{
+ return mon->flags & MONITOR_USE_READLINE;
+}
+
+static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
+{
+ return !monitor_is_qmp(mon) && !monitor_uses_readline(mon);
+}
+
+static void monitor_flush_locked(Monitor *mon);
+
+static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
+ void *opaque)
+{
+ Monitor *mon = opaque;
+
+ qemu_mutex_lock(&mon->mon_lock);
+ mon->out_watch = 0;
+ monitor_flush_locked(mon);
+ qemu_mutex_unlock(&mon->mon_lock);
+ return FALSE;
+}
+
+/* Caller must hold mon->mon_lock */
+static void monitor_flush_locked(Monitor *mon)
+{
+ int rc;
+ size_t len;
+ const char *buf;
+
+ if (mon->skip_flush) {
+ return;
+ }
+
+ buf = qstring_get_str(mon->outbuf);
+ len = qstring_get_length(mon->outbuf);
+
+ if (len && !mon->mux_out) {
+ rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len);
+ if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
+ /* all flushed or error */
+ qobject_unref(mon->outbuf);
+ mon->outbuf = qstring_new();
+ return;
+ }
+ if (rc > 0) {
+ /* partial write */
+ QString *tmp = qstring_from_str(buf + rc);
+ qobject_unref(mon->outbuf);
+ mon->outbuf = tmp;
+ }
+ if (mon->out_watch == 0) {
+ mon->out_watch =
+ qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP,
+ monitor_unblocked, mon);
+ }
+ }
+}
+
+void monitor_flush(Monitor *mon)
+{
+ qemu_mutex_lock(&mon->mon_lock);
+ monitor_flush_locked(mon);
+ qemu_mutex_unlock(&mon->mon_lock);
+}
+
+/* flush at every end of line */
+int monitor_puts(Monitor *mon, const char *str)
+{
+ int i;
+ char c;
+
+ qemu_mutex_lock(&mon->mon_lock);
+ for (i = 0; str[i]; i++) {
+ c = str[i];
+ if (c == '\n') {
+ qstring_append_chr(mon->outbuf, '\r');
+ }
+ qstring_append_chr(mon->outbuf, c);
+ if (c == '\n') {
+ monitor_flush_locked(mon);
+ }
+ }
+ qemu_mutex_unlock(&mon->mon_lock);
+
+ return i;
+}
+
+int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+{
+ char *buf;
+ int n;
+
+ if (!mon)
+ return -1;
+
+ if (monitor_is_qmp(mon)) {
+ return -1;
+ }
+
+ buf = g_strdup_vprintf(fmt, ap);
+ n = monitor_puts(mon, buf);
+ g_free(buf);
+ return n;
+}
+
+int monitor_printf(Monitor *mon, const char *fmt, ...)
+{
+ int ret;
+
+ va_list ap;
+ va_start(ap, fmt);
+ ret = monitor_vprintf(mon, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
+ /* Limit guest-triggerable events to 1 per second */
+ [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
+ [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS },
+ [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS },
+ [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS },
+ [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS },
+ [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS },
+};
+
+/*
+ * Return the clock to use for recording an event's time.
+ * It's QEMU_CLOCK_REALTIME, except for qtests it's
+ * QEMU_CLOCK_VIRTUAL, to support testing rate limits.
+ * Beware: result is invalid before configure_accelerator().
+ */
+static inline QEMUClockType monitor_get_event_clock(void)
+{
+ return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME;
+}
+
+/*
+ * Broadcast an event to all monitors.
+ * @qdict is the event object. Its member "event" must match @event.
+ * Caller must hold monitor_lock.
+ */
+static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
+{
+ Monitor *mon;
+ MonitorQMP *qmp_mon;
+
+ trace_monitor_protocol_event_emit(event, qdict);
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
+ if (!monitor_is_qmp(mon)) {
+ continue;
+ }
+
+ qmp_mon = container_of(mon, MonitorQMP, common);
+ if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
+ qmp_send_response(qmp_mon, qdict);
+ }
+ }
+}
+
+static void monitor_qapi_event_handler(void *opaque);
+
+/*
+ * Queue a new event for emission to Monitor instances,
+ * applying any rate limiting if required.
+ */
+static void
+monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict)
+{
+ MonitorQAPIEventConf *evconf;
+ MonitorQAPIEventState *evstate;
+
+ assert(event < QAPI_EVENT__MAX);
+ evconf = &monitor_qapi_event_conf[event];
+ trace_monitor_protocol_event_queue(event, qdict, evconf->rate);
+
+ qemu_mutex_lock(&monitor_lock);
+
+ if (!evconf->rate) {
+ /* Unthrottled event */
+ monitor_qapi_event_emit(event, qdict);
+ } else {
+ QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
+ MonitorQAPIEventState key = { .event = event, .data = data };
+
+ evstate = g_hash_table_lookup(monitor_qapi_event_state, &key);
+ assert(!evstate || timer_pending(evstate->timer));
+
+ if (evstate) {
+ /*
+ * Timer is pending for (at least) evconf->rate ns after
+ * last send. Store event for sending when timer fires,
+ * replacing a prior stored event if any.
+ */
+ qobject_unref(evstate->qdict);
+ evstate->qdict = qobject_ref(qdict);
+ } else {
+ /*
+ * Last send was (at least) evconf->rate ns ago.
+ * Send immediately, and arm the timer to call
+ * monitor_qapi_event_handler() in evconf->rate ns. Any
+ * events arriving before then will be delayed until then.
+ */
+ int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
+
+ monitor_qapi_event_emit(event, qdict);
+
+ evstate = g_new(MonitorQAPIEventState, 1);
+ evstate->event = event;
+ evstate->data = qobject_ref(data);
+ evstate->qdict = NULL;
+ evstate->timer = timer_new_ns(monitor_get_event_clock(),
+ monitor_qapi_event_handler,
+ evstate);
+ g_hash_table_add(monitor_qapi_event_state, evstate);
+ timer_mod_ns(evstate->timer, now + evconf->rate);
+ }
+ }
+
+ qemu_mutex_unlock(&monitor_lock);
+}
+
+void qapi_event_emit(QAPIEvent event, QDict *qdict)
+{
+ /*
+ * monitor_qapi_event_queue_no_reenter() is not reentrant: it
+ * would deadlock on monitor_lock. Work around by queueing
+ * events in thread-local storage.
+ * TODO: remove this, make it re-enter safe.
+ */
+ typedef struct MonitorQapiEvent {
+ QAPIEvent event;
+ QDict *qdict;
+ QSIMPLEQ_ENTRY(MonitorQapiEvent) entry;
+ } MonitorQapiEvent;
+ static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue;
+ static __thread bool reentered;
+ MonitorQapiEvent *ev;
+
+ if (!reentered) {
+ QSIMPLEQ_INIT(&event_queue);
+ }
+
+ ev = g_new(MonitorQapiEvent, 1);
+ ev->qdict = qobject_ref(qdict);
+ ev->event = event;
+ QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry);
+ if (reentered) {
+ return;
+ }
+
+ reentered = true;
+
+ while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) {
+ QSIMPLEQ_REMOVE_HEAD(&event_queue, entry);
+ monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict);
+ qobject_unref(ev->qdict);
+ g_free(ev);
+ }
+
+ reentered = false;
+}
+
+/*
+ * This function runs evconf->rate ns after sending a throttled
+ * event.
+ * If another event has since been stored, send it.
+ */
+static void monitor_qapi_event_handler(void *opaque)
+{
+ MonitorQAPIEventState *evstate = opaque;
+ MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event];
+
+ trace_monitor_protocol_event_handler(evstate->event, evstate->qdict);
+ qemu_mutex_lock(&monitor_lock);
+
+ if (evstate->qdict) {
+ int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
+
+ monitor_qapi_event_emit(evstate->event, evstate->qdict);
+ qobject_unref(evstate->qdict);
+ evstate->qdict = NULL;
+ timer_mod_ns(evstate->timer, now + evconf->rate);
+ } else {
+ g_hash_table_remove(monitor_qapi_event_state, evstate);
+ qobject_unref(evstate->data);
+ timer_free(evstate->timer);
+ g_free(evstate);
+ }
+
+ qemu_mutex_unlock(&monitor_lock);
+}
+
+static unsigned int qapi_event_throttle_hash(const void *key)
+{
+ const MonitorQAPIEventState *evstate = key;
+ unsigned int hash = evstate->event * 255;
+
+ if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) {
+ hash += g_str_hash(qdict_get_str(evstate->data, "id"));
+ }
+
+ if (evstate->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
+ hash += g_str_hash(qdict_get_str(evstate->data, "node-name"));
+ }
+
+ return hash;
+}
+
+static gboolean qapi_event_throttle_equal(const void *a, const void *b)
+{
+ const MonitorQAPIEventState *eva = a;
+ const MonitorQAPIEventState *evb = b;
+
+ if (eva->event != evb->event) {
+ return FALSE;
+ }
+
+ if (eva->event == QAPI_EVENT_VSERPORT_CHANGE) {
+ return !strcmp(qdict_get_str(eva->data, "id"),
+ qdict_get_str(evb->data, "id"));
+ }
+
+ if (eva->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
+ return !strcmp(qdict_get_str(eva->data, "node-name"),
+ qdict_get_str(evb->data, "node-name"));
+ }
+
+ return TRUE;
+}
+
+int monitor_suspend(Monitor *mon)
+{
+ if (monitor_is_hmp_non_interactive(mon)) {
+ return -ENOTTY;
+ }
+
+ atomic_inc(&mon->suspend_cnt);
+
+ if (mon->use_io_thread) {
+ /*
+ * Kick I/O thread to make sure this takes effect. It'll be
+ * evaluated again in prepare() of the watch object.
+ */
+ aio_notify(iothread_get_aio_context(mon_iothread));
+ }
+
+ trace_monitor_suspend(mon, 1);
+ return 0;
+}
+
+static void monitor_accept_input(void *opaque)
+{
+ Monitor *mon = opaque;
+
+ qemu_chr_fe_accept_input(&mon->chr);
+}
+
+void monitor_resume(Monitor *mon)
+{
+ if (monitor_is_hmp_non_interactive(mon)) {
+ return;
+ }
+
+ if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
+ AioContext *ctx;
+
+ if (mon->use_io_thread) {
+ ctx = iothread_get_aio_context(mon_iothread);
+ } else {
+ ctx = qemu_get_aio_context();
+ }
+
+ if (!monitor_is_qmp(mon)) {
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+ assert(hmp_mon->rs);
+ readline_show_prompt(hmp_mon->rs);
+ }
+
+ aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
+ }
+
+ trace_monitor_suspend(mon, -1);
+}
+
+int monitor_can_read(void *opaque)
+{
+ Monitor *mon = opaque;
+
+ return !atomic_mb_read(&mon->suspend_cnt);
+}
+
+void monitor_list_append(Monitor *mon)
+{
+ qemu_mutex_lock(&monitor_lock);
+ /*
+ * This prevents inserting new monitors during monitor_cleanup().
+ * A cleaner solution would involve the main thread telling other
+ * threads to terminate, waiting for their termination.
+ */
+ if (!monitor_destroyed) {
+ QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
+ mon = NULL;
+ }
+ qemu_mutex_unlock(&monitor_lock);
+
+ if (mon) {
+ monitor_data_destroy(mon);
+ g_free(mon);
+ }
+}
+
+static void monitor_iothread_init(void)
+{
+ mon_iothread = iothread_create("mon_iothread", &error_abort);
+}
+
+void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
+ bool use_io_thread)
+{
+ if (use_io_thread && !mon_iothread) {
+ monitor_iothread_init();
+ }
+ memset(mon, 0, sizeof(Monitor));
+ qemu_mutex_init(&mon->mon_lock);
+ mon->outbuf = qstring_new();
+ mon->skip_flush = skip_flush;
+ mon->use_io_thread = use_io_thread;
+ mon->flags = flags;
+}
+
+void monitor_data_destroy(Monitor *mon)
+{
+ g_free(mon->mon_cpu_path);
+ qemu_chr_fe_deinit(&mon->chr, false);
+ if (monitor_is_qmp(mon)) {
+ MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
+ monitor_data_destroy_qmp(qmp_mon);
+ } else {
+ MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
+ readline_free(hmp_mon->rs);
+ }
+ qobject_unref(mon->outbuf);
+ qemu_mutex_destroy(&mon->mon_lock);
+}
+
+void monitor_init(Chardev *chr, int flags)
+{
+ if (flags & MONITOR_USE_CONTROL) {
+ monitor_init_qmp(chr, flags);
+ } else {
+ monitor_init_hmp(chr, flags);
+ }
+}
+
+void monitor_cleanup(void)
+{
+ /*
+ * We need to explicitly stop the I/O thread (but not destroy it),
+ * clean up the monitor resources, then destroy the I/O thread since
+ * we need to unregister from chardev below in
+ * monitor_data_destroy(), and chardev is not thread-safe yet
+ */
+ if (mon_iothread) {
+ iothread_stop(mon_iothread);
+ }
+
+ /* Flush output buffers and destroy monitors */
+ qemu_mutex_lock(&monitor_lock);
+ monitor_destroyed = true;
+ while (!QTAILQ_EMPTY(&mon_list)) {
+ Monitor *mon = QTAILQ_FIRST(&mon_list);
+ QTAILQ_REMOVE(&mon_list, mon, entry);
+ /* Permit QAPI event emission from character frontend release */
+ qemu_mutex_unlock(&monitor_lock);
+ monitor_flush(mon);
+ monitor_data_destroy(mon);
+ qemu_mutex_lock(&monitor_lock);
+ g_free(mon);
+ }
+ 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;
+ if (mon_iothread) {
+ iothread_destroy(mon_iothread);
+ mon_iothread = NULL;
+ }
+}
+
+static void monitor_qapi_event_init(void)
+{
+ monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash,
+ qapi_event_throttle_equal);
+}
+
+void monitor_init_globals_core(void)
+{
+ monitor_qapi_event_init();
+ qemu_mutex_init(&monitor_lock);
+
+ /*
+ * 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);
+}
+
+QemuOptsList qemu_mon_opts = {
+ .name = "mon",
+ .implied_opt_name = "chardev",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
+ .desc = {
+ {
+ .name = "mode",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "chardev",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "pretty",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
diff --git a/monitor/misc.c b/monitor/misc.c
index 408d11e1fe..1f60f31c95 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -117,43 +117,13 @@ struct MonFdset {
QLIST_ENTRY(MonFdset) next;
};
-/*
- * To prevent flooding clients, events can be throttled. The
- * throttling is calculated globally, rather than per-Monitor
- * instance.
- */
-typedef struct MonitorQAPIEventState {
- QAPIEvent event; /* Throttling state for this event type and... */
- QDict *data; /* ... data, see qapi_event_throttle_equal() */
- QEMUTimer *timer; /* Timer for handling delayed events */
- QDict *qdict; /* Delayed event (if any) */
-} MonitorQAPIEventState;
-
-typedef struct {
- int64_t rate; /* Minimum time (in ns) between two events */
-} MonitorQAPIEventConf;
-
-/* Shared monitor I/O thread */
-IOThread *mon_iothread;
-
-/* Bottom half to dispatch the requests received from I/O thread */
-QEMUBH *qmp_dispatcher_bh;
-
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
-/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
-QemuMutex monitor_lock;
-static GHashTable *monitor_qapi_event_state;
-MonitorList mon_list;
-static bool monitor_destroyed;
-
/* Protects mon_fdsets */
static QemuMutex mon_fdsets_lock;
static QLIST_HEAD(, MonFdset) mon_fdsets;
-int mon_refcount;
-
static mon_cmd_t info_cmds[];
__thread Monitor *cur_mon;
@@ -161,32 +131,6 @@ __thread Monitor *cur_mon;
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque);
-/**
- * Is @mon is using readline?
- * Note: not all HMP monitors use readline, e.g., gdbserver has a
- * non-interactive HMP monitor, so readline is not used there.
- */
-static inline bool monitor_uses_readline(const Monitor *mon)
-{
- return mon->flags & MONITOR_USE_READLINE;
-}
-
-static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
-{
- return !monitor_is_qmp(mon) && !monitor_uses_readline(mon);
-}
-
-/*
- * Return the clock to use for recording an event's time.
- * It's QEMU_CLOCK_REALTIME, except for qtests it's
- * QEMU_CLOCK_VIRTUAL, to support testing rate limits.
- * Beware: result is invalid before configure_accelerator().
- */
-static inline QEMUClockType monitor_get_event_clock(void)
-{
- return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME;
-}
-
/**
* Is the current monitor, if any, a QMP monitor?
*/
@@ -220,355 +164,6 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
}
-static void monitor_flush_locked(Monitor *mon);
-
-static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
- void *opaque)
-{
- Monitor *mon = opaque;
-
- qemu_mutex_lock(&mon->mon_lock);
- mon->out_watch = 0;
- monitor_flush_locked(mon);
- qemu_mutex_unlock(&mon->mon_lock);
- return FALSE;
-}
-
-/* Caller must hold mon->mon_lock */
-static void monitor_flush_locked(Monitor *mon)
-{
- int rc;
- size_t len;
- const char *buf;
-
- if (mon->skip_flush) {
- return;
- }
-
- buf = qstring_get_str(mon->outbuf);
- len = qstring_get_length(mon->outbuf);
-
- if (len && !mon->mux_out) {
- rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len);
- if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
- /* all flushed or error */
- qobject_unref(mon->outbuf);
- mon->outbuf = qstring_new();
- return;
- }
- if (rc > 0) {
- /* partial write */
- QString *tmp = qstring_from_str(buf + rc);
- qobject_unref(mon->outbuf);
- mon->outbuf = tmp;
- }
- if (mon->out_watch == 0) {
- mon->out_watch =
- qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP,
- monitor_unblocked, mon);
- }
- }
-}
-
-void monitor_flush(Monitor *mon)
-{
- qemu_mutex_lock(&mon->mon_lock);
- monitor_flush_locked(mon);
- qemu_mutex_unlock(&mon->mon_lock);
-}
-
-/* flush at every end of line */
-int monitor_puts(Monitor *mon, const char *str)
-{
- int i;
- char c;
-
- qemu_mutex_lock(&mon->mon_lock);
- for (i = 0; str[i]; i++) {
- c = str[i];
- if (c == '\n') {
- qstring_append_chr(mon->outbuf, '\r');
- }
- qstring_append_chr(mon->outbuf, c);
- if (c == '\n') {
- monitor_flush_locked(mon);
- }
- }
- qemu_mutex_unlock(&mon->mon_lock);
-
- return i;
-}
-
-int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
-{
- char *buf;
- int n;
-
- if (!mon)
- return -1;
-
- if (monitor_is_qmp(mon)) {
- return -1;
- }
-
- buf = g_strdup_vprintf(fmt, ap);
- n = monitor_puts(mon, buf);
- g_free(buf);
- return n;
-}
-
-int monitor_printf(Monitor *mon, const char *fmt, ...)
-{
- int ret;
-
- va_list ap;
- va_start(ap, fmt);
- ret = monitor_vprintf(mon, fmt, ap);
- va_end(ap);
- return ret;
-}
-
-static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
- /* Limit guest-triggerable events to 1 per second */
- [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
- [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS },
- [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS },
- [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS },
- [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS },
- [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS },
-};
-
-/*
- * Broadcast an event to all monitors.
- * @qdict is the event object. Its member "event" must match @event.
- * Caller must hold monitor_lock.
- */
-static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
-{
- Monitor *mon;
- MonitorQMP *qmp_mon;
-
- trace_monitor_protocol_event_emit(event, qdict);
- QTAILQ_FOREACH(mon, &mon_list, entry) {
- if (!monitor_is_qmp(mon)) {
- continue;
- }
-
- qmp_mon = container_of(mon, MonitorQMP, common);
- if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
- qmp_send_response(qmp_mon, qdict);
- }
- }
-}
-
-static void monitor_qapi_event_handler(void *opaque);
-
-/*
- * Queue a new event for emission to Monitor instances,
- * applying any rate limiting if required.
- */
-static void
-monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict)
-{
- MonitorQAPIEventConf *evconf;
- MonitorQAPIEventState *evstate;
-
- assert(event < QAPI_EVENT__MAX);
- evconf = &monitor_qapi_event_conf[event];
- trace_monitor_protocol_event_queue(event, qdict, evconf->rate);
-
- qemu_mutex_lock(&monitor_lock);
-
- if (!evconf->rate) {
- /* Unthrottled event */
- monitor_qapi_event_emit(event, qdict);
- } else {
- QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
- MonitorQAPIEventState key = { .event = event, .data = data };
-
- evstate = g_hash_table_lookup(monitor_qapi_event_state, &key);
- assert(!evstate || timer_pending(evstate->timer));
-
- if (evstate) {
- /*
- * Timer is pending for (at least) evconf->rate ns after
- * last send. Store event for sending when timer fires,
- * replacing a prior stored event if any.
- */
- qobject_unref(evstate->qdict);
- evstate->qdict = qobject_ref(qdict);
- } else {
- /*
- * Last send was (at least) evconf->rate ns ago.
- * Send immediately, and arm the timer to call
- * monitor_qapi_event_handler() in evconf->rate ns. Any
- * events arriving before then will be delayed until then.
- */
- int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
-
- monitor_qapi_event_emit(event, qdict);
-
- evstate = g_new(MonitorQAPIEventState, 1);
- evstate->event = event;
- evstate->data = qobject_ref(data);
- evstate->qdict = NULL;
- evstate->timer = timer_new_ns(monitor_get_event_clock(),
- monitor_qapi_event_handler,
- evstate);
- g_hash_table_add(monitor_qapi_event_state, evstate);
- timer_mod_ns(evstate->timer, now + evconf->rate);
- }
- }
-
- qemu_mutex_unlock(&monitor_lock);
-}
-
-void qapi_event_emit(QAPIEvent event, QDict *qdict)
-{
- /*
- * monitor_qapi_event_queue_no_reenter() is not reentrant: it
- * would deadlock on monitor_lock. Work around by queueing
- * events in thread-local storage.
- * TODO: remove this, make it re-enter safe.
- */
- typedef struct MonitorQapiEvent {
- QAPIEvent event;
- QDict *qdict;
- QSIMPLEQ_ENTRY(MonitorQapiEvent) entry;
- } MonitorQapiEvent;
- static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue;
- static __thread bool reentered;
- MonitorQapiEvent *ev;
-
- if (!reentered) {
- QSIMPLEQ_INIT(&event_queue);
- }
-
- ev = g_new(MonitorQapiEvent, 1);
- ev->qdict = qobject_ref(qdict);
- ev->event = event;
- QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry);
- if (reentered) {
- return;
- }
-
- reentered = true;
-
- while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) {
- QSIMPLEQ_REMOVE_HEAD(&event_queue, entry);
- monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict);
- qobject_unref(ev->qdict);
- g_free(ev);
- }
-
- reentered = false;
-}
-
-/*
- * This function runs evconf->rate ns after sending a throttled
- * event.
- * If another event has since been stored, send it.
- */
-static void monitor_qapi_event_handler(void *opaque)
-{
- MonitorQAPIEventState *evstate = opaque;
- MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event];
-
- trace_monitor_protocol_event_handler(evstate->event, evstate->qdict);
- qemu_mutex_lock(&monitor_lock);
-
- if (evstate->qdict) {
- int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
-
- monitor_qapi_event_emit(evstate->event, evstate->qdict);
- qobject_unref(evstate->qdict);
- evstate->qdict = NULL;
- timer_mod_ns(evstate->timer, now + evconf->rate);
- } else {
- g_hash_table_remove(monitor_qapi_event_state, evstate);
- qobject_unref(evstate->data);
- timer_free(evstate->timer);
- g_free(evstate);
- }
-
- qemu_mutex_unlock(&monitor_lock);
-}
-
-static unsigned int qapi_event_throttle_hash(const void *key)
-{
- const MonitorQAPIEventState *evstate = key;
- unsigned int hash = evstate->event * 255;
-
- if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) {
- hash += g_str_hash(qdict_get_str(evstate->data, "id"));
- }
-
- if (evstate->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
- hash += g_str_hash(qdict_get_str(evstate->data, "node-name"));
- }
-
- return hash;
-}
-
-static gboolean qapi_event_throttle_equal(const void *a, const void *b)
-{
- const MonitorQAPIEventState *eva = a;
- const MonitorQAPIEventState *evb = b;
-
- if (eva->event != evb->event) {
- return FALSE;
- }
-
- if (eva->event == QAPI_EVENT_VSERPORT_CHANGE) {
- return !strcmp(qdict_get_str(eva->data, "id"),
- qdict_get_str(evb->data, "id"));
- }
-
- if (eva->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
- return !strcmp(qdict_get_str(eva->data, "node-name"),
- qdict_get_str(evb->data, "node-name"));
- }
-
- return TRUE;
-}
-
-static void monitor_qapi_event_init(void)
-{
- monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash,
- qapi_event_throttle_equal);
-}
-
-static void monitor_iothread_init(void);
-
-void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
- bool use_io_thread)
-{
- if (use_io_thread && !mon_iothread) {
- monitor_iothread_init();
- }
- memset(mon, 0, sizeof(Monitor));
- qemu_mutex_init(&mon->mon_lock);
- mon->outbuf = qstring_new();
- mon->skip_flush = skip_flush;
- mon->use_io_thread = use_io_thread;
- mon->flags = flags;
-}
-
-static void monitor_data_destroy(Monitor *mon)
-{
- g_free(mon->mon_cpu_path);
- qemu_chr_fe_deinit(&mon->chr, false);
- if (monitor_is_qmp(mon)) {
- MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
- monitor_data_destroy_qmp(qmp_mon);
- } else {
- MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
- readline_free(hmp_mon->rs);
- }
- qobject_unref(mon->outbuf);
- qemu_mutex_destroy(&mon->mon_lock);
-}
-
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
int64_t cpu_index, Error **errp)
{
@@ -2738,13 +2333,6 @@ void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
}
}
-int monitor_can_read(void *opaque)
-{
- Monitor *mon = opaque;
-
- return !atomic_mb_read(&mon->suspend_cnt);
-}
-
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque)
{
@@ -2755,60 +2343,6 @@ static void monitor_command_cb(void *opaque, const char *cmdline,
monitor_resume(&mon->common);
}
-int monitor_suspend(Monitor *mon)
-{
- if (monitor_is_hmp_non_interactive(mon)) {
- return -ENOTTY;
- }
-
- atomic_inc(&mon->suspend_cnt);
-
- if (mon->use_io_thread) {
- /*
- * Kick I/O thread to make sure this takes effect. It'll be
- * evaluated again in prepare() of the watch object.
- */
- aio_notify(iothread_get_aio_context(mon_iothread));
- }
-
- trace_monitor_suspend(mon, 1);
- return 0;
-}
-
-static void monitor_accept_input(void *opaque)
-{
- Monitor *mon = opaque;
-
- qemu_chr_fe_accept_input(&mon->chr);
-}
-
-void monitor_resume(Monitor *mon)
-{
- if (monitor_is_hmp_non_interactive(mon)) {
- return;
- }
-
- if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
- AioContext *ctx;
-
- if (mon->use_io_thread) {
- ctx = iothread_get_aio_context(mon_iothread);
- } else {
- ctx = qemu_get_aio_context();
- }
-
- if (!monitor_is_qmp(mon)) {
- MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
- assert(hmp_mon->rs);
- readline_show_prompt(hmp_mon->rs);
- }
-
- aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
- }
-
- trace_monitor_suspend(mon, -1);
-}
-
static int
compare_mon_cmd(const void *a, const void *b)
{
@@ -2828,27 +2362,12 @@ static void sortcmdlist(void)
qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd);
}
-static void monitor_iothread_init(void)
-{
- mon_iothread = iothread_create("mon_iothread", &error_abort);
-}
-
void monitor_init_globals(void)
{
+ monitor_init_globals_core();
monitor_init_qmp_commands();
- monitor_qapi_event_init();
sortcmdlist();
- qemu_mutex_init(&monitor_lock);
qemu_mutex_init(&mon_fdsets_lock);
-
- /*
- * 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);
}
/*
@@ -2873,90 +2392,6 @@ int error_vprintf_unless_qmp(const char *fmt, va_list ap)
return -1;
}
-void monitor_list_append(Monitor *mon)
-{
- qemu_mutex_lock(&monitor_lock);
- /*
- * This prevents inserting new monitors during monitor_cleanup().
- * A cleaner solution would involve the main thread telling other
- * threads to terminate, waiting for their termination.
- */
- if (!monitor_destroyed) {
- QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
- mon = NULL;
- }
- qemu_mutex_unlock(&monitor_lock);
-
- if (mon) {
- monitor_data_destroy(mon);
- g_free(mon);
- }
-}
-
-void monitor_init(Chardev *chr, int flags)
-{
- if (flags & MONITOR_USE_CONTROL) {
- monitor_init_qmp(chr, flags);
- } else {
- monitor_init_hmp(chr, flags);
- }
-}
-
-void monitor_cleanup(void)
-{
- /*
- * We need to explicitly stop the I/O thread (but not destroy it),
- * clean up the monitor resources, then destroy the I/O thread since
- * we need to unregister from chardev below in
- * monitor_data_destroy(), and chardev is not thread-safe yet
- */
- if (mon_iothread) {
- iothread_stop(mon_iothread);
- }
-
- /* Flush output buffers and destroy monitors */
- qemu_mutex_lock(&monitor_lock);
- monitor_destroyed = true;
- while (!QTAILQ_EMPTY(&mon_list)) {
- Monitor *mon = QTAILQ_FIRST(&mon_list);
- QTAILQ_REMOVE(&mon_list, mon, entry);
- /* Permit QAPI event emission from character frontend release */
- qemu_mutex_unlock(&monitor_lock);
- monitor_flush(mon);
- monitor_data_destroy(mon);
- qemu_mutex_lock(&monitor_lock);
- g_free(mon);
- }
- 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;
- if (mon_iothread) {
- iothread_destroy(mon_iothread);
- mon_iothread = NULL;
- }
-}
-
-QemuOptsList qemu_mon_opts = {
- .name = "mon",
- .implied_opt_name = "chardev",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
- .desc = {
- {
- .name = "mode",
- .type = QEMU_OPT_STRING,
- },{
- .name = "chardev",
- .type = QEMU_OPT_STRING,
- },{
- .name = "pretty",
- .type = QEMU_OPT_BOOL,
- },
- { /* end of list */ }
- },
-};
-
HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
index 48c73eed51..c8dff5e4b5 100644
--- a/monitor/Makefile.objs
+++ b/monitor/Makefile.objs
@@ -1,2 +1,2 @@
obj-y += misc.o
-common-obj-y += qmp.o hmp.o
+common-obj-y += core.o qmp.o hmp.o
--
2.20.1
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c Kevin Wolf
@ 2019-06-07 17:29 ` Dr. David Alan Gilbert
2019-06-11 9:22 ` Kevin Wolf
2019-06-07 17:30 ` Dr. David Alan Gilbert
1 sibling, 1 reply; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 17:29 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Move the monitor core infrastructure from monitor/misc.c to
> monitor/core.c. This is code that can be shared for all targets, so
> compile it only once.
>
> What remains in monitor/misc.c after this patch is mostly monitor
> command implementations and code that requires a system emulator or is
> even target-dependent.
>
> The amount of function and particularly extern variables in
> monitor_int.h is probably a bit larger than it needs to be, but this way
> no non-trivial code modifications are needed. The interfaces between all
> monitor parts can be cleaned up later.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
OK, but can you call it anything other than core.* - I regularly end up
deleting things like that!
Other than that,
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
> include/monitor/monitor.h | 1 +
> monitor/monitor_int.h | 1 +
> monitor/core.c | 604 ++++++++++++++++++++++++++++++++++++++
> monitor/misc.c | 567 +----------------------------------
> monitor/Makefile.objs | 2 +-
> 5 files changed, 608 insertions(+), 567 deletions(-)
> create mode 100644 monitor/core.c
>
> diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
> index 8547529e49..b9f8d175ed 100644
> --- a/include/monitor/monitor.h
> +++ b/include/monitor/monitor.h
> @@ -20,6 +20,7 @@ typedef struct MonitorHMP MonitorHMP;
> bool monitor_cur_is_qmp(void);
>
> void monitor_init_globals(void);
> +void monitor_init_globals_core(void);
> void monitor_init(Chardev *chr, int flags);
> void monitor_init_qmp(Chardev *chr, int flags);
> void monitor_init_hmp(Chardev *chr, int flags);
> diff --git a/monitor/monitor_int.h b/monitor/monitor_int.h
> index 8c5d95f942..1a7af6a223 100644
> --- a/monitor/monitor_int.h
> +++ b/monitor/monitor_int.h
> @@ -190,6 +190,7 @@ extern mon_cmd_t mon_cmds[];
> int monitor_puts(Monitor *mon, const char *str);
> void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> bool use_io_thread);
> +void monitor_data_destroy(Monitor *mon);
> int monitor_can_read(void *opaque);
> void monitor_list_append(Monitor *mon);
> void monitor_fdsets_cleanup(void);
> diff --git a/monitor/core.c b/monitor/core.c
> new file mode 100644
> index 0000000000..1cea56054b
> --- /dev/null
> +++ b/monitor/core.c
> @@ -0,0 +1,604 @@
> +/*
> + * QEMU monitor
> + *
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "monitor_int.h"
> +
> +#include "qapi/error.h"
> +#include "qapi/qapi-emit-events.h"
> +#include "qapi/qmp/qstring.h"
> +
> +#include "qemu/option.h"
> +#include "sysemu/qtest.h"
> +
> +#include "trace-root.h"
> +
> +/*
> + * To prevent flooding clients, events can be throttled. The
> + * throttling is calculated globally, rather than per-Monitor
> + * instance.
> + */
> +typedef struct MonitorQAPIEventState {
> + QAPIEvent event; /* Throttling state for this event type and... */
> + QDict *data; /* ... data, see qapi_event_throttle_equal() */
> + QEMUTimer *timer; /* Timer for handling delayed events */
> + QDict *qdict; /* Delayed event (if any) */
> +} MonitorQAPIEventState;
> +
> +typedef struct {
> + int64_t rate; /* Minimum time (in ns) between two events */
> +} MonitorQAPIEventConf;
> +
> +/* Shared monitor I/O thread */
> +IOThread *mon_iothread;
> +
> +/* Bottom half to dispatch the requests received from I/O thread */
> +QEMUBH *qmp_dispatcher_bh;
> +
> +/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
> +QemuMutex monitor_lock;
> +static GHashTable *monitor_qapi_event_state;
> +
> +MonitorList mon_list;
> +int mon_refcount;
> +static bool monitor_destroyed;
> +
> +/**
> + * Is @mon is using readline?
> + * Note: not all HMP monitors use readline, e.g., gdbserver has a
> + * non-interactive HMP monitor, so readline is not used there.
> + */
> +static inline bool monitor_uses_readline(const Monitor *mon)
> +{
> + return mon->flags & MONITOR_USE_READLINE;
> +}
> +
> +static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
> +{
> + return !monitor_is_qmp(mon) && !monitor_uses_readline(mon);
> +}
> +
> +static void monitor_flush_locked(Monitor *mon);
> +
> +static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
> + void *opaque)
> +{
> + Monitor *mon = opaque;
> +
> + qemu_mutex_lock(&mon->mon_lock);
> + mon->out_watch = 0;
> + monitor_flush_locked(mon);
> + qemu_mutex_unlock(&mon->mon_lock);
> + return FALSE;
> +}
> +
> +/* Caller must hold mon->mon_lock */
> +static void monitor_flush_locked(Monitor *mon)
> +{
> + int rc;
> + size_t len;
> + const char *buf;
> +
> + if (mon->skip_flush) {
> + return;
> + }
> +
> + buf = qstring_get_str(mon->outbuf);
> + len = qstring_get_length(mon->outbuf);
> +
> + if (len && !mon->mux_out) {
> + rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len);
> + if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
> + /* all flushed or error */
> + qobject_unref(mon->outbuf);
> + mon->outbuf = qstring_new();
> + return;
> + }
> + if (rc > 0) {
> + /* partial write */
> + QString *tmp = qstring_from_str(buf + rc);
> + qobject_unref(mon->outbuf);
> + mon->outbuf = tmp;
> + }
> + if (mon->out_watch == 0) {
> + mon->out_watch =
> + qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP,
> + monitor_unblocked, mon);
> + }
> + }
> +}
> +
> +void monitor_flush(Monitor *mon)
> +{
> + qemu_mutex_lock(&mon->mon_lock);
> + monitor_flush_locked(mon);
> + qemu_mutex_unlock(&mon->mon_lock);
> +}
> +
> +/* flush at every end of line */
> +int monitor_puts(Monitor *mon, const char *str)
> +{
> + int i;
> + char c;
> +
> + qemu_mutex_lock(&mon->mon_lock);
> + for (i = 0; str[i]; i++) {
> + c = str[i];
> + if (c == '\n') {
> + qstring_append_chr(mon->outbuf, '\r');
> + }
> + qstring_append_chr(mon->outbuf, c);
> + if (c == '\n') {
> + monitor_flush_locked(mon);
> + }
> + }
> + qemu_mutex_unlock(&mon->mon_lock);
> +
> + return i;
> +}
> +
> +int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
> +{
> + char *buf;
> + int n;
> +
> + if (!mon)
> + return -1;
> +
> + if (monitor_is_qmp(mon)) {
> + return -1;
> + }
> +
> + buf = g_strdup_vprintf(fmt, ap);
> + n = monitor_puts(mon, buf);
> + g_free(buf);
> + return n;
> +}
> +
> +int monitor_printf(Monitor *mon, const char *fmt, ...)
> +{
> + int ret;
> +
> + va_list ap;
> + va_start(ap, fmt);
> + ret = monitor_vprintf(mon, fmt, ap);
> + va_end(ap);
> + return ret;
> +}
> +
> +
> +static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
> + /* Limit guest-triggerable events to 1 per second */
> + [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
> + [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS },
> + [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS },
> + [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS },
> + [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS },
> + [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS },
> +};
> +
> +/*
> + * Return the clock to use for recording an event's time.
> + * It's QEMU_CLOCK_REALTIME, except for qtests it's
> + * QEMU_CLOCK_VIRTUAL, to support testing rate limits.
> + * Beware: result is invalid before configure_accelerator().
> + */
> +static inline QEMUClockType monitor_get_event_clock(void)
> +{
> + return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME;
> +}
> +
> +/*
> + * Broadcast an event to all monitors.
> + * @qdict is the event object. Its member "event" must match @event.
> + * Caller must hold monitor_lock.
> + */
> +static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
> +{
> + Monitor *mon;
> + MonitorQMP *qmp_mon;
> +
> + trace_monitor_protocol_event_emit(event, qdict);
> + QTAILQ_FOREACH(mon, &mon_list, entry) {
> + if (!monitor_is_qmp(mon)) {
> + continue;
> + }
> +
> + qmp_mon = container_of(mon, MonitorQMP, common);
> + if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
> + qmp_send_response(qmp_mon, qdict);
> + }
> + }
> +}
> +
> +static void monitor_qapi_event_handler(void *opaque);
> +
> +/*
> + * Queue a new event for emission to Monitor instances,
> + * applying any rate limiting if required.
> + */
> +static void
> +monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict)
> +{
> + MonitorQAPIEventConf *evconf;
> + MonitorQAPIEventState *evstate;
> +
> + assert(event < QAPI_EVENT__MAX);
> + evconf = &monitor_qapi_event_conf[event];
> + trace_monitor_protocol_event_queue(event, qdict, evconf->rate);
> +
> + qemu_mutex_lock(&monitor_lock);
> +
> + if (!evconf->rate) {
> + /* Unthrottled event */
> + monitor_qapi_event_emit(event, qdict);
> + } else {
> + QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
> + MonitorQAPIEventState key = { .event = event, .data = data };
> +
> + evstate = g_hash_table_lookup(monitor_qapi_event_state, &key);
> + assert(!evstate || timer_pending(evstate->timer));
> +
> + if (evstate) {
> + /*
> + * Timer is pending for (at least) evconf->rate ns after
> + * last send. Store event for sending when timer fires,
> + * replacing a prior stored event if any.
> + */
> + qobject_unref(evstate->qdict);
> + evstate->qdict = qobject_ref(qdict);
> + } else {
> + /*
> + * Last send was (at least) evconf->rate ns ago.
> + * Send immediately, and arm the timer to call
> + * monitor_qapi_event_handler() in evconf->rate ns. Any
> + * events arriving before then will be delayed until then.
> + */
> + int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
> +
> + monitor_qapi_event_emit(event, qdict);
> +
> + evstate = g_new(MonitorQAPIEventState, 1);
> + evstate->event = event;
> + evstate->data = qobject_ref(data);
> + evstate->qdict = NULL;
> + evstate->timer = timer_new_ns(monitor_get_event_clock(),
> + monitor_qapi_event_handler,
> + evstate);
> + g_hash_table_add(monitor_qapi_event_state, evstate);
> + timer_mod_ns(evstate->timer, now + evconf->rate);
> + }
> + }
> +
> + qemu_mutex_unlock(&monitor_lock);
> +}
> +
> +void qapi_event_emit(QAPIEvent event, QDict *qdict)
> +{
> + /*
> + * monitor_qapi_event_queue_no_reenter() is not reentrant: it
> + * would deadlock on monitor_lock. Work around by queueing
> + * events in thread-local storage.
> + * TODO: remove this, make it re-enter safe.
> + */
> + typedef struct MonitorQapiEvent {
> + QAPIEvent event;
> + QDict *qdict;
> + QSIMPLEQ_ENTRY(MonitorQapiEvent) entry;
> + } MonitorQapiEvent;
> + static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue;
> + static __thread bool reentered;
> + MonitorQapiEvent *ev;
> +
> + if (!reentered) {
> + QSIMPLEQ_INIT(&event_queue);
> + }
> +
> + ev = g_new(MonitorQapiEvent, 1);
> + ev->qdict = qobject_ref(qdict);
> + ev->event = event;
> + QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry);
> + if (reentered) {
> + return;
> + }
> +
> + reentered = true;
> +
> + while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) {
> + QSIMPLEQ_REMOVE_HEAD(&event_queue, entry);
> + monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict);
> + qobject_unref(ev->qdict);
> + g_free(ev);
> + }
> +
> + reentered = false;
> +}
> +
> +/*
> + * This function runs evconf->rate ns after sending a throttled
> + * event.
> + * If another event has since been stored, send it.
> + */
> +static void monitor_qapi_event_handler(void *opaque)
> +{
> + MonitorQAPIEventState *evstate = opaque;
> + MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event];
> +
> + trace_monitor_protocol_event_handler(evstate->event, evstate->qdict);
> + qemu_mutex_lock(&monitor_lock);
> +
> + if (evstate->qdict) {
> + int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
> +
> + monitor_qapi_event_emit(evstate->event, evstate->qdict);
> + qobject_unref(evstate->qdict);
> + evstate->qdict = NULL;
> + timer_mod_ns(evstate->timer, now + evconf->rate);
> + } else {
> + g_hash_table_remove(monitor_qapi_event_state, evstate);
> + qobject_unref(evstate->data);
> + timer_free(evstate->timer);
> + g_free(evstate);
> + }
> +
> + qemu_mutex_unlock(&monitor_lock);
> +}
> +
> +static unsigned int qapi_event_throttle_hash(const void *key)
> +{
> + const MonitorQAPIEventState *evstate = key;
> + unsigned int hash = evstate->event * 255;
> +
> + if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) {
> + hash += g_str_hash(qdict_get_str(evstate->data, "id"));
> + }
> +
> + if (evstate->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
> + hash += g_str_hash(qdict_get_str(evstate->data, "node-name"));
> + }
> +
> + return hash;
> +}
> +
> +static gboolean qapi_event_throttle_equal(const void *a, const void *b)
> +{
> + const MonitorQAPIEventState *eva = a;
> + const MonitorQAPIEventState *evb = b;
> +
> + if (eva->event != evb->event) {
> + return FALSE;
> + }
> +
> + if (eva->event == QAPI_EVENT_VSERPORT_CHANGE) {
> + return !strcmp(qdict_get_str(eva->data, "id"),
> + qdict_get_str(evb->data, "id"));
> + }
> +
> + if (eva->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
> + return !strcmp(qdict_get_str(eva->data, "node-name"),
> + qdict_get_str(evb->data, "node-name"));
> + }
> +
> + return TRUE;
> +}
> +
> +int monitor_suspend(Monitor *mon)
> +{
> + if (monitor_is_hmp_non_interactive(mon)) {
> + return -ENOTTY;
> + }
> +
> + atomic_inc(&mon->suspend_cnt);
> +
> + if (mon->use_io_thread) {
> + /*
> + * Kick I/O thread to make sure this takes effect. It'll be
> + * evaluated again in prepare() of the watch object.
> + */
> + aio_notify(iothread_get_aio_context(mon_iothread));
> + }
> +
> + trace_monitor_suspend(mon, 1);
> + return 0;
> +}
> +
> +static void monitor_accept_input(void *opaque)
> +{
> + Monitor *mon = opaque;
> +
> + qemu_chr_fe_accept_input(&mon->chr);
> +}
> +
> +void monitor_resume(Monitor *mon)
> +{
> + if (monitor_is_hmp_non_interactive(mon)) {
> + return;
> + }
> +
> + if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
> + AioContext *ctx;
> +
> + if (mon->use_io_thread) {
> + ctx = iothread_get_aio_context(mon_iothread);
> + } else {
> + ctx = qemu_get_aio_context();
> + }
> +
> + if (!monitor_is_qmp(mon)) {
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> + assert(hmp_mon->rs);
> + readline_show_prompt(hmp_mon->rs);
> + }
> +
> + aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
> + }
> +
> + trace_monitor_suspend(mon, -1);
> +}
> +
> +int monitor_can_read(void *opaque)
> +{
> + Monitor *mon = opaque;
> +
> + return !atomic_mb_read(&mon->suspend_cnt);
> +}
> +
> +void monitor_list_append(Monitor *mon)
> +{
> + qemu_mutex_lock(&monitor_lock);
> + /*
> + * This prevents inserting new monitors during monitor_cleanup().
> + * A cleaner solution would involve the main thread telling other
> + * threads to terminate, waiting for their termination.
> + */
> + if (!monitor_destroyed) {
> + QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
> + mon = NULL;
> + }
> + qemu_mutex_unlock(&monitor_lock);
> +
> + if (mon) {
> + monitor_data_destroy(mon);
> + g_free(mon);
> + }
> +}
> +
> +static void monitor_iothread_init(void)
> +{
> + mon_iothread = iothread_create("mon_iothread", &error_abort);
> +}
> +
> +void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> + bool use_io_thread)
> +{
> + if (use_io_thread && !mon_iothread) {
> + monitor_iothread_init();
> + }
> + memset(mon, 0, sizeof(Monitor));
> + qemu_mutex_init(&mon->mon_lock);
> + mon->outbuf = qstring_new();
> + mon->skip_flush = skip_flush;
> + mon->use_io_thread = use_io_thread;
> + mon->flags = flags;
> +}
> +
> +void monitor_data_destroy(Monitor *mon)
> +{
> + g_free(mon->mon_cpu_path);
> + qemu_chr_fe_deinit(&mon->chr, false);
> + if (monitor_is_qmp(mon)) {
> + MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
> + monitor_data_destroy_qmp(qmp_mon);
> + } else {
> + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> + readline_free(hmp_mon->rs);
> + }
> + qobject_unref(mon->outbuf);
> + qemu_mutex_destroy(&mon->mon_lock);
> +}
> +
> +void monitor_init(Chardev *chr, int flags)
> +{
> + if (flags & MONITOR_USE_CONTROL) {
> + monitor_init_qmp(chr, flags);
> + } else {
> + monitor_init_hmp(chr, flags);
> + }
> +}
> +
> +void monitor_cleanup(void)
> +{
> + /*
> + * We need to explicitly stop the I/O thread (but not destroy it),
> + * clean up the monitor resources, then destroy the I/O thread since
> + * we need to unregister from chardev below in
> + * monitor_data_destroy(), and chardev is not thread-safe yet
> + */
> + if (mon_iothread) {
> + iothread_stop(mon_iothread);
> + }
> +
> + /* Flush output buffers and destroy monitors */
> + qemu_mutex_lock(&monitor_lock);
> + monitor_destroyed = true;
> + while (!QTAILQ_EMPTY(&mon_list)) {
> + Monitor *mon = QTAILQ_FIRST(&mon_list);
> + QTAILQ_REMOVE(&mon_list, mon, entry);
> + /* Permit QAPI event emission from character frontend release */
> + qemu_mutex_unlock(&monitor_lock);
> + monitor_flush(mon);
> + monitor_data_destroy(mon);
> + qemu_mutex_lock(&monitor_lock);
> + g_free(mon);
> + }
> + 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;
> + if (mon_iothread) {
> + iothread_destroy(mon_iothread);
> + mon_iothread = NULL;
> + }
> +}
> +
> +static void monitor_qapi_event_init(void)
> +{
> + monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash,
> + qapi_event_throttle_equal);
> +}
> +
> +void monitor_init_globals_core(void)
> +{
> + monitor_qapi_event_init();
> + qemu_mutex_init(&monitor_lock);
> +
> + /*
> + * 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);
> +}
> +
> +QemuOptsList qemu_mon_opts = {
> + .name = "mon",
> + .implied_opt_name = "chardev",
> + .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
> + .desc = {
> + {
> + .name = "mode",
> + .type = QEMU_OPT_STRING,
> + },{
> + .name = "chardev",
> + .type = QEMU_OPT_STRING,
> + },{
> + .name = "pretty",
> + .type = QEMU_OPT_BOOL,
> + },
> + { /* end of list */ }
> + },
> +};
> diff --git a/monitor/misc.c b/monitor/misc.c
> index 408d11e1fe..1f60f31c95 100644
> --- a/monitor/misc.c
> +++ b/monitor/misc.c
> @@ -117,43 +117,13 @@ struct MonFdset {
> QLIST_ENTRY(MonFdset) next;
> };
>
> -/*
> - * To prevent flooding clients, events can be throttled. The
> - * throttling is calculated globally, rather than per-Monitor
> - * instance.
> - */
> -typedef struct MonitorQAPIEventState {
> - QAPIEvent event; /* Throttling state for this event type and... */
> - QDict *data; /* ... data, see qapi_event_throttle_equal() */
> - QEMUTimer *timer; /* Timer for handling delayed events */
> - QDict *qdict; /* Delayed event (if any) */
> -} MonitorQAPIEventState;
> -
> -typedef struct {
> - int64_t rate; /* Minimum time (in ns) between two events */
> -} MonitorQAPIEventConf;
> -
> -/* Shared monitor I/O thread */
> -IOThread *mon_iothread;
> -
> -/* Bottom half to dispatch the requests received from I/O thread */
> -QEMUBH *qmp_dispatcher_bh;
> -
> /* QMP checker flags */
> #define QMP_ACCEPT_UNKNOWNS 1
>
> -/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
> -QemuMutex monitor_lock;
> -static GHashTable *monitor_qapi_event_state;
> -MonitorList mon_list;
> -static bool monitor_destroyed;
> -
> /* Protects mon_fdsets */
> static QemuMutex mon_fdsets_lock;
> static QLIST_HEAD(, MonFdset) mon_fdsets;
>
> -int mon_refcount;
> -
> static mon_cmd_t info_cmds[];
>
> __thread Monitor *cur_mon;
> @@ -161,32 +131,6 @@ __thread Monitor *cur_mon;
> static void monitor_command_cb(void *opaque, const char *cmdline,
> void *readline_opaque);
>
> -/**
> - * Is @mon is using readline?
> - * Note: not all HMP monitors use readline, e.g., gdbserver has a
> - * non-interactive HMP monitor, so readline is not used there.
> - */
> -static inline bool monitor_uses_readline(const Monitor *mon)
> -{
> - return mon->flags & MONITOR_USE_READLINE;
> -}
> -
> -static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
> -{
> - return !monitor_is_qmp(mon) && !monitor_uses_readline(mon);
> -}
> -
> -/*
> - * Return the clock to use for recording an event's time.
> - * It's QEMU_CLOCK_REALTIME, except for qtests it's
> - * QEMU_CLOCK_VIRTUAL, to support testing rate limits.
> - * Beware: result is invalid before configure_accelerator().
> - */
> -static inline QEMUClockType monitor_get_event_clock(void)
> -{
> - return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME;
> -}
> -
> /**
> * Is the current monitor, if any, a QMP monitor?
> */
> @@ -220,355 +164,6 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
> }
>
>
> -static void monitor_flush_locked(Monitor *mon);
> -
> -static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
> - void *opaque)
> -{
> - Monitor *mon = opaque;
> -
> - qemu_mutex_lock(&mon->mon_lock);
> - mon->out_watch = 0;
> - monitor_flush_locked(mon);
> - qemu_mutex_unlock(&mon->mon_lock);
> - return FALSE;
> -}
> -
> -/* Caller must hold mon->mon_lock */
> -static void monitor_flush_locked(Monitor *mon)
> -{
> - int rc;
> - size_t len;
> - const char *buf;
> -
> - if (mon->skip_flush) {
> - return;
> - }
> -
> - buf = qstring_get_str(mon->outbuf);
> - len = qstring_get_length(mon->outbuf);
> -
> - if (len && !mon->mux_out) {
> - rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len);
> - if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
> - /* all flushed or error */
> - qobject_unref(mon->outbuf);
> - mon->outbuf = qstring_new();
> - return;
> - }
> - if (rc > 0) {
> - /* partial write */
> - QString *tmp = qstring_from_str(buf + rc);
> - qobject_unref(mon->outbuf);
> - mon->outbuf = tmp;
> - }
> - if (mon->out_watch == 0) {
> - mon->out_watch =
> - qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP,
> - monitor_unblocked, mon);
> - }
> - }
> -}
> -
> -void monitor_flush(Monitor *mon)
> -{
> - qemu_mutex_lock(&mon->mon_lock);
> - monitor_flush_locked(mon);
> - qemu_mutex_unlock(&mon->mon_lock);
> -}
> -
> -/* flush at every end of line */
> -int monitor_puts(Monitor *mon, const char *str)
> -{
> - int i;
> - char c;
> -
> - qemu_mutex_lock(&mon->mon_lock);
> - for (i = 0; str[i]; i++) {
> - c = str[i];
> - if (c == '\n') {
> - qstring_append_chr(mon->outbuf, '\r');
> - }
> - qstring_append_chr(mon->outbuf, c);
> - if (c == '\n') {
> - monitor_flush_locked(mon);
> - }
> - }
> - qemu_mutex_unlock(&mon->mon_lock);
> -
> - return i;
> -}
> -
> -int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
> -{
> - char *buf;
> - int n;
> -
> - if (!mon)
> - return -1;
> -
> - if (monitor_is_qmp(mon)) {
> - return -1;
> - }
> -
> - buf = g_strdup_vprintf(fmt, ap);
> - n = monitor_puts(mon, buf);
> - g_free(buf);
> - return n;
> -}
> -
> -int monitor_printf(Monitor *mon, const char *fmt, ...)
> -{
> - int ret;
> -
> - va_list ap;
> - va_start(ap, fmt);
> - ret = monitor_vprintf(mon, fmt, ap);
> - va_end(ap);
> - return ret;
> -}
> -
> -static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
> - /* Limit guest-triggerable events to 1 per second */
> - [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
> - [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS },
> - [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS },
> - [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS },
> - [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS },
> - [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS },
> -};
> -
> -/*
> - * Broadcast an event to all monitors.
> - * @qdict is the event object. Its member "event" must match @event.
> - * Caller must hold monitor_lock.
> - */
> -static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
> -{
> - Monitor *mon;
> - MonitorQMP *qmp_mon;
> -
> - trace_monitor_protocol_event_emit(event, qdict);
> - QTAILQ_FOREACH(mon, &mon_list, entry) {
> - if (!monitor_is_qmp(mon)) {
> - continue;
> - }
> -
> - qmp_mon = container_of(mon, MonitorQMP, common);
> - if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
> - qmp_send_response(qmp_mon, qdict);
> - }
> - }
> -}
> -
> -static void monitor_qapi_event_handler(void *opaque);
> -
> -/*
> - * Queue a new event for emission to Monitor instances,
> - * applying any rate limiting if required.
> - */
> -static void
> -monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict)
> -{
> - MonitorQAPIEventConf *evconf;
> - MonitorQAPIEventState *evstate;
> -
> - assert(event < QAPI_EVENT__MAX);
> - evconf = &monitor_qapi_event_conf[event];
> - trace_monitor_protocol_event_queue(event, qdict, evconf->rate);
> -
> - qemu_mutex_lock(&monitor_lock);
> -
> - if (!evconf->rate) {
> - /* Unthrottled event */
> - monitor_qapi_event_emit(event, qdict);
> - } else {
> - QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
> - MonitorQAPIEventState key = { .event = event, .data = data };
> -
> - evstate = g_hash_table_lookup(monitor_qapi_event_state, &key);
> - assert(!evstate || timer_pending(evstate->timer));
> -
> - if (evstate) {
> - /*
> - * Timer is pending for (at least) evconf->rate ns after
> - * last send. Store event for sending when timer fires,
> - * replacing a prior stored event if any.
> - */
> - qobject_unref(evstate->qdict);
> - evstate->qdict = qobject_ref(qdict);
> - } else {
> - /*
> - * Last send was (at least) evconf->rate ns ago.
> - * Send immediately, and arm the timer to call
> - * monitor_qapi_event_handler() in evconf->rate ns. Any
> - * events arriving before then will be delayed until then.
> - */
> - int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
> -
> - monitor_qapi_event_emit(event, qdict);
> -
> - evstate = g_new(MonitorQAPIEventState, 1);
> - evstate->event = event;
> - evstate->data = qobject_ref(data);
> - evstate->qdict = NULL;
> - evstate->timer = timer_new_ns(monitor_get_event_clock(),
> - monitor_qapi_event_handler,
> - evstate);
> - g_hash_table_add(monitor_qapi_event_state, evstate);
> - timer_mod_ns(evstate->timer, now + evconf->rate);
> - }
> - }
> -
> - qemu_mutex_unlock(&monitor_lock);
> -}
> -
> -void qapi_event_emit(QAPIEvent event, QDict *qdict)
> -{
> - /*
> - * monitor_qapi_event_queue_no_reenter() is not reentrant: it
> - * would deadlock on monitor_lock. Work around by queueing
> - * events in thread-local storage.
> - * TODO: remove this, make it re-enter safe.
> - */
> - typedef struct MonitorQapiEvent {
> - QAPIEvent event;
> - QDict *qdict;
> - QSIMPLEQ_ENTRY(MonitorQapiEvent) entry;
> - } MonitorQapiEvent;
> - static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue;
> - static __thread bool reentered;
> - MonitorQapiEvent *ev;
> -
> - if (!reentered) {
> - QSIMPLEQ_INIT(&event_queue);
> - }
> -
> - ev = g_new(MonitorQapiEvent, 1);
> - ev->qdict = qobject_ref(qdict);
> - ev->event = event;
> - QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry);
> - if (reentered) {
> - return;
> - }
> -
> - reentered = true;
> -
> - while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) {
> - QSIMPLEQ_REMOVE_HEAD(&event_queue, entry);
> - monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict);
> - qobject_unref(ev->qdict);
> - g_free(ev);
> - }
> -
> - reentered = false;
> -}
> -
> -/*
> - * This function runs evconf->rate ns after sending a throttled
> - * event.
> - * If another event has since been stored, send it.
> - */
> -static void monitor_qapi_event_handler(void *opaque)
> -{
> - MonitorQAPIEventState *evstate = opaque;
> - MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event];
> -
> - trace_monitor_protocol_event_handler(evstate->event, evstate->qdict);
> - qemu_mutex_lock(&monitor_lock);
> -
> - if (evstate->qdict) {
> - int64_t now = qemu_clock_get_ns(monitor_get_event_clock());
> -
> - monitor_qapi_event_emit(evstate->event, evstate->qdict);
> - qobject_unref(evstate->qdict);
> - evstate->qdict = NULL;
> - timer_mod_ns(evstate->timer, now + evconf->rate);
> - } else {
> - g_hash_table_remove(monitor_qapi_event_state, evstate);
> - qobject_unref(evstate->data);
> - timer_free(evstate->timer);
> - g_free(evstate);
> - }
> -
> - qemu_mutex_unlock(&monitor_lock);
> -}
> -
> -static unsigned int qapi_event_throttle_hash(const void *key)
> -{
> - const MonitorQAPIEventState *evstate = key;
> - unsigned int hash = evstate->event * 255;
> -
> - if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) {
> - hash += g_str_hash(qdict_get_str(evstate->data, "id"));
> - }
> -
> - if (evstate->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
> - hash += g_str_hash(qdict_get_str(evstate->data, "node-name"));
> - }
> -
> - return hash;
> -}
> -
> -static gboolean qapi_event_throttle_equal(const void *a, const void *b)
> -{
> - const MonitorQAPIEventState *eva = a;
> - const MonitorQAPIEventState *evb = b;
> -
> - if (eva->event != evb->event) {
> - return FALSE;
> - }
> -
> - if (eva->event == QAPI_EVENT_VSERPORT_CHANGE) {
> - return !strcmp(qdict_get_str(eva->data, "id"),
> - qdict_get_str(evb->data, "id"));
> - }
> -
> - if (eva->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
> - return !strcmp(qdict_get_str(eva->data, "node-name"),
> - qdict_get_str(evb->data, "node-name"));
> - }
> -
> - return TRUE;
> -}
> -
> -static void monitor_qapi_event_init(void)
> -{
> - monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash,
> - qapi_event_throttle_equal);
> -}
> -
> -static void monitor_iothread_init(void);
> -
> -void monitor_data_init(Monitor *mon, int flags, bool skip_flush,
> - bool use_io_thread)
> -{
> - if (use_io_thread && !mon_iothread) {
> - monitor_iothread_init();
> - }
> - memset(mon, 0, sizeof(Monitor));
> - qemu_mutex_init(&mon->mon_lock);
> - mon->outbuf = qstring_new();
> - mon->skip_flush = skip_flush;
> - mon->use_io_thread = use_io_thread;
> - mon->flags = flags;
> -}
> -
> -static void monitor_data_destroy(Monitor *mon)
> -{
> - g_free(mon->mon_cpu_path);
> - qemu_chr_fe_deinit(&mon->chr, false);
> - if (monitor_is_qmp(mon)) {
> - MonitorQMP *qmp_mon = container_of(mon, MonitorQMP, common);
> - monitor_data_destroy_qmp(qmp_mon);
> - } else {
> - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> - readline_free(hmp_mon->rs);
> - }
> - qobject_unref(mon->outbuf);
> - qemu_mutex_destroy(&mon->mon_lock);
> -}
> -
> char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
> int64_t cpu_index, Error **errp)
> {
> @@ -2738,13 +2333,6 @@ void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
> }
> }
>
> -int monitor_can_read(void *opaque)
> -{
> - Monitor *mon = opaque;
> -
> - return !atomic_mb_read(&mon->suspend_cnt);
> -}
> -
> static void monitor_command_cb(void *opaque, const char *cmdline,
> void *readline_opaque)
> {
> @@ -2755,60 +2343,6 @@ static void monitor_command_cb(void *opaque, const char *cmdline,
> monitor_resume(&mon->common);
> }
>
> -int monitor_suspend(Monitor *mon)
> -{
> - if (monitor_is_hmp_non_interactive(mon)) {
> - return -ENOTTY;
> - }
> -
> - atomic_inc(&mon->suspend_cnt);
> -
> - if (mon->use_io_thread) {
> - /*
> - * Kick I/O thread to make sure this takes effect. It'll be
> - * evaluated again in prepare() of the watch object.
> - */
> - aio_notify(iothread_get_aio_context(mon_iothread));
> - }
> -
> - trace_monitor_suspend(mon, 1);
> - return 0;
> -}
> -
> -static void monitor_accept_input(void *opaque)
> -{
> - Monitor *mon = opaque;
> -
> - qemu_chr_fe_accept_input(&mon->chr);
> -}
> -
> -void monitor_resume(Monitor *mon)
> -{
> - if (monitor_is_hmp_non_interactive(mon)) {
> - return;
> - }
> -
> - if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
> - AioContext *ctx;
> -
> - if (mon->use_io_thread) {
> - ctx = iothread_get_aio_context(mon_iothread);
> - } else {
> - ctx = qemu_get_aio_context();
> - }
> -
> - if (!monitor_is_qmp(mon)) {
> - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
> - assert(hmp_mon->rs);
> - readline_show_prompt(hmp_mon->rs);
> - }
> -
> - aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
> - }
> -
> - trace_monitor_suspend(mon, -1);
> -}
> -
> static int
> compare_mon_cmd(const void *a, const void *b)
> {
> @@ -2828,27 +2362,12 @@ static void sortcmdlist(void)
> qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd);
> }
>
> -static void monitor_iothread_init(void)
> -{
> - mon_iothread = iothread_create("mon_iothread", &error_abort);
> -}
> -
> void monitor_init_globals(void)
> {
> + monitor_init_globals_core();
> monitor_init_qmp_commands();
> - monitor_qapi_event_init();
> sortcmdlist();
> - qemu_mutex_init(&monitor_lock);
> qemu_mutex_init(&mon_fdsets_lock);
> -
> - /*
> - * 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);
> }
>
> /*
> @@ -2873,90 +2392,6 @@ int error_vprintf_unless_qmp(const char *fmt, va_list ap)
> return -1;
> }
>
> -void monitor_list_append(Monitor *mon)
> -{
> - qemu_mutex_lock(&monitor_lock);
> - /*
> - * This prevents inserting new monitors during monitor_cleanup().
> - * A cleaner solution would involve the main thread telling other
> - * threads to terminate, waiting for their termination.
> - */
> - if (!monitor_destroyed) {
> - QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
> - mon = NULL;
> - }
> - qemu_mutex_unlock(&monitor_lock);
> -
> - if (mon) {
> - monitor_data_destroy(mon);
> - g_free(mon);
> - }
> -}
> -
> -void monitor_init(Chardev *chr, int flags)
> -{
> - if (flags & MONITOR_USE_CONTROL) {
> - monitor_init_qmp(chr, flags);
> - } else {
> - monitor_init_hmp(chr, flags);
> - }
> -}
> -
> -void monitor_cleanup(void)
> -{
> - /*
> - * We need to explicitly stop the I/O thread (but not destroy it),
> - * clean up the monitor resources, then destroy the I/O thread since
> - * we need to unregister from chardev below in
> - * monitor_data_destroy(), and chardev is not thread-safe yet
> - */
> - if (mon_iothread) {
> - iothread_stop(mon_iothread);
> - }
> -
> - /* Flush output buffers and destroy monitors */
> - qemu_mutex_lock(&monitor_lock);
> - monitor_destroyed = true;
> - while (!QTAILQ_EMPTY(&mon_list)) {
> - Monitor *mon = QTAILQ_FIRST(&mon_list);
> - QTAILQ_REMOVE(&mon_list, mon, entry);
> - /* Permit QAPI event emission from character frontend release */
> - qemu_mutex_unlock(&monitor_lock);
> - monitor_flush(mon);
> - monitor_data_destroy(mon);
> - qemu_mutex_lock(&monitor_lock);
> - g_free(mon);
> - }
> - 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;
> - if (mon_iothread) {
> - iothread_destroy(mon_iothread);
> - mon_iothread = NULL;
> - }
> -}
> -
> -QemuOptsList qemu_mon_opts = {
> - .name = "mon",
> - .implied_opt_name = "chardev",
> - .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
> - .desc = {
> - {
> - .name = "mode",
> - .type = QEMU_OPT_STRING,
> - },{
> - .name = "chardev",
> - .type = QEMU_OPT_STRING,
> - },{
> - .name = "pretty",
> - .type = QEMU_OPT_BOOL,
> - },
> - { /* end of list */ }
> - },
> -};
> -
> HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp)
> {
> MachineState *ms = MACHINE(qdev_get_machine());
> diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
> index 48c73eed51..c8dff5e4b5 100644
> --- a/monitor/Makefile.objs
> +++ b/monitor/Makefile.objs
> @@ -1,2 +1,2 @@
> obj-y += misc.o
> -common-obj-y += qmp.o hmp.o
> +common-obj-y += core.o qmp.o hmp.o
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c
2019-06-07 17:29 ` Dr. David Alan Gilbert
@ 2019-06-11 9:22 ` Kevin Wolf
2019-06-11 9:25 ` Dr. David Alan Gilbert
0 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-11 9:22 UTC (permalink / raw)
To: Dr. David Alan Gilbert; +Cc: qemu-devel, qemu-block, armbru
Am 07.06.2019 um 19:29 hat Dr. David Alan Gilbert geschrieben:
> * Kevin Wolf (kwolf@redhat.com) wrote:
> > Move the monitor core infrastructure from monitor/misc.c to
> > monitor/core.c. This is code that can be shared for all targets, so
> > compile it only once.
> >
> > What remains in monitor/misc.c after this patch is mostly monitor
> > command implementations and code that requires a system emulator or is
> > even target-dependent.
> >
> > The amount of function and particularly extern variables in
> > monitor_int.h is probably a bit larger than it needs to be, but this way
> > no non-trivial code modifications are needed. The interfaces between all
> > monitor parts can be cleaned up later.
> >
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>
> OK, but can you call it anything other than core.* - I regularly end up
> deleting things like that!
Oh, I didn't even think of this kind of core.*!
I imagine in practice it wouldn't be so bad to have a monitor/core.c
because it's in a subdirectory, and it's under version control anyway.
We already seem to have quite a few of them in subdirectories:
./hw/acpi/core.c
./hw/bt/core.c
./hw/cpu/core.c
./hw/i2c/core.c
./hw/ide/core.c
./hw/sd/core.c
./hw/usb/core.c
But I'll gladly rename it if I can find a good name. Do you have any
suggestions? Maybe just monitor/monitor.c?
Kevin
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c
2019-06-11 9:22 ` Kevin Wolf
@ 2019-06-11 9:25 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-11 9:25 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
* Kevin Wolf (kwolf@redhat.com) wrote:
> Am 07.06.2019 um 19:29 hat Dr. David Alan Gilbert geschrieben:
> > * Kevin Wolf (kwolf@redhat.com) wrote:
> > > Move the monitor core infrastructure from monitor/misc.c to
> > > monitor/core.c. This is code that can be shared for all targets, so
> > > compile it only once.
> > >
> > > What remains in monitor/misc.c after this patch is mostly monitor
> > > command implementations and code that requires a system emulator or is
> > > even target-dependent.
> > >
> > > The amount of function and particularly extern variables in
> > > monitor_int.h is probably a bit larger than it needs to be, but this way
> > > no non-trivial code modifications are needed. The interfaces between all
> > > monitor parts can be cleaned up later.
> > >
> > > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> >
> > OK, but can you call it anything other than core.* - I regularly end up
> > deleting things like that!
>
> Oh, I didn't even think of this kind of core.*!
>
> I imagine in practice it wouldn't be so bad to have a monitor/core.c
> because it's in a subdirectory, and it's under version control anyway.
> We already seem to have quite a few of them in subdirectories:
>
> ./hw/acpi/core.c
> ./hw/bt/core.c
> ./hw/cpu/core.c
> ./hw/i2c/core.c
> ./hw/ide/core.c
> ./hw/sd/core.c
> ./hw/usb/core.c
Yes, they all annoy me in the same way :-)
> But I'll gladly rename it if I can find a good name. Do you have any
> suggestions? Maybe just monitor/monitor.c?
Yes that's fine, thanks!
Dave
> Kevin
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c Kevin Wolf
2019-06-07 17:29 ` Dr. David Alan Gilbert
@ 2019-06-07 17:30 ` Dr. David Alan Gilbert
1 sibling, 0 replies; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 17:30 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel, qemu-block, armbru
Oh, can you also fix up the paths in writing-qmp-commands.txt?
Thanks for this split!
Dave
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (9 preceding siblings ...)
2019-06-07 13:54 ` [Qemu-devel] [RFC PATCH 10/10] monitor: Split out monitor/core.c Kevin Wolf
@ 2019-06-07 14:03 ` Daniel P. Berrangé
2019-06-07 14:25 ` Kevin Wolf
2019-06-07 16:48 ` [Qemu-devel] " no-reply
2019-06-07 20:42 ` no-reply
12 siblings, 1 reply; 31+ messages in thread
From: Daniel P. Berrangé @ 2019-06-07 14:03 UTC (permalink / raw)
To: Kevin Wolf; +Cc: dgilbert, qemu-devel, qemu-block, armbru
On Fri, Jun 07, 2019 at 03:54:20PM +0200, Kevin Wolf wrote:
> monitor.c mixes a lot of different things in a single file: The core
> monitor infrastructure, HMP infrastrcture, QMP infrastructure, and the
> implementation of several HMP and QMP commands. Almost worse, struct
> Monitor mixes state for HMP, for QMP, and state actually shared between
> all monitors. monitor.c must be linked with a system emulator and even
> requires per-target compilation because some of the commands it
> implements access system emulator state.
>
> The reason why I care about this is that I'm working on a protoype for a
> storage daemon, which wants to use QMP (but probably not HMP) and
> obviously doesn't have any system emulator state. So I'm interested in
> some core monitor parts that can be linked to non-system-emulator tools.
>
> This series first creates separate structs MonitorQMP and MonitorHMP
> which inherit from Monitor, and then moves the associated infrastructure
> code into separate source files.
>
> While the split is probably not perfect, I think it's an improvement of
> the current state even for QEMU proper, and it's good enough so I can
> link my storage daemon against just monitor/core.o and monitor/qmp.o and
> get a useless QMP monitor that parses the JSON input and rejects
> everything as an unknown command.
>
> Next I'll try to teach it a subset of QMP commands that can actually be
> supported in a tool, but while there will be a few follow-up patches to
> achieve this, I don't expect that this work will bring up much that
> needs to be changed in the splitting process done in this series.
>
> Kevin Wolf (10):
> monitor: Remove unused password prompting fields
> monitor: Split monitor_init in HMP and QMP function
> monitor: Make MonitorQMP a child class of Monitor
> monitor: Create MonitorHMP with readline state
> monitor: Move cmd_table to MonitorHMP
> Move monitor.c to monitor/misc.c
> monitor: Create monitor_int.h with common definitions
> monitor: Split out monitor/qmp.c
> monitor: Split out monitor/hmp.c
> monitor: Split out monitor/core.c
>
> include/monitor/monitor.h | 8 +-
> monitor/monitor_int.h | 207 ++
> hmp.c | 4 +-
> monitor.c | 4727 -------------------------------------
> monitor/core.c | 604 +++++
> monitor/hmp.c | 1351 +++++++++++
> monitor/misc.c | 2406 +++++++++++++++++++
> monitor/qmp.c | 404 ++++
> Makefile.objs | 1 +
> Makefile.target | 3 +-
> monitor/Makefile.objs | 2 +
It will be nice to have the monitor code split up a bit more.
I'm not a fan, however, of having both $ROOT/qmp.c and $ROOT/monitor/qmp.c
Likwise $ROOT/hmp.c and $ROOT/monitor/hmp.c. Can we move those other
existing files out of the root dir, into monitor/, so we don't have two
files with the same name in different dirs.
Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 14:03 ` [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Daniel P. Berrangé
@ 2019-06-07 14:25 ` Kevin Wolf
2019-06-07 14:31 ` Daniel P. Berrangé
0 siblings, 1 reply; 31+ messages in thread
From: Kevin Wolf @ 2019-06-07 14:25 UTC (permalink / raw)
To: Daniel P. Berrangé; +Cc: dgilbert, qemu-devel, qemu-block, armbru
Am 07.06.2019 um 16:03 hat Daniel P. Berrangé geschrieben:
> On Fri, Jun 07, 2019 at 03:54:20PM +0200, Kevin Wolf wrote:
> > monitor.c mixes a lot of different things in a single file: The core
> > monitor infrastructure, HMP infrastrcture, QMP infrastructure, and the
> > implementation of several HMP and QMP commands. Almost worse, struct
> > Monitor mixes state for HMP, for QMP, and state actually shared between
> > all monitors. monitor.c must be linked with a system emulator and even
> > requires per-target compilation because some of the commands it
> > implements access system emulator state.
> >
> > The reason why I care about this is that I'm working on a protoype for a
> > storage daemon, which wants to use QMP (but probably not HMP) and
> > obviously doesn't have any system emulator state. So I'm interested in
> > some core monitor parts that can be linked to non-system-emulator tools.
> >
> > This series first creates separate structs MonitorQMP and MonitorHMP
> > which inherit from Monitor, and then moves the associated infrastructure
> > code into separate source files.
> >
> > While the split is probably not perfect, I think it's an improvement of
> > the current state even for QEMU proper, and it's good enough so I can
> > link my storage daemon against just monitor/core.o and monitor/qmp.o and
> > get a useless QMP monitor that parses the JSON input and rejects
> > everything as an unknown command.
> >
> > Next I'll try to teach it a subset of QMP commands that can actually be
> > supported in a tool, but while there will be a few follow-up patches to
> > achieve this, I don't expect that this work will bring up much that
> > needs to be changed in the splitting process done in this series.
> >
> > Kevin Wolf (10):
> > monitor: Remove unused password prompting fields
> > monitor: Split monitor_init in HMP and QMP function
> > monitor: Make MonitorQMP a child class of Monitor
> > monitor: Create MonitorHMP with readline state
> > monitor: Move cmd_table to MonitorHMP
> > Move monitor.c to monitor/misc.c
> > monitor: Create monitor_int.h with common definitions
> > monitor: Split out monitor/qmp.c
> > monitor: Split out monitor/hmp.c
> > monitor: Split out monitor/core.c
> >
> > include/monitor/monitor.h | 8 +-
> > monitor/monitor_int.h | 207 ++
> > hmp.c | 4 +-
> > monitor.c | 4727 -------------------------------------
> > monitor/core.c | 604 +++++
> > monitor/hmp.c | 1351 +++++++++++
> > monitor/misc.c | 2406 +++++++++++++++++++
> > monitor/qmp.c | 404 ++++
> > Makefile.objs | 1 +
> > Makefile.target | 3 +-
> > monitor/Makefile.objs | 2 +
>
> It will be nice to have the monitor code split up a bit more.
>
> I'm not a fan, however, of having both $ROOT/qmp.c and $ROOT/monitor/qmp.c
> Likwise $ROOT/hmp.c and $ROOT/monitor/hmp.c. Can we move those other
> existing files out of the root dir, into monitor/, so we don't have two
> files with the same name in different dirs.
$ROOT/hmp.c and $ROOT/qmp.c contain various command implementations,
just as $ROOT/monitor/misc.c. This is still a bit of a mess. I'll have
to address this at least partially in the next step because I need to
separate commands that can be linked with tools from those that require
a system emulator.
My plan involves at least creating some monitor/qmp-cmds-*.c, which
might already make $ROOT/qmp.c empty. Even though I don't strictly need
it, there's no reason not to do the same for HMP, too. In any case, I'd
rather address this in a separate follow-up series.
But if people prefer, I can move the existing files in the root
directory to monitor/{qmp,hmp}-cmds.c temporarily in this series and
then work from there with follow-ups until they are empty (or maybe I
don't even have to make them completely empty then).
Kevin
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 14:25 ` Kevin Wolf
@ 2019-06-07 14:31 ` Daniel P. Berrangé
2019-06-07 15:35 ` Dr. David Alan Gilbert
0 siblings, 1 reply; 31+ messages in thread
From: Daniel P. Berrangé @ 2019-06-07 14:31 UTC (permalink / raw)
To: Kevin Wolf; +Cc: dgilbert, qemu-devel, qemu-block, armbru
On Fri, Jun 07, 2019 at 04:25:14PM +0200, Kevin Wolf wrote:
> Am 07.06.2019 um 16:03 hat Daniel P. Berrangé geschrieben:
> > On Fri, Jun 07, 2019 at 03:54:20PM +0200, Kevin Wolf wrote:
> > > monitor.c mixes a lot of different things in a single file: The core
> > > monitor infrastructure, HMP infrastrcture, QMP infrastructure, and the
> > > implementation of several HMP and QMP commands. Almost worse, struct
> > > Monitor mixes state for HMP, for QMP, and state actually shared between
> > > all monitors. monitor.c must be linked with a system emulator and even
> > > requires per-target compilation because some of the commands it
> > > implements access system emulator state.
> > >
> > > The reason why I care about this is that I'm working on a protoype for a
> > > storage daemon, which wants to use QMP (but probably not HMP) and
> > > obviously doesn't have any system emulator state. So I'm interested in
> > > some core monitor parts that can be linked to non-system-emulator tools.
> > >
> > > This series first creates separate structs MonitorQMP and MonitorHMP
> > > which inherit from Monitor, and then moves the associated infrastructure
> > > code into separate source files.
> > >
> > > While the split is probably not perfect, I think it's an improvement of
> > > the current state even for QEMU proper, and it's good enough so I can
> > > link my storage daemon against just monitor/core.o and monitor/qmp.o and
> > > get a useless QMP monitor that parses the JSON input and rejects
> > > everything as an unknown command.
> > >
> > > Next I'll try to teach it a subset of QMP commands that can actually be
> > > supported in a tool, but while there will be a few follow-up patches to
> > > achieve this, I don't expect that this work will bring up much that
> > > needs to be changed in the splitting process done in this series.
> > >
> > > Kevin Wolf (10):
> > > monitor: Remove unused password prompting fields
> > > monitor: Split monitor_init in HMP and QMP function
> > > monitor: Make MonitorQMP a child class of Monitor
> > > monitor: Create MonitorHMP with readline state
> > > monitor: Move cmd_table to MonitorHMP
> > > Move monitor.c to monitor/misc.c
> > > monitor: Create monitor_int.h with common definitions
> > > monitor: Split out monitor/qmp.c
> > > monitor: Split out monitor/hmp.c
> > > monitor: Split out monitor/core.c
> > >
> > > include/monitor/monitor.h | 8 +-
> > > monitor/monitor_int.h | 207 ++
> > > hmp.c | 4 +-
> > > monitor.c | 4727 -------------------------------------
> > > monitor/core.c | 604 +++++
> > > monitor/hmp.c | 1351 +++++++++++
> > > monitor/misc.c | 2406 +++++++++++++++++++
> > > monitor/qmp.c | 404 ++++
> > > Makefile.objs | 1 +
> > > Makefile.target | 3 +-
> > > monitor/Makefile.objs | 2 +
> >
> > It will be nice to have the monitor code split up a bit more.
> >
> > I'm not a fan, however, of having both $ROOT/qmp.c and $ROOT/monitor/qmp.c
> > Likwise $ROOT/hmp.c and $ROOT/monitor/hmp.c. Can we move those other
> > existing files out of the root dir, into monitor/, so we don't have two
> > files with the same name in different dirs.
>
> $ROOT/hmp.c and $ROOT/qmp.c contain various command implementations,
> just as $ROOT/monitor/misc.c. This is still a bit of a mess. I'll have
> to address this at least partially in the next step because I need to
> separate commands that can be linked with tools from those that require
> a system emulator.
>
> My plan involves at least creating some monitor/qmp-cmds-*.c, which
> might already make $ROOT/qmp.c empty. Even though I don't strictly need
> it, there's no reason not to do the same for HMP, too. In any case, I'd
> rather address this in a separate follow-up series.
Ok, if you have a plan for this, that's fine with me.
> But if people prefer, I can move the existing files in the root
> directory to monitor/{qmp,hmp}-cmds.c temporarily in this series and
> then work from there with follow-ups until they are empty (or maybe I
> don't even have to make them completely empty then).
A plain rename like this won't hurt in the meantime.
Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 14:31 ` Daniel P. Berrangé
@ 2019-06-07 15:35 ` Dr. David Alan Gilbert
2019-06-07 18:11 ` [Qemu-devel] [Qemu-block] " Eric Blake
0 siblings, 1 reply; 31+ messages in thread
From: Dr. David Alan Gilbert @ 2019-06-07 15:35 UTC (permalink / raw)
To: Daniel P. Berrangé; +Cc: Kevin Wolf, qemu-devel, qemu-block, armbru
* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Fri, Jun 07, 2019 at 04:25:14PM +0200, Kevin Wolf wrote:
> > Am 07.06.2019 um 16:03 hat Daniel P. Berrangé geschrieben:
> > > On Fri, Jun 07, 2019 at 03:54:20PM +0200, Kevin Wolf wrote:
> > > > monitor.c mixes a lot of different things in a single file: The core
> > > > monitor infrastructure, HMP infrastrcture, QMP infrastructure, and the
> > > > implementation of several HMP and QMP commands. Almost worse, struct
> > > > Monitor mixes state for HMP, for QMP, and state actually shared between
> > > > all monitors. monitor.c must be linked with a system emulator and even
> > > > requires per-target compilation because some of the commands it
> > > > implements access system emulator state.
> > > >
> > > > The reason why I care about this is that I'm working on a protoype for a
> > > > storage daemon, which wants to use QMP (but probably not HMP) and
> > > > obviously doesn't have any system emulator state. So I'm interested in
> > > > some core monitor parts that can be linked to non-system-emulator tools.
> > > >
> > > > This series first creates separate structs MonitorQMP and MonitorHMP
> > > > which inherit from Monitor, and then moves the associated infrastructure
> > > > code into separate source files.
> > > >
> > > > While the split is probably not perfect, I think it's an improvement of
> > > > the current state even for QEMU proper, and it's good enough so I can
> > > > link my storage daemon against just monitor/core.o and monitor/qmp.o and
> > > > get a useless QMP monitor that parses the JSON input and rejects
> > > > everything as an unknown command.
> > > >
> > > > Next I'll try to teach it a subset of QMP commands that can actually be
> > > > supported in a tool, but while there will be a few follow-up patches to
> > > > achieve this, I don't expect that this work will bring up much that
> > > > needs to be changed in the splitting process done in this series.
> > > >
> > > > Kevin Wolf (10):
> > > > monitor: Remove unused password prompting fields
> > > > monitor: Split monitor_init in HMP and QMP function
> > > > monitor: Make MonitorQMP a child class of Monitor
> > > > monitor: Create MonitorHMP with readline state
> > > > monitor: Move cmd_table to MonitorHMP
> > > > Move monitor.c to monitor/misc.c
> > > > monitor: Create monitor_int.h with common definitions
> > > > monitor: Split out monitor/qmp.c
> > > > monitor: Split out monitor/hmp.c
> > > > monitor: Split out monitor/core.c
> > > >
> > > > include/monitor/monitor.h | 8 +-
> > > > monitor/monitor_int.h | 207 ++
> > > > hmp.c | 4 +-
> > > > monitor.c | 4727 -------------------------------------
> > > > monitor/core.c | 604 +++++
> > > > monitor/hmp.c | 1351 +++++++++++
> > > > monitor/misc.c | 2406 +++++++++++++++++++
> > > > monitor/qmp.c | 404 ++++
> > > > Makefile.objs | 1 +
> > > > Makefile.target | 3 +-
> > > > monitor/Makefile.objs | 2 +
> > >
> > > It will be nice to have the monitor code split up a bit more.
> > >
> > > I'm not a fan, however, of having both $ROOT/qmp.c and $ROOT/monitor/qmp.c
> > > Likwise $ROOT/hmp.c and $ROOT/monitor/hmp.c. Can we move those other
> > > existing files out of the root dir, into monitor/, so we don't have two
> > > files with the same name in different dirs.
> >
> > $ROOT/hmp.c and $ROOT/qmp.c contain various command implementations,
> > just as $ROOT/monitor/misc.c. This is still a bit of a mess. I'll have
> > to address this at least partially in the next step because I need to
> > separate commands that can be linked with tools from those that require
> > a system emulator.
> >
> > My plan involves at least creating some monitor/qmp-cmds-*.c, which
> > might already make $ROOT/qmp.c empty. Even though I don't strictly need
> > it, there's no reason not to do the same for HMP, too. In any case, I'd
> > rather address this in a separate follow-up series.
>
> Ok, if you have a plan for this, that's fine with me.
>
> > But if people prefer, I can move the existing files in the root
> > directory to monitor/{qmp,hmp}-cmds.c temporarily in this series and
> > then work from there with follow-ups until they are empty (or maybe I
> > don't even have to make them completely empty then).
>
> A plain rename like this won't hurt in the meantime.
Yeh agreed, my brain hurts too much with files of the same name.
Dave
> Regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [Qemu-block] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 15:35 ` Dr. David Alan Gilbert
@ 2019-06-07 18:11 ` Eric Blake
0 siblings, 0 replies; 31+ messages in thread
From: Eric Blake @ 2019-06-07 18:11 UTC (permalink / raw)
To: Dr. David Alan Gilbert, Daniel P. Berrangé
Cc: Kevin Wolf, qemu-devel, qemu-block, armbru
[-- Attachment #1.1: Type: text/plain, Size: 718 bytes --]
On 6/7/19 10:35 AM, Dr. David Alan Gilbert wrote:
>>> But if people prefer, I can move the existing files in the root
>>> directory to monitor/{qmp,hmp}-cmds.c temporarily in this series and
>>> then work from there with follow-ups until they are empty (or maybe I
>>> don't even have to make them completely empty then).
>>
>> A plain rename like this won't hurt in the meantime.
>
> Yeh agreed, my brain hurts too much with files of the same name.
Not just that, but in gdb, it's harder to set breakpoints of the form
file.c:line if file.c is not unique to the image.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3226
Virtualization: qemu.org | libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (10 preceding siblings ...)
2019-06-07 14:03 ` [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Daniel P. Berrangé
@ 2019-06-07 16:48 ` no-reply
2019-06-07 20:42 ` no-reply
12 siblings, 0 replies; 31+ messages in thread
From: no-reply @ 2019-06-07 16:48 UTC (permalink / raw)
To: kwolf; +Cc: kwolf, dgilbert, qemu-devel, qemu-block, armbru
Patchew URL: https://patchew.org/QEMU/20190607135430.22149-1-kwolf@redhat.com/
Hi,
This series seems to have some coding style problems. See output below for
more information:
Subject: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
Message-id: 20190607135430.22149-1-kwolf@redhat.com
Type: series
=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===
Switched to a new branch 'test'
dbb0f22 monitor: Split out monitor/core.c
54736ce monitor: Split out monitor/hmp.c
9a88f45 monitor: Split out monitor/qmp.c
d3d699e monitor: Create monitor_int.h with common definitions
febc650 Move monitor.c to monitor/misc.c
8f3621b monitor: Move cmd_table to MonitorHMP
75ae78b monitor: Create MonitorHMP with readline state
6ab73c3 monitor: Make MonitorQMP a child class of Monitor
95f9e11 monitor: Split monitor_init in HMP and QMP function
d319053 monitor: Remove unused password prompting fields
=== OUTPUT BEGIN ===
1/10 Checking commit d3190532cbbb (monitor: Remove unused password prompting fields)
2/10 Checking commit 95f9e11f320f (monitor: Split monitor_init in HMP and QMP function)
3/10 Checking commit 6ab73c33845d (monitor: Make MonitorQMP a child class of Monitor)
4/10 Checking commit 75ae78baf77e (monitor: Create MonitorHMP with readline state)
ERROR: braces {} are necessary for all arms of this statement
#192: FILE: monitor.c:1355:
+ if (!hmp_mon->rs)
[...]
total: 1 errors, 0 warnings, 373 lines checked
Patch 4/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
5/10 Checking commit 8f3621b035e3 (monitor: Move cmd_table to MonitorHMP)
6/10 Checking commit febc6502f515 (Move monitor.c to monitor/misc.c)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#31:
new file mode 100644
total: 0 errors, 1 warnings, 12 lines checked
Patch 6/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
7/10 Checking commit d3d699e3b265 (monitor: Create monitor_int.h with common definitions)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#150:
new file mode 100644
WARNING: Block comments use a leading /* on a separate line
#233: FILE: monitor/monitor_int.h:79:
+ /* @sub_table is a list of 2nd level of commands. If it does not exist,
total: 0 errors, 2 warnings, 275 lines checked
Patch 7/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
8/10 Checking commit 9a88f45205d1 (monitor: Split out monitor/qmp.c)
ERROR: return is not a function, parentheses are not required
#584: FILE: monitor/monitor_int.h:153:
+ return (mon->flags & MONITOR_USE_CONTROL);
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#608:
new file mode 100644
total: 1 errors, 1 warnings, 956 lines checked
Patch 8/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
9/10 Checking commit 54736ce87811 (monitor: Split out monitor/hmp.c)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#39:
new file mode 100644
ERROR: braces {} are necessary for all arms of this statement
#327: FILE: monitor/hmp.c:284:
+ while (qemu_isspace(*pch))
[...]
ERROR: space required before the open parenthesis '('
#340: FILE: monitor/hmp.c:297:
+ switch(*pch) {
ERROR: braces {} are necessary for all arms of this statement
#363: FILE: monitor/hmp.c:320:
+ if (*pch == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#367: FILE: monitor/hmp.c:324:
+ if (*pch != '\'')
[...]
ERROR: braces {} are necessary for all arms of this statement
#382: FILE: monitor/hmp.c:339:
+ if ((q - buf) < sizeof(buf) - 1)
[...]
ERROR: braces {} are necessary for all arms of this statement
#386: FILE: monitor/hmp.c:343:
+ while (qemu_isspace(*pch))
[...]
ERROR: braces {} are necessary for all arms of this statement
#390: FILE: monitor/hmp.c:347:
+ if (ret < 0)
[...]
ERROR: consider using qemu_strtoull in preference to strtoull
#401: FILE: monitor/hmp.c:358:
+ n = strtoull(pch, &p, 0);
ERROR: braces {} are necessary for all arms of this statement
#409: FILE: monitor/hmp.c:366:
+ while (qemu_isspace(*pch))
[...]
ERROR: space required before the open parenthesis '('
#422: FILE: monitor/hmp.c:379:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#424: FILE: monitor/hmp.c:381:
+ if (op != '*' && op != '/' && op != '%')
[...]
ERROR: space required before the open parenthesis '('
#428: FILE: monitor/hmp.c:385:
+ switch(op) {
ERROR: braces {} are necessary for all arms of this statement
#435: FILE: monitor/hmp.c:392:
+ if (val2 == 0)
[...]
ERROR: braces {} are necessary for all arms of this statement
#437: FILE: monitor/hmp.c:394:
+ if (op == '/')
[...]
+ else
[...]
ERROR: space required before the open parenthesis '('
#453: FILE: monitor/hmp.c:410:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#455: FILE: monitor/hmp.c:412:
+ if (op != '&' && op != '|' && op != '^')
[...]
ERROR: space required before the open parenthesis '('
#459: FILE: monitor/hmp.c:416:
+ switch(op) {
ERROR: space required before the open parenthesis '('
#481: FILE: monitor/hmp.c:438:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#483: FILE: monitor/hmp.c:440:
+ if (op != '+' && op != '-')
[...]
ERROR: braces {} are necessary for all arms of this statement
#487: FILE: monitor/hmp.c:444:
+ if (op == '+')
[...]
+ else
[...]
ERROR: braces {} are necessary for all arms of this statement
#502: FILE: monitor/hmp.c:459:
+ while (qemu_isspace(*pch))
[...]
ERROR: braces {} are necessary for all arms of this statement
#541: FILE: monitor/hmp.c:498:
+ while (qemu_isspace(*p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#543: FILE: monitor/hmp.c:500:
+ if (*p == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#546: FILE: monitor/hmp.c:503:
+ while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#549: FILE: monitor/hmp.c:506:
+ if (len > nlen - 1)
[...]
ERROR: braces {} are necessary for all arms of this statement
#565: FILE: monitor/hmp.c:522:
+ if (*type == ',')
[...]
ERROR: space required before the open parenthesis '('
#684: FILE: monitor/hmp.c:641:
+ for(;;) {
ERROR: space required before the open parenthesis '('
#691: FILE: monitor/hmp.c:648:
+ switch(c) {
ERROR: braces {} are necessary for all arms of this statement
#698: FILE: monitor/hmp.c:655:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#709: FILE: monitor/hmp.c:666:
+ switch(c) {
ERROR: braces {} are necessary for all arms of this statement
#757: FILE: monitor/hmp.c:714:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#772: FILE: monitor/hmp.c:729:
+ for(;;) {
ERROR: space required before the open parenthesis '('
#773: FILE: monitor/hmp.c:730:
+ switch(*p) {
ERROR: braces {} are necessary for all arms of this statement
#840: FILE: monitor/hmp.c:797:
+ while (qemu_isspace(*p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#861: FILE: monitor/hmp.c:818:
+ if (get_expr(mon, &val, &p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#965: FILE: monitor/hmp.c:922:
+ if (c == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#967: FILE: monitor/hmp.c:924:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#971: FILE: monitor/hmp.c:928:
+ if(c != *p) {
ERROR: space required before the open parenthesis '('
#972: FILE: monitor/hmp.c:929:
+ if(!is_valid_option(p, typestr)) {
ERROR: space required before the open parenthesis '('
#980: FILE: monitor/hmp.c:937:
+ if(skip_key) {
ERROR: braces {} are necessary for all arms of this statement
#1024: FILE: monitor/hmp.c:981:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#1074: FILE: monitor/hmp.c:1031:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#1078: FILE: monitor/hmp.c:1035:
+ if (len > sizeof(cmd) - 2)
[...]
ERROR: braces {} are necessary for all arms of this statement
#1085: FILE: monitor/hmp.c:1042:
+ if (*p == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#1108: FILE: monitor/hmp.c:1065:
+ if (input_path_len > sizeof(path) - 1)
[...]
ERROR: braces {} are necessary for all arms of this statement
#1115: FILE: monitor/hmp.c:1072:
+ if (!ffs)
[...]
ERROR: space required before the open parenthesis '('
#1117: FILE: monitor/hmp.c:1074:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#1120: FILE: monitor/hmp.c:1077:
+ if (!d)
[...]
WARNING: Block comments use a leading /* on a separate line
#1132: FILE: monitor/hmp.c:1089:
+ /* stat the file to find out if it's a directory.
ERROR: braces {} are necessary for all arms of this statement
#1163: FILE: monitor/hmp.c:1120:
+ if (nb_args == 0)
[...]
+ else
[...]
ERROR: space required before the open parenthesis '('
#1199: FILE: monitor/hmp.c:1156:
+ for(i = 0; i < nb_args - 2; i++) {
ERROR: braces {} are necessary for all arms of this statement
#1202: FILE: monitor/hmp.c:1159:
+ while (*ptype == '?')
[...]
ERROR: space required before the open parenthesis '('
#1212: FILE: monitor/hmp.c:1169:
+ switch(*ptype) {
WARNING: Block comments use a leading /* on a separate line
#1254: FILE: monitor/hmp.c:1211:
+ /* if the line ends with a space, it means we want to complete the
WARNING: Block comments use * on subsequent lines
#1255: FILE: monitor/hmp.c:1212:
+ /* if the line ends with a space, it means we want to complete the
+ next arg */
WARNING: Block comments use a trailing */ on a separate line
#1255: FILE: monitor/hmp.c:1212:
+ next arg */
WARNING: Block comments use a leading /* on a separate line
#1348: FILE: monitor/hmp.c:1305:
+/* These functions just adapt the readline interface in a typesafe way. We
ERROR: space required before the open parenthesis '('
#2850: FILE: monitor/monitor_int.h:166:
+ for(;;) {
total: 53 errors, 6 warnings, 2828 lines checked
Patch 9/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
10/10 Checking commit dbb0f228d63f (monitor: Split out monitor/core.c)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#44:
new file mode 100644
ERROR: braces {} are necessary for all arms of this statement
#214: FILE: monitor/core.c:166:
+ if (!mon)
[...]
WARNING: Block comments use a leading /* on a separate line
#650: FILE: monitor/core.c:602:
+ { /* end of list */ }
total: 1 errors, 2 warnings, 1242 lines checked
Patch 10/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
The full log is available at
http://patchew.org/logs/20190607135430.22149-1-kwolf@redhat.com/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
2019-06-07 13:54 [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc Kevin Wolf
` (11 preceding siblings ...)
2019-06-07 16:48 ` [Qemu-devel] " no-reply
@ 2019-06-07 20:42 ` no-reply
12 siblings, 0 replies; 31+ messages in thread
From: no-reply @ 2019-06-07 20:42 UTC (permalink / raw)
To: kwolf; +Cc: kwolf, dgilbert, qemu-devel, qemu-block, armbru
Patchew URL: https://patchew.org/QEMU/20190607135430.22149-1-kwolf@redhat.com/
Hi,
This series seems to have some coding style problems. See output below for
more information:
Type: series
Subject: [Qemu-devel] [RFC PATCH 00/10] monitor: Split monitor.c in core/HMP/QMP/misc
Message-id: 20190607135430.22149-1-kwolf@redhat.com
=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===
Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
* [new tag] patchew/20190607184802.100945-1-vsementsov@virtuozzo.com -> patchew/20190607184802.100945-1-vsementsov@virtuozzo.com
Switched to a new branch 'test'
393e03b monitor: Split out monitor/core.c
6938636 monitor: Split out monitor/hmp.c
b33f363 monitor: Split out monitor/qmp.c
4e784bf monitor: Create monitor_int.h with common definitions
ec11ead Move monitor.c to monitor/misc.c
00365cc monitor: Move cmd_table to MonitorHMP
05c3aa3 monitor: Create MonitorHMP with readline state
5435f06 monitor: Make MonitorQMP a child class of Monitor
a541d49 monitor: Split monitor_init in HMP and QMP function
d1dcf9d monitor: Remove unused password prompting fields
=== OUTPUT BEGIN ===
1/10 Checking commit d1dcf9d89219 (monitor: Remove unused password prompting fields)
2/10 Checking commit a541d49ece87 (monitor: Split monitor_init in HMP and QMP function)
3/10 Checking commit 5435f06ca5bf (monitor: Make MonitorQMP a child class of Monitor)
4/10 Checking commit 05c3aa3627dd (monitor: Create MonitorHMP with readline state)
ERROR: braces {} are necessary for all arms of this statement
#192: FILE: monitor.c:1355:
+ if (!hmp_mon->rs)
[...]
total: 1 errors, 0 warnings, 373 lines checked
Patch 4/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
5/10 Checking commit 00365cc53224 (monitor: Move cmd_table to MonitorHMP)
6/10 Checking commit ec11ead0d0aa (Move monitor.c to monitor/misc.c)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#31:
new file mode 100644
total: 0 errors, 1 warnings, 12 lines checked
Patch 6/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
7/10 Checking commit 4e784bfbeaf0 (monitor: Create monitor_int.h with common definitions)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#150:
new file mode 100644
WARNING: Block comments use a leading /* on a separate line
#233: FILE: monitor/monitor_int.h:79:
+ /* @sub_table is a list of 2nd level of commands. If it does not exist,
total: 0 errors, 2 warnings, 275 lines checked
Patch 7/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
8/10 Checking commit b33f3634c4a3 (monitor: Split out monitor/qmp.c)
ERROR: return is not a function, parentheses are not required
#584: FILE: monitor/monitor_int.h:153:
+ return (mon->flags & MONITOR_USE_CONTROL);
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#608:
new file mode 100644
total: 1 errors, 1 warnings, 956 lines checked
Patch 8/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
9/10 Checking commit 693863658d05 (monitor: Split out monitor/hmp.c)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#39:
new file mode 100644
ERROR: braces {} are necessary for all arms of this statement
#327: FILE: monitor/hmp.c:284:
+ while (qemu_isspace(*pch))
[...]
ERROR: space required before the open parenthesis '('
#340: FILE: monitor/hmp.c:297:
+ switch(*pch) {
ERROR: braces {} are necessary for all arms of this statement
#363: FILE: monitor/hmp.c:320:
+ if (*pch == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#367: FILE: monitor/hmp.c:324:
+ if (*pch != '\'')
[...]
ERROR: braces {} are necessary for all arms of this statement
#382: FILE: monitor/hmp.c:339:
+ if ((q - buf) < sizeof(buf) - 1)
[...]
ERROR: braces {} are necessary for all arms of this statement
#386: FILE: monitor/hmp.c:343:
+ while (qemu_isspace(*pch))
[...]
ERROR: braces {} are necessary for all arms of this statement
#390: FILE: monitor/hmp.c:347:
+ if (ret < 0)
[...]
ERROR: consider using qemu_strtoull in preference to strtoull
#401: FILE: monitor/hmp.c:358:
+ n = strtoull(pch, &p, 0);
ERROR: braces {} are necessary for all arms of this statement
#409: FILE: monitor/hmp.c:366:
+ while (qemu_isspace(*pch))
[...]
ERROR: space required before the open parenthesis '('
#422: FILE: monitor/hmp.c:379:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#424: FILE: monitor/hmp.c:381:
+ if (op != '*' && op != '/' && op != '%')
[...]
ERROR: space required before the open parenthesis '('
#428: FILE: monitor/hmp.c:385:
+ switch(op) {
ERROR: braces {} are necessary for all arms of this statement
#435: FILE: monitor/hmp.c:392:
+ if (val2 == 0)
[...]
ERROR: braces {} are necessary for all arms of this statement
#437: FILE: monitor/hmp.c:394:
+ if (op == '/')
[...]
+ else
[...]
ERROR: space required before the open parenthesis '('
#453: FILE: monitor/hmp.c:410:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#455: FILE: monitor/hmp.c:412:
+ if (op != '&' && op != '|' && op != '^')
[...]
ERROR: space required before the open parenthesis '('
#459: FILE: monitor/hmp.c:416:
+ switch(op) {
ERROR: space required before the open parenthesis '('
#481: FILE: monitor/hmp.c:438:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#483: FILE: monitor/hmp.c:440:
+ if (op != '+' && op != '-')
[...]
ERROR: braces {} are necessary for all arms of this statement
#487: FILE: monitor/hmp.c:444:
+ if (op == '+')
[...]
+ else
[...]
ERROR: braces {} are necessary for all arms of this statement
#502: FILE: monitor/hmp.c:459:
+ while (qemu_isspace(*pch))
[...]
ERROR: braces {} are necessary for all arms of this statement
#541: FILE: monitor/hmp.c:498:
+ while (qemu_isspace(*p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#543: FILE: monitor/hmp.c:500:
+ if (*p == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#546: FILE: monitor/hmp.c:503:
+ while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#549: FILE: monitor/hmp.c:506:
+ if (len > nlen - 1)
[...]
ERROR: braces {} are necessary for all arms of this statement
#565: FILE: monitor/hmp.c:522:
+ if (*type == ',')
[...]
ERROR: space required before the open parenthesis '('
#684: FILE: monitor/hmp.c:641:
+ for(;;) {
ERROR: space required before the open parenthesis '('
#691: FILE: monitor/hmp.c:648:
+ switch(c) {
ERROR: braces {} are necessary for all arms of this statement
#698: FILE: monitor/hmp.c:655:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#709: FILE: monitor/hmp.c:666:
+ switch(c) {
ERROR: braces {} are necessary for all arms of this statement
#757: FILE: monitor/hmp.c:714:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#772: FILE: monitor/hmp.c:729:
+ for(;;) {
ERROR: space required before the open parenthesis '('
#773: FILE: monitor/hmp.c:730:
+ switch(*p) {
ERROR: braces {} are necessary for all arms of this statement
#840: FILE: monitor/hmp.c:797:
+ while (qemu_isspace(*p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#861: FILE: monitor/hmp.c:818:
+ if (get_expr(mon, &val, &p))
[...]
ERROR: braces {} are necessary for all arms of this statement
#965: FILE: monitor/hmp.c:922:
+ if (c == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#967: FILE: monitor/hmp.c:924:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#971: FILE: monitor/hmp.c:928:
+ if(c != *p) {
ERROR: space required before the open parenthesis '('
#972: FILE: monitor/hmp.c:929:
+ if(!is_valid_option(p, typestr)) {
ERROR: space required before the open parenthesis '('
#980: FILE: monitor/hmp.c:937:
+ if(skip_key) {
ERROR: braces {} are necessary for all arms of this statement
#1024: FILE: monitor/hmp.c:981:
+ while (qemu_isspace(*p))
[...]
ERROR: space required before the open parenthesis '('
#1074: FILE: monitor/hmp.c:1031:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#1078: FILE: monitor/hmp.c:1035:
+ if (len > sizeof(cmd) - 2)
[...]
ERROR: braces {} are necessary for all arms of this statement
#1085: FILE: monitor/hmp.c:1042:
+ if (*p == '\0')
[...]
ERROR: braces {} are necessary for all arms of this statement
#1108: FILE: monitor/hmp.c:1065:
+ if (input_path_len > sizeof(path) - 1)
[...]
ERROR: braces {} are necessary for all arms of this statement
#1115: FILE: monitor/hmp.c:1072:
+ if (!ffs)
[...]
ERROR: space required before the open parenthesis '('
#1117: FILE: monitor/hmp.c:1074:
+ for(;;) {
ERROR: braces {} are necessary for all arms of this statement
#1120: FILE: monitor/hmp.c:1077:
+ if (!d)
[...]
WARNING: Block comments use a leading /* on a separate line
#1132: FILE: monitor/hmp.c:1089:
+ /* stat the file to find out if it's a directory.
ERROR: braces {} are necessary for all arms of this statement
#1163: FILE: monitor/hmp.c:1120:
+ if (nb_args == 0)
[...]
+ else
[...]
ERROR: space required before the open parenthesis '('
#1199: FILE: monitor/hmp.c:1156:
+ for(i = 0; i < nb_args - 2; i++) {
ERROR: braces {} are necessary for all arms of this statement
#1202: FILE: monitor/hmp.c:1159:
+ while (*ptype == '?')
[...]
ERROR: space required before the open parenthesis '('
#1212: FILE: monitor/hmp.c:1169:
+ switch(*ptype) {
WARNING: Block comments use a leading /* on a separate line
#1254: FILE: monitor/hmp.c:1211:
+ /* if the line ends with a space, it means we want to complete the
WARNING: Block comments use * on subsequent lines
#1255: FILE: monitor/hmp.c:1212:
+ /* if the line ends with a space, it means we want to complete the
+ next arg */
WARNING: Block comments use a trailing */ on a separate line
#1255: FILE: monitor/hmp.c:1212:
+ next arg */
WARNING: Block comments use a leading /* on a separate line
#1348: FILE: monitor/hmp.c:1305:
+/* These functions just adapt the readline interface in a typesafe way. We
ERROR: space required before the open parenthesis '('
#2850: FILE: monitor/monitor_int.h:166:
+ for(;;) {
total: 53 errors, 6 warnings, 2828 lines checked
Patch 9/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
10/10 Checking commit 393e03bb4f4e (monitor: Split out monitor/core.c)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#44:
new file mode 100644
ERROR: braces {} are necessary for all arms of this statement
#214: FILE: monitor/core.c:166:
+ if (!mon)
[...]
WARNING: Block comments use a leading /* on a separate line
#650: FILE: monitor/core.c:602:
+ { /* end of list */ }
total: 1 errors, 2 warnings, 1242 lines checked
Patch 10/10 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
The full log is available at
http://patchew.org/logs/20190607135430.22149-1-kwolf@redhat.com/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
^ permalink raw reply [flat|nested] 31+ messages in thread