Add a 'target' top-unit to be compiled seperately from the common qapi modules, in order to allow poisoined target #ifdef.
The generated commands must be registered seperately. The events have a different enum, and must thus use a different limiter/emitter. Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- qapi/qapi-schema.json | 1 + qapi/target.json | 13 +++ include/qapi/qmp-event.h | 4 + monitor.c | 205 ++++++++++++++++++++++++++------------- qapi/qmp-event.c | 11 +++ Makefile | 1 + Makefile.target | 2 + 7 files changed, 170 insertions(+), 67 deletions(-) create mode 100644 qapi/target.json diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 25bce78352..6e7f85ae6d 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -93,3 +93,4 @@ { 'include': 'trace.json' } { 'include': 'introspect.json' } { 'include': 'misc.json' } +{ 'include': 'target.json' } diff --git a/qapi/target.json b/qapi/target.json new file mode 100644 index 0000000000..be2360ca34 --- /dev/null +++ b/qapi/target.json @@ -0,0 +1,13 @@ +# -*- Mode: Python -*- +# + +## +# = Target-specific commands & events +## + +{ 'pragma': { 'top-unit': 'target' } } + +## +# @DUMMY: +## +{ 'event': 'DUMMY' } diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h index 0c87ad833e..cdf3b9b970 100644 --- a/include/qapi/qmp-event.h +++ b/include/qapi/qmp-event.h @@ -21,5 +21,9 @@ void qmp_event_set_func_emit(QMPEventFuncEmit emit); QMPEventFuncEmit qmp_event_get_func_emit(void); +void target_qmp_event_set_func_emit(QMPEventFuncEmit emit); + +QMPEventFuncEmit target_qmp_event_get_func_emit(void); + QDict *qmp_event_build_dict(const char *event_name); #endif diff --git a/monitor.c b/monitor.c index 0d84c3edb4..0b96285e94 100644 --- a/monitor.c +++ b/monitor.c @@ -77,6 +77,8 @@ #include "block/qapi.h" #include "qapi/qapi-commands.h" #include "qapi/qapi-events.h" +#include "qapi/target-qapi-commands.h" +#include "qapi/target-qapi-events.h" #include "qapi/error.h" #include "qapi/qmp-event.h" #include "qapi/qapi-introspect.h" @@ -184,22 +186,25 @@ typedef struct { GQueue *qmp_responses; } MonitorQMP; +typedef struct { + GHashTable *state; + const size_t size; /* size of array == number of events */ + const int64_t rate[]; /* Minimum time (in ns) between two events */ +} MonitorQAPIEventRateLimit; + /* * 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... */ + const MonitorQAPIEventRateLimit *limiter; + unsigned 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; - struct Monitor { CharBackend chr; int reset_seen; @@ -502,23 +507,11 @@ static void monitor_qmp_bh_responder(void *opaque) } } -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, QDict *qdict) +static void monitor_qapi_event_emit(unsigned event, QDict *qdict) { Monitor *mon; @@ -538,30 +531,31 @@ static void monitor_qapi_event_handler(void *opaque); * applying any rate limiting if required. */ static void -monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) +monitor_qapi_event_queue_limit(const MonitorQAPIEventRateLimit *limiter, + unsigned event, QDict *qdict, Error **errp) { - MonitorQAPIEventConf *evconf; + int64_t rate; MonitorQAPIEventState *evstate; - assert(event < QAPI_EVENT__MAX); - evconf = &monitor_qapi_event_conf[event]; - trace_monitor_protocol_event_queue(event, qdict, evconf->rate); + assert(event < limiter->size); + rate = limiter->rate[event]; + trace_monitor_protocol_event_queue(event, qdict, rate); qemu_mutex_lock(&monitor_lock); - if (!evconf->rate) { + if (!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); + evstate = g_hash_table_lookup(limiter->state, &key); assert(!evstate || timer_pending(evstate->timer)); if (evstate) { /* - * Timer is pending for (at least) evconf->rate ns after + * Timer is pending for (at least) rate ns after * last send. Store event for sending when timer fires, * replacing a prior stored event if any. */ @@ -570,9 +564,9 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) QINCREF(evstate->qdict); } else { /* - * Last send was (at least) evconf->rate ns ago. + * Last send was (at least) rate ns ago. * Send immediately, and arm the timer to call - * monitor_qapi_event_handler() in evconf->rate ns. Any + * monitor_qapi_event_handler() in rate ns. Any * events arriving before then will be delayed until then. */ int64_t now = qemu_clock_get_ns(event_clock_type); @@ -580,6 +574,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) monitor_qapi_event_emit(event, qdict); evstate = g_new(MonitorQAPIEventState, 1); + evstate->limiter = limiter; evstate->event = event; evstate->data = data; QINCREF(evstate->data); @@ -587,44 +582,14 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) evstate->timer = timer_new_ns(event_clock_type, monitor_qapi_event_handler, evstate); - g_hash_table_add(monitor_qapi_event_state, evstate); - timer_mod_ns(evstate->timer, now + evconf->rate); + g_hash_table_add(limiter->state, evstate); + timer_mod_ns(evstate->timer, now + rate); } } qemu_mutex_unlock(&monitor_lock); } -/* - * 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(event_clock_type); - - 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); - } - - qemu_mutex_unlock(&monitor_lock); -} - static unsigned int qapi_event_throttle_hash(const void *key) { const MonitorQAPIEventState *evstate = key; @@ -663,15 +628,108 @@ static gboolean qapi_event_throttle_equal(const void *a, const void *b) return TRUE; } +static void +monitor_qapi_event_queue(unsigned event, QDict *qdict, Error **errp) +{ + static MonitorQAPIEventRateLimit limiter = { + .size = QAPI_EVENT__MAX, + .rate = { + /* 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, + [QAPI_EVENT__MAX] = 0, + }, + }; + + if (!limiter.state) { + limiter.state = g_hash_table_new(qapi_event_throttle_hash, + qapi_event_throttle_equal); + } + + monitor_qapi_event_queue_limit(&limiter, event, qdict, errp); +} + +static unsigned int target_qapi_event_throttle_hash(const void *key) +{ + const MonitorQAPIEventState *evstate = key; + unsigned int hash = evstate->event * 255; + + return hash; +} + +static gboolean target_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; + } + + return TRUE; +} + +static void +target_monitor_qapi_event_queue(unsigned event, QDict *qdict, Error **errp) +{ + static MonitorQAPIEventRateLimit limiter = { + .size = TARGET_QAPI_EVENT__MAX, + .rate = { + /* Limit guest-triggerable events to 1 per second */ + [TARGET_QAPI_EVENT__MAX] = 0, + }, + }; + + if (!limiter.state) { + limiter.state = g_hash_table_new(target_qapi_event_throttle_hash, + target_qapi_event_throttle_equal); + } + monitor_qapi_event_queue_limit(&limiter, event, qdict, errp); +} + +/* + * 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; + const MonitorQAPIEventRateLimit *limiter = evstate->limiter; + int64_t rate = limiter->rate[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(event_clock_type); + + monitor_qapi_event_emit(evstate->event, evstate->qdict); + QDECREF(evstate->qdict); + evstate->qdict = NULL; + timer_mod_ns(evstate->timer, now + rate); + } else { + g_hash_table_remove(limiter->state, evstate); + QDECREF(evstate->data); + timer_free(evstate->timer); + g_free(evstate); + } + + qemu_mutex_unlock(&monitor_lock); +} + static void monitor_qapi_event_init(void) { if (qtest_enabled()) { event_clock_type = QEMU_CLOCK_VIRTUAL; } - 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); + target_qmp_event_set_func_emit(target_monitor_qapi_event_queue); } static void handle_hmp_command(Monitor *mon, const char *cmdline); @@ -1039,9 +1097,10 @@ CommandInfoList *qmp_query_commands(Error **errp) return list; } -EventInfoList *qmp_query_events(Error **errp) +static void qmp_query_events_list(EventInfoList **list, + const QEnumLookup *qenum, int max) { - EventInfoList *info, *ev_list = NULL; + EventInfoList *info; QAPIEvent e; for (e = 0 ; e < QAPI_EVENT__MAX ; e++) { @@ -1051,11 +1110,22 @@ EventInfoList *qmp_query_events(Error **errp) info->value = g_malloc0(sizeof(*info->value)); info->value->name = g_strdup(event_name); - info->next = ev_list; - ev_list = info; + info->next = *list; + *list = info; } - return ev_list; +} + +EventInfoList *qmp_query_events(Error **errp) +{ + EventInfoList *list = NULL; + + qmp_query_events_list(&list, &QAPIEvent_lookup, + QAPI_EVENT__MAX); + qmp_query_events_list(&list, &target_QAPIEvent_lookup, + TARGET_QAPI_EVENT__MAX); + + return list; } /* @@ -1123,6 +1193,7 @@ static void monitor_init_qmp_commands(void) QTAILQ_INIT(&qmp_commands); qmp_init_marshal(&qmp_commands); + target_qmp_init_marshal(&qmp_commands); qmp_register_command(&qmp_commands, "query-qmp-schema", qmp_query_qmp_schema, diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c index 9d7e88e84a..72fd83841d 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -20,6 +20,7 @@ #include "qapi/qmp/qjson.h" static QMPEventFuncEmit qmp_emit; +static QMPEventFuncEmit target_qmp_emit; void qmp_event_set_func_emit(QMPEventFuncEmit emit) { @@ -31,6 +32,16 @@ QMPEventFuncEmit qmp_event_get_func_emit(void) return qmp_emit; } +void target_qmp_event_set_func_emit(QMPEventFuncEmit emit) +{ + target_qmp_emit = emit; +} + +QMPEventFuncEmit target_qmp_event_get_func_emit(void) +{ + return target_qmp_emit; +} + static void timestamp_put(QDict *qdict) { int err; diff --git a/Makefile b/Makefile index 727ef118f3..93a8ff5b78 100644 --- a/Makefile +++ b/Makefile @@ -588,6 +588,7 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/rocker.json \ $(SRC_PATH)/qapi/run-state.json \ $(SRC_PATH)/qapi/sockets.json \ + $(SRC_PATH)/qapi/target.json \ $(SRC_PATH)/qapi/tpm.json \ $(SRC_PATH)/qapi/trace.json \ $(SRC_PATH)/qapi/transaction.json \ diff --git a/Makefile.target b/Makefile.target index 0bb18dff9e..9db617cda9 100644 --- a/Makefile.target +++ b/Makefile.target @@ -150,6 +150,8 @@ endif GENERATED_FILES += hmp-commands.h hmp-commands-info.h +obj-y += qapi/target-qapi-types.o qapi/target-qapi-visit.o +obj-y += qapi/target-qapi-events.o qapi/target-qapi-commands.o obj-y += qapi/qapi-introspect.o endif # CONFIG_SOFTMMU -- 2.16.2.521.g9aa15f885a