X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/9e72681d16792d0ffc42bab634b1753ff299bdfd..03cbfd7b5cdb00a7dfadf502b5acdeeefd69422c:/monitor.c diff --git a/monitor.c b/monitor.c index d0edb3bb15..73eac17952 100644 --- a/monitor.c +++ b/monitor.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include #include "hw/hw.h" #include "monitor/qdev.h" @@ -59,7 +60,6 @@ #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include -#include "qemu/osdep.h" #include "cpu.h" #include "trace.h" #include "trace/control.h" @@ -181,13 +181,16 @@ typedef struct { * instance. */ typedef struct MonitorQAPIEventState { - QAPIEvent event; /* Event being tracked */ - int64_t rate; /* Minimum time (in ns) between two events */ - int64_t last; /* QEMU_CLOCK_REALTIME value at last emission */ + QAPIEvent event; /* Throttling state for this event type and... */ + QDict *data; /* ... data, see qapi_event_throttle_equal() */ QEMUTimer *timer; /* Timer for handling delayed events */ - QObject *data; /* Event pending delayed dispatch */ + QDict *qdict; /* Delayed event (if any) */ } MonitorQAPIEventState; +typedef struct { + int64_t rate; /* Minimum time (in ns) between two events */ +} MonitorQAPIEventConf; + struct Monitor { CharDriverState *chr; int reset_seen; @@ -372,8 +375,7 @@ void monitor_printf(Monitor *mon, const char *fmt, ...) va_end(ap); } -static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream, - const char *fmt, ...) +int monitor_fprintf(FILE *stream, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -401,7 +403,7 @@ static QDict *build_qmp_error_dict(Error *err) QObject *obj; obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }", - ErrorClass_lookup[error_get_class(err)], + QapiErrorClass_lookup[error_get_class(err)], error_get_pretty(err)); return qobject_to_qdict(obj); @@ -439,132 +441,161 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data, } -static MonitorQAPIEventState monitor_qapi_event_state[QAPI_EVENT_MAX]; +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 }, +}; + +GHashTable *monitor_qapi_event_state; /* * Emits the event to every monitor instance, @event is only used for trace * Called with monitor_lock held. */ -static void monitor_qapi_event_emit(QAPIEvent event, QObject *data) +static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) { Monitor *mon; - trace_monitor_protocol_event_emit(event, data); + trace_monitor_protocol_event_emit(event, qdict); QLIST_FOREACH(mon, &mon_list, entry) { if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) { - monitor_json_emitter(mon, data); + monitor_json_emitter(mon, QOBJECT(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(QAPIEvent event, QDict *data, Error **errp) +monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) { + MonitorQAPIEventConf *evconf; MonitorQAPIEventState *evstate; - assert(event < QAPI_EVENT_MAX); - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - evstate = &(monitor_qapi_event_state[event]); - trace_monitor_protocol_event_queue(event, - data, - evstate->rate, - evstate->last, - now); + assert(event < QAPI_EVENT__MAX); + evconf = &monitor_qapi_event_conf[event]; + trace_monitor_protocol_event_queue(event, qdict, evconf->rate); - /* Rate limit of 0 indicates no throttling */ qemu_mutex_lock(&monitor_lock); - if (!evstate->rate) { - monitor_qapi_event_emit(event, QOBJECT(data)); - evstate->last = now; + + if (!evconf->rate) { + /* Unthrottled event */ + monitor_qapi_event_emit(event, qdict); } else { - int64_t delta = now - evstate->last; - if (evstate->data || - delta < evstate->rate) { - /* If there's an existing event pending, replace - * it with the new event, otherwise schedule a - * timer for delayed emission + 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. */ - if (evstate->data) { - qobject_decref(evstate->data); - } else { - int64_t then = evstate->last + evstate->rate; - timer_mod_ns(evstate->timer, then); - } - evstate->data = QOBJECT(data); - qobject_incref(evstate->data); + QDECREF(evstate->qdict); + evstate->qdict = qdict; + QINCREF(evstate->qdict); } else { - monitor_qapi_event_emit(event, QOBJECT(data)); - evstate->last = now; + /* + * 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(QEMU_CLOCK_REALTIME); + + monitor_qapi_event_emit(event, qdict); + + evstate = g_new(MonitorQAPIEventState, 1); + evstate->event = event; + evstate->data = data; + QINCREF(evstate->data); + evstate->qdict = NULL; + evstate->timer = timer_new_ns(QEMU_CLOCK_REALTIME, + 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); } /* - * The callback invoked by QemuTimer when a delayed - * event is ready to be emitted + * 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; - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event]; - trace_monitor_protocol_event_handler(evstate->event, - evstate->data, - evstate->last, - now); + trace_monitor_protocol_event_handler(evstate->event, evstate->qdict); qemu_mutex_lock(&monitor_lock); - if (evstate->data) { - monitor_qapi_event_emit(evstate->event, evstate->data); - qobject_decref(evstate->data); - evstate->data = NULL; + + if (evstate->qdict) { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + + monitor_qapi_event_emit(evstate->event, evstate->qdict); + QDECREF(evstate->qdict); + evstate->qdict = NULL; + timer_mod_ns(evstate->timer, now + evconf->rate); + } else { + g_hash_table_remove(monitor_qapi_event_state, evstate); + QDECREF(evstate->data); + timer_free(evstate->timer); + g_free(evstate); } - evstate->last = now; + qemu_mutex_unlock(&monitor_lock); } -/* - * @event: the event ID to be limited - * @rate: the rate limit in milliseconds - * - * Sets a rate limit on a particular event, so no - * more than 1 event will be emitted within @rate - * milliseconds - */ -static void -monitor_qapi_event_throttle(QAPIEvent event, int64_t rate) +static unsigned int qapi_event_throttle_hash(const void *key) { - MonitorQAPIEventState *evstate; - assert(event < QAPI_EVENT_MAX); + const MonitorQAPIEventState *evstate = key; + unsigned int hash = evstate->event * 255; - evstate = &(monitor_qapi_event_state[event]); + if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) { + hash += g_str_hash(qdict_get_str(evstate->data, "id")); + } - trace_monitor_protocol_event_throttle(event, rate); - evstate->event = event; - assert(rate * SCALE_MS <= INT64_MAX); - evstate->rate = rate * SCALE_MS; - evstate->last = 0; - evstate->data = NULL; - evstate->timer = timer_new(QEMU_CLOCK_REALTIME, - SCALE_MS, - monitor_qapi_event_handler, - evstate); + return hash; } -static void monitor_qapi_event_init(void) +static gboolean qapi_event_throttle_equal(const void *a, const void *b) { - /* Limit guest-triggerable events to 1 per second */ - monitor_qapi_event_throttle(QAPI_EVENT_RTC_CHANGE, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_WATCHDOG, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_BALLOON_CHANGE, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_QUORUM_REPORT_BAD, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_QUORUM_FAILURE, 1000); - monitor_qapi_event_throttle(QAPI_EVENT_VSERPORT_CHANGE, 1000); + 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")); + } + + 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); qmp_event_set_func_emit(monitor_qapi_event_queue); } @@ -915,7 +946,7 @@ EventInfoList *qmp_query_events(Error **errp) EventInfoList *info, *ev_list = NULL; QAPIEvent e; - for (e = 0 ; e < QAPI_EVENT_MAX ; e++) { + for (e = 0 ; e < QAPI_EVENT__MAX ; e++) { const char *event_name = QAPIEvent_lookup[e]; assert(event_name != NULL); info = g_malloc0(sizeof(*info)); @@ -957,7 +988,7 @@ int monitor_set_cpu(int cpu_index) return 0; } -static CPUState *mon_get_cpu(void) +CPUState *mon_get_cpu(void) { if (!cur_mon->mon_cpu) { monitor_set_cpu(0); @@ -1344,7 +1375,7 @@ static void hmp_mouse_move(Monitor *mon, const QDict *qdict) if (dz_str) { dz = strtol(dz_str, NULL, 0); if (dz != 0) { - button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; + button = (dz > 0) ? INPUT_BUTTON_WHEELUP : INPUT_BUTTON_WHEELDOWN; qemu_input_queue_btn(NULL, button, true); qemu_input_event_sync(); qemu_input_queue_btn(NULL, button, false); @@ -1355,7 +1386,7 @@ static void hmp_mouse_move(Monitor *mon, const QDict *qdict) static void hmp_mouse_button(Monitor *mon, const QDict *qdict) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, @@ -1433,8 +1464,7 @@ static void hmp_boot_set(Monitor *mon, const QDict *qdict) qemu_boot_set(bootdevice, &local_err); if (local_err) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } else { monitor_printf(mon, "boot device list now set to %s\n", bootdevice); } @@ -2105,6 +2135,8 @@ static int get_monitor_def(target_long *pval, const char *name) { const MonitorDef *md = target_monitor_defs(); void *ptr; + uint64_t tmp = 0; + int ret; if (md == NULL) { return -1; @@ -2132,7 +2164,13 @@ static int get_monitor_def(target_long *pval, const char *name) return 0; } } - return -1; + + ret = target_get_monitor_def(mon_get_cpu(), name, &tmp); + if (!ret) { + *pval = (target_long) tmp; + } + + return ret; } static void next(void) @@ -2697,7 +2735,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, break; } } - val = strtosz(p, &end); + val = qemu_strtosz(p, &end); if (val < 0) { monitor_printf(mon, "invalid size\n"); goto fail; @@ -3178,7 +3216,7 @@ void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) } len = strlen(str); readline_set_completion_index(rs, len); - for (i = 0; i < Q_KEY_CODE_MAX; i++) { + for (i = 0; i < Q_KEY_CODE__MAX; i++) { if (!strncmp(str, QKeyCode_lookup[i], len)) { readline_add_completion(rs, QKeyCode_lookup[i]); } @@ -3277,7 +3315,7 @@ void migrate_set_capability_completion(ReadLineState *rs, int nb_args, readline_set_completion_index(rs, len); if (nb_args == 2) { int i; - for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { const char *name = MigrationCapability_lookup[i]; if (!strncmp(str, name, len)) { readline_add_completion(rs, name); @@ -3298,7 +3336,7 @@ void migrate_set_parameter_completion(ReadLineState *rs, int nb_args, readline_set_completion_index(rs, len); if (nb_args == 2) { int i; - for (i = 0; i < MIGRATION_PARAMETER_MAX; i++) { + for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { const char *name = MigrationParameter_lookup[i]; if (!strncmp(str, name, len)) { readline_add_completion(rs, name); @@ -3377,13 +3415,18 @@ static void vm_completion(ReadLineState *rs, const char *str) readline_set_completion_index(rs, len); while ((bs = bdrv_next(bs))) { SnapshotInfoList *snapshots, *snapshot; + AioContext *ctx = bdrv_get_aio_context(bs); + bool ok = false; - if (!bdrv_can_snapshot(bs)) { - continue; + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs)) { + ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; } - if (bdrv_query_snapshot_info_list(bs, &snapshots, NULL)) { + aio_context_release(ctx); + if (!ok) { continue; } + snapshot = snapshots; while (snapshot) { char *completion = snapshot->value->name; @@ -3805,7 +3848,7 @@ static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp) return input_dict; } -static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) +static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) { Error *local_err = NULL; QObject *obj, *data; @@ -3863,6 +3906,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) err_out: monitor_protocol_emitter(mon, data, local_err); qobject_decref(data); + error_free(local_err); QDECREF(input); QDECREF(args); } @@ -4104,8 +4148,7 @@ static void bdrv_password_cb(void *opaque, const char *password, bdrv_add_key(bs, password, &local_err); if (local_err) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); ret = -EPERM; } if (mon->password_completion_cb) @@ -4146,6 +4189,10 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, monitor_printf(mon, "Device not found %s\n", device); return -1; } + if (!blk_bs(blk)) { + monitor_printf(mon, "Device '%s' has no medium\n", device); + return -1; + } bdrv_add_key(blk_bs(blk), NULL, &err); if (err) {