From 8640d2eea6044e4a20fe4eee4f80df667ba1d87a Mon Sep 17 00:00:00 2001 Message-Id: <8640d2eea6044e4a20fe4eee4f80df667ba1d87a.1355253494.git.minovotn@redhat.com> In-Reply-To: References: From: Luiz Capitulino Date: Tue, 11 Dec 2012 15:26:27 +0100 Subject: [PATCH 5/5] Add rate limiting of RTC_CHANGE, BALLOON_CHANGE & WATCHDOG events RH-Author: Luiz Capitulino Message-id: <1355239587-12473-5-git-send-email-lcapitulino@redhat.com> Patchwork-id: 44912 O-Subject: [RHEL6.4 qemu-kvm PATCH 4/4] Add rate limiting of RTC_CHANGE, BALLOON_CHANGE & WATCHDOG events Bugzilla: 881732 RH-Acked-by: Paolo Bonzini RH-Acked-by: Kevin Wolf RH-Acked-by: Markus Armbruster From: "Daniel P. Berrange" Allow certain event types to be rate limited to avoid flooding monitor clients. The monitor_protocol_event() method is changed such that instead of immediately emitting the event to Monitor instances, it will call a new monitor_protocol_event_queue() method. This will check to see if the rate limit for the event has been exceeded, and if so schedule a timer to wakeup at the end of the rate limit period. If further events arrive before the timer fires, the previously queued event will be discarded in favour of the new event. The event will eventually be emitted when the timer fires. This logic is applied to RTC_CHANGE, BALLOON_CHANGE & WATCHDOG events, since the data associated with these events is stateless * monitor.c: Add support for rate limiting * monitor.h: Define monitor_global_init for one-time setup tasks * vl.c: Invoke monitor_global_init * trace-events: Add hooks for monitor event tracing Signed-off-by: Daniel P. Berrange Acked-by: Amit Shah Signed-off-by: Luiz Capitulino (cherry picked from commit afeecec2e8e99ba184c487633d5d0dde68a002ac) Conflicts: monitor.c Signed-off-by: Luiz Capitulino --- monitor.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- trace-events | 5 ++ 2 files changed, 156 insertions(+), 6 deletions(-) Signed-off-by: Michal Novotny --- monitor.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- trace-events | 5 ++ 2 files changed, 156 insertions(+), 6 deletions(-) diff --git a/monitor.c b/monitor.c index 6e3c8d8..4eff1ae 100644 --- a/monitor.c +++ b/monitor.c @@ -65,6 +65,7 @@ #include "ui/qemu-spice.h" #include "qmp-commands.h" #include "hmp.h" +#include "qemu-thread.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -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; @@ -457,6 +471,140 @@ static const char *monitor_event_names[] = { }; 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(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(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(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; + evstate->timer = qemu_new_timer(rt_clock, + 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 * @@ -466,7 +614,6 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) { QDict *qmp; const char *event_name; - Monitor *mon; assert(event < QEVENT_MAX); @@ -481,11 +628,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); } @@ -4972,6 +5116,7 @@ void monitor_init(CharDriverState *chr, int flags) if (is_first_init) { key_timer = qemu_new_timer(vm_clock, release_keys, NULL); + monitor_protocol_event_init(); is_first_init = 0; } diff --git a/trace-events b/trace-events index b51d13b..03a5165 100644 --- a/trace-events +++ b/trace-events @@ -277,6 +277,11 @@ disable qxl_render_update_area_done(void *cookie) "%p" # monitor.c disable handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\"" disable monitor_protocol_emitter(void *mon) "mon %p" +disable monitor_protocol_event(uint32_t event, const char *evname, void *data) "event=%d name \"%s\" data %p" +disable monitor_protocol_event_handler(uint32_t event, void *data, uint64_t last, uint64_t now) "event=%d data=%p last=%" PRId64 " now=%" PRId64 +disable monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p" +disable monitor_protocol_event_queue(uint32_t event, void *data, uint64_t rate, uint64_t last, uint64_t now) "event=%d data=%p rate=%" PRId64 " last=%" PRId64 " now=%" PRId64 +disable monitor_protocol_event_throttle(uint32_t event, uint64_t rate) "event=%d rate=%" PRId64 # hbitmap.c disable hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" -- 1.7.11.7