diff options
Diffstat (limited to 'monitor.c')
-rw-r--r-- | monitor.c | 343 |
1 files changed, 216 insertions, 127 deletions
@@ -66,6 +66,7 @@ #include "memory.h" #include "qmp-commands.h" #include "hmp.h" +#include "qemu-thread.h" /* for pic/irq_info */ #if defined(TARGET_SPARC) @@ -145,6 +146,19 @@ typedef struct MonitorControl { int command_mode; } MonitorControl; +/* + * To prevent flooding clients, events can be throttled. The + * throttling is calculated globally, rather than per-Monitor + * instance. + */ +typedef struct MonitorEventState { + MonitorEvent event; /* Event being tracked */ + int64_t rate; /* Period over which to throttle. 0 to disable */ + int64_t last; /* Time at which event was last emitted */ + QEMUTimer *timer; /* Timer for handling delayed events */ + QObject *data; /* Event pending delayed dispatch */ +} MonitorEventState; + struct Monitor { CharDriverState *chr; int mux_out; @@ -422,6 +436,166 @@ static void timestamp_put(QDict *qdict) qdict_put_obj(qdict, "timestamp", obj); } + +static const char *monitor_event_names[] = { + [QEVENT_SHUTDOWN] = "SHUTDOWN", + [QEVENT_RESET] = "RESET", + [QEVENT_POWERDOWN] = "POWERDOWN", + [QEVENT_STOP] = "STOP", + [QEVENT_RESUME] = "RESUME", + [QEVENT_VNC_CONNECTED] = "VNC_CONNECTED", + [QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED", + [QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED", + [QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR", + [QEVENT_RTC_CHANGE] = "RTC_CHANGE", + [QEVENT_WATCHDOG] = "WATCHDOG", + [QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED", + [QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED", + [QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED", + [QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED", + [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", + [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", + [QEVENT_SUSPEND] = "SUSPEND", + [QEVENT_WAKEUP] = "WAKEUP", + [QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE", +}; +QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) + +MonitorEventState monitor_event_state[QEVENT_MAX]; +QemuMutex monitor_event_state_lock; + +/* + * Emits the event to every monitor instance + */ +static void +monitor_protocol_event_emit(MonitorEvent event, + QObject *data) +{ + Monitor *mon; + + trace_monitor_protocol_event_emit(event, data); + QLIST_FOREACH(mon, &mon_list, entry) { + if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) { + monitor_json_emitter(mon, data); + } + } +} + + +/* + * Queue a new event for emission to Monitor instances, + * applying any rate limiting if required. + */ +static void +monitor_protocol_event_queue(MonitorEvent event, + QObject *data) +{ + MonitorEventState *evstate; + int64_t now = qemu_get_clock_ns(rt_clock); + assert(event < QEVENT_MAX); + + qemu_mutex_lock(&monitor_event_state_lock); + evstate = &(monitor_event_state[event]); + trace_monitor_protocol_event_queue(event, + data, + evstate->rate, + evstate->last, + now); + + /* Rate limit of 0 indicates no throttling */ + if (!evstate->rate) { + monitor_protocol_event_emit(event, data); + evstate->last = now; + } 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 + */ + if (evstate->data) { + qobject_decref(evstate->data); + } else { + int64_t then = evstate->last + evstate->rate; + qemu_mod_timer_ns(evstate->timer, then); + } + evstate->data = data; + qobject_incref(evstate->data); + } else { + monitor_protocol_event_emit(event, data); + evstate->last = now; + } + } + qemu_mutex_unlock(&monitor_event_state_lock); +} + + +/* + * The callback invoked by QemuTimer when a delayed + * event is ready to be emitted + */ +static void monitor_protocol_event_handler(void *opaque) +{ + MonitorEventState *evstate = opaque; + int64_t now = qemu_get_clock_ns(rt_clock); + + qemu_mutex_lock(&monitor_event_state_lock); + + trace_monitor_protocol_event_handler(evstate->event, + evstate->data, + evstate->last, + now); + if (evstate->data) { + monitor_protocol_event_emit(evstate->event, evstate->data); + qobject_decref(evstate->data); + evstate->data = NULL; + } + evstate->last = now; + qemu_mutex_unlock(&monitor_event_state_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_protocol_event_throttle(MonitorEvent event, + int64_t rate) +{ + MonitorEventState *evstate; + assert(event < QEVENT_MAX); + + evstate = &(monitor_event_state[event]); + + trace_monitor_protocol_event_throttle(event, rate); + evstate->event = event; + evstate->rate = rate * SCALE_MS; + evstate->timer = qemu_new_timer(rt_clock, + SCALE_MS, + monitor_protocol_event_handler, + evstate); + evstate->last = 0; + evstate->data = NULL; +} + + +/* Global, one-time initializer to configure the rate limiting + * and initialize state */ +static void monitor_protocol_event_init(void) +{ + qemu_mutex_init(&monitor_event_state_lock); + /* Limit RTC & BALLOON events to 1 per second */ + monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000); + monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000); + monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000); +} + /** * monitor_protocol_event(): Generate a Monitor event * @@ -431,72 +605,11 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) { QDict *qmp; const char *event_name; - Monitor *mon; assert(event < QEVENT_MAX); - switch (event) { - case QEVENT_SHUTDOWN: - event_name = "SHUTDOWN"; - break; - case QEVENT_RESET: - event_name = "RESET"; - break; - case QEVENT_POWERDOWN: - event_name = "POWERDOWN"; - break; - case QEVENT_STOP: - event_name = "STOP"; - break; - case QEVENT_RESUME: - event_name = "RESUME"; - break; - case QEVENT_VNC_CONNECTED: - event_name = "VNC_CONNECTED"; - break; - case QEVENT_VNC_INITIALIZED: - event_name = "VNC_INITIALIZED"; - break; - case QEVENT_VNC_DISCONNECTED: - event_name = "VNC_DISCONNECTED"; - break; - case QEVENT_BLOCK_IO_ERROR: - event_name = "BLOCK_IO_ERROR"; - break; - case QEVENT_RTC_CHANGE: - event_name = "RTC_CHANGE"; - break; - case QEVENT_WATCHDOG: - event_name = "WATCHDOG"; - break; - case QEVENT_SPICE_CONNECTED: - event_name = "SPICE_CONNECTED"; - break; - case QEVENT_SPICE_INITIALIZED: - event_name = "SPICE_INITIALIZED"; - break; - case QEVENT_SPICE_DISCONNECTED: - event_name = "SPICE_DISCONNECTED"; - break; - case QEVENT_BLOCK_JOB_COMPLETED: - event_name = "BLOCK_JOB_COMPLETED"; - break; - case QEVENT_BLOCK_JOB_CANCELLED: - event_name = "BLOCK_JOB_CANCELLED"; - break; - case QEVENT_DEVICE_TRAY_MOVED: - event_name = "DEVICE_TRAY_MOVED"; - break; - case QEVENT_SUSPEND: - event_name = "SUSPEND"; - break; - case QEVENT_WAKEUP: - event_name = "WAKEUP"; - break; - default: - abort(); - break; - } + event_name = monitor_event_names[event]; + assert(event_name != NULL); qmp = qdict_new(); timestamp_put(qmp); @@ -506,11 +619,8 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) qdict_put_obj(qmp, "data", data); } - QLIST_FOREACH(mon, &mon_list, entry) { - if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) { - monitor_json_emitter(mon, QOBJECT(qmp)); - } - } + trace_monitor_protocol_event(event, event_name, qmp); + monitor_protocol_event_queue(event, QOBJECT(qmp)); QDECREF(qmp); } @@ -738,6 +848,25 @@ CommandInfoList *qmp_query_commands(Error **errp) return cmd_list; } +EventInfoList *qmp_query_events(Error **errp) +{ + EventInfoList *info, *ev_list = NULL; + MonitorEvent e; + + for (e = 0 ; e < QEVENT_MAX ; e++) { + const char *event_name = monitor_event_names[e]; + assert(event_name != NULL); + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(event_name); + + info->next = ev_list; + ev_list = info; + } + + return ev_list; +} + /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { @@ -812,13 +941,6 @@ static void do_info_cpu_stats(Monitor *mon) } #endif -#if defined(CONFIG_TRACE_SIMPLE) -static void do_info_trace(Monitor *mon) -{ - st_print_trace((FILE *)mon, &monitor_fprintf); -} -#endif - static void do_trace_print_events(Monitor *mon) { trace_print_events((FILE *)mon, &monitor_fprintf); @@ -1133,45 +1255,24 @@ static void do_print(Monitor *mon, const QDict *qdict) int format = qdict_get_int(qdict, "format"); target_phys_addr_t val = qdict_get_int(qdict, "val"); -#if TARGET_PHYS_ADDR_BITS == 32 - switch(format) { - case 'o': - monitor_printf(mon, "%#o", val); - break; - case 'x': - monitor_printf(mon, "%#x", val); - break; - case 'u': - monitor_printf(mon, "%u", val); - break; - default: - case 'd': - monitor_printf(mon, "%d", val); - break; - case 'c': - monitor_printc(mon, val); - break; - } -#else switch(format) { case 'o': - monitor_printf(mon, "%#" PRIo64, val); + monitor_printf(mon, "%#" TARGET_PRIoPHYS, val); break; case 'x': - monitor_printf(mon, "%#" PRIx64, val); + monitor_printf(mon, "%#" TARGET_PRIxPHYS, val); break; case 'u': - monitor_printf(mon, "%" PRIu64, val); + monitor_printf(mon, "%" TARGET_PRIuPHYS, val); break; default: case 'd': - monitor_printf(mon, "%" PRId64, val); + monitor_printf(mon, "%" TARGET_PRIdPHYS, val); break; case 'c': monitor_printc(mon, val); break; } -#endif monitor_printf(mon, "\n"); } @@ -2199,48 +2300,45 @@ static void do_inject_mce(Monitor *mon, const QDict *qdict) } #endif -static int do_getfd(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_getfd(const char *fdname, Error **errp) { - const char *fdname = qdict_get_str(qdict, "fdname"); mon_fd_t *monfd; int fd; - fd = qemu_chr_fe_get_msgfd(mon->chr); + fd = qemu_chr_fe_get_msgfd(cur_mon->chr); if (fd == -1) { - qerror_report(QERR_FD_NOT_SUPPLIED); - return -1; + error_set(errp, QERR_FD_NOT_SUPPLIED); + return; } if (qemu_isdigit(fdname[0])) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "fdname", - "a name not starting with a digit"); - return -1; + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", + "a name not starting with a digit"); + return; } - QLIST_FOREACH(monfd, &mon->fds, next) { + QLIST_FOREACH(monfd, &cur_mon->fds, next) { if (strcmp(monfd->name, fdname) != 0) { continue; } close(monfd->fd); monfd->fd = fd; - return 0; + return; } monfd = g_malloc0(sizeof(mon_fd_t)); monfd->name = g_strdup(fdname); monfd->fd = fd; - QLIST_INSERT_HEAD(&mon->fds, monfd, next); - return 0; + QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); } -static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_closefd(const char *fdname, Error **errp) { - const char *fdname = qdict_get_str(qdict, "fdname"); mon_fd_t *monfd; - QLIST_FOREACH(monfd, &mon->fds, next) { + QLIST_FOREACH(monfd, &cur_mon->fds, next) { if (strcmp(monfd->name, fdname) != 0) { continue; } @@ -2249,11 +2347,10 @@ static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data) close(monfd->fd); g_free(monfd->name); g_free(monfd); - return 0; + return; } - qerror_report(QERR_FD_NOT_FOUND, fdname); - return -1; + error_set(errp, QERR_FD_NOT_FOUND, fdname); } static void do_loadvm(Monitor *mon, const QDict *qdict) @@ -2585,15 +2682,6 @@ static mon_cmd_t info_cmds[] = { .help = "show roms", .mhandler.info = do_info_roms, }, -#if defined(CONFIG_TRACE_SIMPLE) - { - .name = "trace", - .args_type = "", - .params = "", - .help = "show current contents of trace buffer", - .mhandler.info = do_info_trace, - }, -#endif { .name = "trace-events", .args_type = "", @@ -4587,6 +4675,7 @@ void monitor_init(CharDriverState *chr, int flags) if (is_first_init) { key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL); + monitor_protocol_event_init(); is_first_init = 0; } |