#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qnull.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qapi-visit-block-core.h"
return NULL;
}
- options = qobject_to_qdict(options_obj);
+ options = qobject_to(QDict, options_obj);
if (!options) {
qobject_decref(options_obj);
error_setg(errp, "Invalid JSON object given");
}
visit_complete(v, &obj);
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
/* bdrv_open_inherit() defaults to the values in bdrv_flags (for
/* See cautionary note on accessing @options above */
backing = qdict_get_try_str(options, "backing");
- if (backing && *backing == '\0') {
+ if (qobject_to(QNull, qdict_get(options, "backing")) != NULL ||
+ (backing && *backing == '\0'))
+ {
+ if (backing) {
+ warn_report("Use of \"backing\": \"\" is deprecated; "
+ "use \"backing\": null instead");
+ }
flags |= BDRV_O_NO_BACKING;
qdict_del(options, "backing");
}
cookie->type = type;
}
+/* block_latency_histogram_compare_func:
+ * Compare @key with interval [@it[0], @it[1]).
+ * Return: -1 if @key < @it[0]
+ * 0 if @key in [@it[0], @it[1])
+ * +1 if @key >= @it[1]
+ */
+static int block_latency_histogram_compare_func(const void *key, const void *it)
+{
+ uint64_t k = *(uint64_t *)key;
+ uint64_t a = ((uint64_t *)it)[0];
+ uint64_t b = ((uint64_t *)it)[1];
+
+ return k < a ? -1 : (k < b ? 0 : 1);
+}
+
+static void block_latency_histogram_account(BlockLatencyHistogram *hist,
+ int64_t latency_ns)
+{
+ uint64_t *pos;
+
+ if (hist->bins == NULL) {
+ /* histogram disabled */
+ return;
+ }
+
+
+ if (latency_ns < hist->boundaries[0]) {
+ hist->bins[0]++;
+ return;
+ }
+
+ if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
+ hist->bins[hist->nbins - 1]++;
+ return;
+ }
+
+ pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
+ sizeof(hist->boundaries[0]),
+ block_latency_histogram_compare_func);
+ assert(pos != NULL);
+
+ hist->bins[pos - hist->boundaries + 1]++;
+}
+
+int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
+ uint64List *boundaries)
+{
+ BlockLatencyHistogram *hist = &stats->latency_histogram[type];
+ uint64List *entry;
+ uint64_t *ptr;
+ uint64_t prev = 0;
+ int new_nbins = 1;
+
+ for (entry = boundaries; entry; entry = entry->next) {
+ if (entry->value <= prev) {
+ return -EINVAL;
+ }
+ new_nbins++;
+ prev = entry->value;
+ }
+
+ hist->nbins = new_nbins;
+ g_free(hist->boundaries);
+ hist->boundaries = g_new(uint64_t, hist->nbins - 1);
+ for (entry = boundaries, ptr = hist->boundaries; entry;
+ entry = entry->next, ptr++)
+ {
+ *ptr = entry->value;
+ }
+
+ g_free(hist->bins);
+ hist->bins = g_new0(uint64_t, hist->nbins);
+
+ return 0;
+}
+
+void block_latency_histograms_clear(BlockAcctStats *stats)
+{
+ int i;
+
+ for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
+ BlockLatencyHistogram *hist = &stats->latency_histogram[i];
+ g_free(hist->bins);
+ g_free(hist->boundaries);
+ memset(hist, 0, sizeof(*hist));
+ }
+}
+
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
bool failed)
{
stats->nr_ops[cookie->type]++;
}
+ block_latency_histogram_account(&stats->latency_histogram[cookie->type],
+ latency_ns);
+
if (!failed || stats->account_failed) {
stats->total_time_ns[cookie->type] += latency_ns;
stats->last_access_time_ns = time_ns;
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto done;
qapi_free_BlockInfo(info);
}
+static uint64List *uint64_list(uint64_t *list, int size)
+{
+ int i;
+ uint64List *out_list = NULL;
+ uint64List **pout_list = &out_list;
+
+ for (i = 0; i < size; i++) {
+ uint64List *entry = g_new(uint64List, 1);
+ entry->value = list[i];
+ *pout_list = entry;
+ pout_list = &entry->next;
+ }
+
+ *pout_list = NULL;
+
+ return out_list;
+}
+
+static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist,
+ bool *not_null,
+ BlockLatencyHistogramInfo **info)
+{
+ *not_null = hist->bins != NULL;
+ if (*not_null) {
+ *info = g_new0(BlockLatencyHistogramInfo, 1);
+
+ (*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1);
+ (*info)->bins = uint64_list(hist->bins, hist->nbins);
+ }
+}
+
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
{
BlockAcctStats *stats = blk_get_stats(blk);
dev_stats->avg_wr_queue_depth =
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
}
+
+ bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
+ &ds->has_x_rd_latency_histogram,
+ &ds->x_rd_latency_histogram);
+ bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
+ &ds->has_x_wr_latency_histogram,
+ &ds->x_wr_latency_histogram);
+ bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
+ &ds->has_x_flush_latency_histogram,
+ &ds->x_flush_latency_histogram);
}
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
{
switch (qobject_type(obj)) {
case QTYPE_QNUM: {
- QNum *value = qobject_to_qnum(obj);
+ QNum *value = qobject_to(QNum, obj);
char *tmp = qnum_to_string(value);
func_fprintf(f, "%s", tmp);
g_free(tmp);
break;
}
case QTYPE_QSTRING: {
- QString *value = qobject_to_qstring(obj);
+ QString *value = qobject_to(QString, obj);
func_fprintf(f, "%s", qstring_get_str(value));
break;
}
case QTYPE_QDICT: {
- QDict *value = qobject_to_qdict(obj);
+ QDict *value = qobject_to(QDict, obj);
dump_qdict(func_fprintf, f, comp_indent, value);
break;
}
case QTYPE_QLIST: {
- QList *value = qobject_to_qlist(obj);
+ QList *value = qobject_to(QList, obj);
dump_qlist(func_fprintf, f, comp_indent, value);
break;
}
case QTYPE_QBOOL: {
- QBool *value = qobject_to_qbool(obj);
+ QBool *value = qobject_to(QBool, obj);
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
break;
}
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
visit_complete(v, &obj);
- data = qdict_get(qobject_to_qdict(obj), "data");
+ data = qdict_get(qobject_to(QDict, obj), "data");
dump_qobject(func_fprintf, f, 1, data);
qobject_decref(obj);
visit_free(v);
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto fail;
/* Now get the QAPI type BlockdevCreateOptions */
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto finish;
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto fail;
if (!keypairs_json) {
return ret;
}
- keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
- &error_abort));
+ keypairs = qobject_to(QList,
+ qobject_from_json(keypairs_json, &error_abort));
remaining = qlist_size(keypairs) / 2;
assert(remaining);
while (remaining--) {
- name = qobject_to_qstring(qlist_pop(keypairs));
- value = qobject_to_qstring(qlist_pop(keypairs));
+ name = qobject_to(QString, qlist_pop(keypairs));
+ value = qobject_to(QString, qlist_pop(keypairs));
assert(name && value);
key = qstring_get_str(name);
return -EINVAL;
}
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
qdict_put_str(qdict, "driver", "sheepdog");
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto fail;
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto fail;
case QTYPE_QSTRING: {
unsigned long long length;
- const char *str = qstring_get_str(qobject_to_qstring(entry->value));
+ const char *str = qstring_get_str(qobject_to(QString,
+ entry->value));
if (parse_uint_full(str, &length, 10) == 0 &&
length > 0 && length <= UINT_MAX) {
block_acct_add_interval(stats, (unsigned) length);
}
case QTYPE_QNUM: {
- int64_t length = qnum_get_int(qobject_to_qnum(entry->value));
+ int64_t length = qnum_get_int(qobject_to(QNum, entry->value));
if (length > 0 && length <= UINT_MAX) {
block_acct_add_interval(stats, (unsigned) length);
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
QDict *qdict;
- const QDictEntry *ent;
Error *local_err = NULL;
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
}
visit_complete(v, &obj);
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
- /*
- * Rewrite "backing": null to "backing": ""
- * TODO Rewrite "" to null instead, and perhaps not even here
- */
- for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
- char *dot = strrchr(ent->key, '.');
-
- if (!strcmp(dot ? dot + 1 : ent->key, "backing")
- && qobject_type(ent->value) == QTYPE_QNULL) {
- qdict_put(qdict, ent->key, qstring_new());
- }
- }
-
if (!qdict_get_try_str(qdict, "node-name")) {
error_setg(errp, "'node-name' must be specified for the root node");
goto fail;
aio_context_release(old_context);
}
+void qmp_x_block_latency_histogram_set(
+ const char *device,
+ bool has_boundaries, uint64List *boundaries,
+ bool has_boundaries_read, uint64List *boundaries_read,
+ bool has_boundaries_write, uint64List *boundaries_write,
+ bool has_boundaries_flush, uint64List *boundaries_flush,
+ Error **errp)
+{
+ BlockBackend *blk = blk_by_name(device);
+ BlockAcctStats *stats;
+
+ if (!blk) {
+ error_setg(errp, "Device '%s' not found", device);
+ return;
+ }
+ stats = blk_get_stats(blk);
+
+ if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
+ !has_boundaries_flush)
+ {
+ block_latency_histograms_clear(stats);
+ return;
+ }
+
+ if (has_boundaries || has_boundaries_read) {
+ block_latency_histogram_set(
+ stats, BLOCK_ACCT_READ,
+ has_boundaries_read ? boundaries_read : boundaries);
+ }
+
+ if (has_boundaries || has_boundaries_write) {
+ block_latency_histogram_set(
+ stats, BLOCK_ACCT_WRITE,
+ has_boundaries_write ? boundaries_write : boundaries);
+ }
+
+ if (has_boundaries || has_boundaries_flush) {
+ block_latency_histogram_set(
+ stats, BLOCK_ACCT_FLUSH,
+ has_boundaries_flush ? boundaries_flush : boundaries);
+ }
+}
+
QemuOptsList qemu_common_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
#include "qapi/error.h"
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-sockets.h"
+#include "sysemu/sysemu.h"
#include "chardev/char-io.h"
Error *err = NULL;
gchar *name;
+ if (!machine_init_done) {
+ /* This will be postponed to machine_done notifier */
+ return;
+ }
+
if (s->is_listen) {
tioc = qio_channel_tls_new_server(
s->ioc, s->tls_creds,
tcp_chr_connect_async(chr);
}
+ if (s->ioc && s->tls_creds) {
+ tcp_chr_tls_init(chr);
+ }
+
return 0;
}
=== Commands ===
+--- General Command Layout ---
+
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
'*returns': TYPE-NAME, '*boxed': true,
- '*gen': false, '*success-response': false }
+ '*gen': false, '*success-response': false,
+ '*allow-oob': true }
Commands are defined by using a dictionary containing several members,
where three members are most common. The 'command' member is a
'success-response' with boolean value false. So far, only QGA makes
use of this member.
+A command can be declared to support Out-Of-Band (OOB) execution. By
+default, commands do not support OOB. To declare a command that
+supports it, the schema includes an extra 'allow-oob' field. For
+example:
+
+ { 'command': 'migrate_recover',
+ 'data': { 'uri': 'str' }, 'allow-oob': true }
+
+To execute a command with out-of-band priority, the client specifies
+the "control" field in the request, with "run-oob" set to
+true. Example:
+
+ => { "execute": "command-support-oob",
+ "arguments": { ... },
+ "control": { "run-oob": true } }
+ <= { "return": { } }
+
+Without it, even the commands that support out-of-band execution will
+still be run in-band.
+
+Under normal QMP command execution, the following apply to each
+command:
+
+- They are executed in order,
+- They run only in main thread of QEMU,
+- They have the BQL taken during execution.
+
+When a command is executed with OOB, the following changes occur:
+
+- They can be completed before a pending in-band command,
+- They run in a dedicated monitor thread,
+- They do not take the BQL during execution.
+
+OOB command handlers must satisfy the following conditions:
+
+- It executes extremely fast,
+- It does not take any lock, or, it can take very small locks if all
+ critical regions also follow the rules for OOB command handler code,
+- It does not invoke system calls that may block,
+- It does not access guest RAM that may block when userfaultfd is
+ enabled for postcopy live migration.
+
+If in doubt, do not implement OOB execution support.
=== Events ===
QAPI schema definitions not reachable that way are omitted.
The SchemaInfo for a command has meta-type "command", and variant
-members "arg-type" and "ret-type". On the wire, the "arguments"
-member of a client's "execute" command must conform to the object type
-named by "arg-type". The "return" member that the server passes in a
-success response conforms to the type named by "ret-type".
+members "arg-type", "ret-type" and "allow-oob". On the wire, the
+"arguments" member of a client's "execute" command must conform to the
+object type named by "arg-type". The "return" member that the server
+passes in a success response conforms to the type named by
+"ret-type". When "allow-oob" is set, it means the command supports
+out-of-band execution.
If the command takes no arguments, "arg-type" names an object type
without members. Likewise, if the command returns nothing, "ret-type"
#ifndef EXAMPLE_QMP_INTROSPECT_H
#define EXAMPLE_QMP_INTROSPECT_H
- extern const char example_qmp_schema_json[];
+ extern const QLitObject qmp_schema_qlit;
#endif
$ cat qapi-generated/example-qapi-introspect.c
[Uninteresting stuff omitted...]
- const char example_qmp_schema_json[] = "["
- "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
- "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
- "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
- "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
- "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
- "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
- "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
- "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
+ const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) {
+ QLIT_QDICT(((QLitDictEntry[]) {
+ { "arg-type", QLIT_QSTR("0") },
+ { "meta-type", QLIT_QSTR("event") },
+ { "name", QLIT_QSTR("Event") },
+ { }
+ })),
+ QLIT_QDICT(((QLitDictEntry[]) {
+ { "members", QLIT_QLIST(((QLitObject[]) {
+ { }
+ })) },
+ { "meta-type", QLIT_QSTR("object") },
+ { "name", QLIT_QSTR("0") },
+ { }
+ })),
+ ...
+ { }
+ }));
2.2.1 Capabilities
------------------
-As of the date this document was last revised, no server or client
-capability strings have been defined.
+Currently supported capabilities are:
+- "oob": the QMP server supports "Out-Of-Band" (OOB) command
+ execution. For more details, please see the "run-oob" parameter in
+ the "Issuing Commands" section below. Not all commands allow this
+ "oob" execution. The "query-qmp-schema" command can be used to
+ inspect which commands support "oob" execution.
+
+QMP clients can get a list of supported QMP capabilities of the QMP
+server in the greeting message mentioned above. By default, all the
+capabilities are off. To enable any QMP capabilities, the QMP client
+needs to send the "qmp_capabilities" command with an extra parameter
+for the requested capabilities.
2.3 Issuing Commands
--------------------
The format for command execution is:
-{ "execute": json-string, "arguments": json-object, "id": json-value }
+{ "execute": json-string, "arguments": json-object, "id": json-value,
+ "control": json-object }
Where,
required. Each command documents what contents will be considered
valid when handling the json-argument
- The "id" member is a transaction identification associated with the
- command execution, it is optional and will be part of the response if
- provided. The "id" member can be any json-value, although most
- clients merely use a json-number incremented for each successive
- command
+ command execution. It is required for all commands if the OOB -
+ capability was enabled at startup, and optional otherwise. The same
+ "id" field will be part of the response if provided. The "id" member
+ can be any json-value, although most clients merely use a
+ json-number incremented for each successive command
+- The "control" member is optional, and currently only used for
+ out-of-band execution. The handling or response of an "oob" command
+ can overtake prior in-band commands. To enable "oob" handling of a
+ particular command, just provide a control field with: { "control":
+ { "run-oob": true } }
2.4 Commands Responses
----------------------
There are two possible responses which the Server will issue as the result
of a command execution: success or error.
+As long as the commands were issued with a proper "id" field, then the
+same "id" field will be attached in the corresponding response message
+so that requests and responses can match. Clients should drop all the
+responses that have an unknown "id" field.
+
2.4.1 success
-------------
lines = g_strsplit((char *)vmci, "\n", -1);
for (i = 0; lines[i]; i++) {
- if (g_str_has_prefix(lines[i], "NUMBER(phys_base)=")) {
- if (qemu_strtou64(lines[i] + 18, NULL, 16,
+ const char *prefix = NULL;
+
+ if (s->dump_info.d_machine == EM_X86_64) {
+ prefix = "NUMBER(phys_base)=";
+ } else if (s->dump_info.d_machine == EM_AARCH64) {
+ prefix = "NUMBER(PHYS_OFFSET)=";
+ }
+
+ if (prefix && g_str_has_prefix(lines[i], prefix)) {
+ if (qemu_strtou64(lines[i] + strlen(prefix), NULL, 16,
&phys_base) < 0) {
- warn_report("Failed to read NUMBER(phys_base)=");
+ warn_report("Failed to read %s", prefix);
} else {
s->dump_info.phys_base = phys_base;
}
/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
#define BCM2836_CONTROL_BASE 0x40000000
+struct BCM283XInfo {
+ const char *name;
+ const char *cpu_type;
+ int clusterid;
+};
+
+static const BCM283XInfo bcm283x_socs[] = {
+ {
+ .name = TYPE_BCM2836,
+ .cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"),
+ .clusterid = 0xf,
+ },
+#ifdef TARGET_AARCH64
+ {
+ .name = TYPE_BCM2837,
+ .cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"),
+ .clusterid = 0x0,
+ },
+#endif
+};
+
static void bcm2836_init(Object *obj)
{
- BCM2836State *s = BCM2836(obj);
+ BCM283XState *s = BCM283X(obj);
+ BCM283XClass *bc = BCM283X_GET_CLASS(obj);
+ const BCM283XInfo *info = bc->info;
+ int n;
+
+ for (n = 0; n < BCM283X_NCPUS; n++) {
+ object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
+ info->cpu_type);
+ object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
+ &error_abort);
+ }
object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL);
object_property_add_child(obj, "control", OBJECT(&s->control), NULL);
static void bcm2836_realize(DeviceState *dev, Error **errp)
{
- BCM2836State *s = BCM2836(dev);
+ BCM283XState *s = BCM283X(dev);
+ BCM283XClass *bc = BCM283X_GET_CLASS(dev);
+ const BCM283XInfo *info = bc->info;
Object *obj;
Error *err = NULL;
int n;
/* common peripherals from bcm2835 */
- obj = OBJECT(dev);
- for (n = 0; n < BCM2836_NCPUS; n++) {
- object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
- s->cpu_type);
- object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
- &error_abort);
- }
-
obj = object_property_get_link(OBJECT(dev), "ram", &err);
if (obj == NULL) {
error_setg(errp, "%s: required ram link not found: %s",
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
- for (n = 0; n < BCM2836_NCPUS; n++) {
- /* Mirror bcm2836, which has clusterid set to 0xf
- * TODO: this should be converted to a property of ARM_CPU
- */
- s->cpus[n].mp_affinity = 0xF00 | n;
+ for (n = 0; n < BCM283X_NCPUS; n++) {
+ /* TODO: this should be converted to a property of ARM_CPU */
+ s->cpus[n].mp_affinity = (info->clusterid << 8) | n;
/* set periphbase/CBAR value for CPU-local registers */
object_property_set_int(OBJECT(&s->cpus[n]),
}
static Property bcm2836_props[] = {
- DEFINE_PROP_STRING("cpu-type", BCM2836State, cpu_type),
- DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS),
+ DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus,
+ BCM283X_NCPUS),
DEFINE_PROP_END_OF_LIST()
};
-static void bcm2836_class_init(ObjectClass *oc, void *data)
+static void bcm283x_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ BCM283XClass *bc = BCM283X_CLASS(oc);
- dc->props = bcm2836_props;
+ bc->info = data;
dc->realize = bcm2836_realize;
+ dc->props = bcm2836_props;
}
-static const TypeInfo bcm2836_type_info = {
- .name = TYPE_BCM2836,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(BCM2836State),
+static const TypeInfo bcm283x_type_info = {
+ .name = TYPE_BCM283X,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(BCM283XState),
.instance_init = bcm2836_init,
- .class_init = bcm2836_class_init,
+ .class_size = sizeof(BCM283XClass),
+ .abstract = true,
};
static void bcm2836_register_types(void)
{
- type_register_static(&bcm2836_type_info);
+ int i;
+
+ type_register_static(&bcm283x_type_info);
+ for (i = 0; i < ARRAY_SIZE(bcm283x_socs); i++) {
+ TypeInfo ti = {
+ .name = bcm283x_socs[i].name,
+ .parent = TYPE_BCM283X,
+ .class_init = bcm283x_class_init,
+ .class_data = (void *) &bcm283x_socs[i],
+ };
+ type_register(&ti);
+ }
}
type_init(bcm2836_register_types)
} else {
env->pstate = PSTATE_MODE_EL1h;
}
+ /* AArch64 kernels never boot in secure mode */
+ assert(!info->secure_boot);
+ /* This hook is only supported for AArch32 currently:
+ * bootloader_aarch64[] will not call the hook, and
+ * the code above has already dropped us into EL2 or EL1.
+ */
+ assert(!info->secure_board_setup);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ /* If we have EL2 then Linux expects the HVC insn to work */
+ env->cp15.scr_el3 |= SCR_HCE;
}
/* Set to non-secure if not a secure boot */
#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
#define FIRMWARE_ADDR_2 0x8000 /* Pi 2 loads kernel.img here by default */
#define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */
+#define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */
/* Table of Linux board IDs for different Pi versions */
static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44};
typedef struct RasPiState {
- BCM2836State soc;
+ BCM283XState soc;
MemoryRegion ram;
} RasPiState;
info->smp_loader_start);
}
+static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ /* Unlike the AArch32 version we don't need to call the board setup hook.
+ * The mechanism for doing the spin-table is also entirely different.
+ * We must have four 64-bit fields at absolute addresses
+ * 0xd8, 0xe0, 0xe8, 0xf0 in RAM, which are the flag variables for
+ * our CPUs, and which we must ensure are zero initialized before
+ * the primary CPU goes into the kernel. We put these variables inside
+ * a rom blob, so that the reset for ROM contents zeroes them for us.
+ */
+ static const uint32_t smpboot[] = {
+ 0xd2801b05, /* mov x5, 0xd8 */
+ 0xd53800a6, /* mrs x6, mpidr_el1 */
+ 0x924004c6, /* and x6, x6, #0x3 */
+ 0xd503205f, /* spin: wfe */
+ 0xf86678a4, /* ldr x4, [x5,x6,lsl #3] */
+ 0xb4ffffc4, /* cbz x4, spin */
+ 0xd2800000, /* mov x0, #0x0 */
+ 0xd2800001, /* mov x1, #0x0 */
+ 0xd2800002, /* mov x2, #0x0 */
+ 0xd2800003, /* mov x3, #0x0 */
+ 0xd61f0080, /* br x4 */
+ };
+
+ static const uint64_t spintables[] = {
+ 0, 0, 0, 0
+ };
+
+ rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+ rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),
+ SPINTABLE_ADDR);
+}
+
static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
{
arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
binfo.board_id = raspi_boardid[version];
binfo.ram_size = ram_size;
binfo.nb_cpus = smp_cpus;
- binfo.board_setup_addr = BOARDSETUP_ADDR;
- binfo.write_board_setup = write_board_setup;
- binfo.secure_board_setup = true;
- binfo.secure_boot = true;
+
+ if (version <= 2) {
+ /* The rpi1 and 2 require some custom setup code to run in Secure
+ * mode before booting a kernel (to set up the SMC vectors so
+ * that we get a no-op SMC; this is used by Linux to call the
+ * firmware for some cache maintenance operations.
+ * The rpi3 doesn't need this.
+ */
+ binfo.board_setup_addr = BOARDSETUP_ADDR;
+ binfo.write_board_setup = write_board_setup;
+ binfo.secure_board_setup = true;
+ binfo.secure_boot = true;
+ }
/* Pi2 and Pi3 requires SMP setup */
if (version >= 2) {
binfo.smp_loader_start = SMPBOOT_ADDR;
- binfo.write_secondary_boot = write_smpboot;
+ if (version == 2) {
+ binfo.write_secondary_boot = write_smpboot;
+ } else {
+ binfo.write_secondary_boot = write_smpboot64;
+ }
binfo.secondary_cpu_reset_hook = reset_secondary;
}
BusState *bus;
DeviceState *carddev;
- object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
+ object_initialize(&s->soc, sizeof(s->soc),
+ version == 3 ? TYPE_BCM2837 : TYPE_BCM2836);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
&error_abort);
/* Setup the SOC */
object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
&error_abort);
- object_property_set_str(OBJECT(&s->soc), machine->cpu_type, "cpu-type",
- &error_abort);
object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus",
&error_abort);
int board_rev = version == 3 ? 0xa02082 : 0xa21041;
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
- mc->max_cpus = BCM2836_NCPUS;
- mc->min_cpus = BCM2836_NCPUS;
- mc->default_cpus = BCM2836_NCPUS;
+ mc->max_cpus = BCM283X_NCPUS;
+ mc->min_cpus = BCM283X_NCPUS;
+ mc->default_cpus = BCM283X_NCPUS;
mc->default_ram_size = 1024 * 1024 * 1024;
mc->ignore_memory_transaction_failures = true;
};
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53");
- mc->max_cpus = BCM2836_NCPUS;
- mc->min_cpus = BCM2836_NCPUS;
- mc->default_cpus = BCM2836_NCPUS;
+ mc->max_cpus = BCM283X_NCPUS;
+ mc->min_cpus = BCM283X_NCPUS;
+ mc->default_cpus = BCM283X_NCPUS;
mc->default_ram_size = 1024 * 1024 * 1024;
}
DEFINE_MACHINE("raspi3", raspi3_machine_init)
static const VMStateDescription vmstate_imx_serial = {
.name = TYPE_IMX_SERIAL,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_INT32(readbuff, IMXSerialState),
VMSTATE_UINT32(usr1, IMXSerialState),
VMSTATE_UINT32(ubmr, IMXSerialState),
VMSTATE_UINT32(ubrc, IMXSerialState),
VMSTATE_UINT32(ucr3, IMXSerialState),
+ VMSTATE_UINT32(ucr4, IMXSerialState),
VMSTATE_END_OF_LIST()
},
};
static void imx_update(IMXSerialState *s)
{
- uint32_t flags;
+ uint32_t usr1;
+ uint32_t usr2;
+ uint32_t mask;
- flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
- if (s->ucr1 & UCR1_TXMPTYEN) {
- flags |= (s->uts1 & UTS1_TXEMPTY);
- } else {
- flags &= ~USR1_TRDY;
- }
+ /*
+ * Lucky for us TRDY and RRDY has the same offset in both USR1 and
+ * UCR1, so we can get away with something as simple as the
+ * following:
+ */
+ usr1 = s->usr1 & s->ucr1 & (USR1_TRDY | USR1_RRDY);
+ /*
+ * Bits that we want in USR2 are not as conveniently laid out,
+ * unfortunately.
+ */
+ mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0;
+ /*
+ * TCEN and TXDC are both bit 3
+ */
+ mask |= s->ucr4 & UCR4_TCEN;
- qemu_set_irq(s->irq, !!flags);
+ usr2 = s->usr2 & mask;
+
+ qemu_set_irq(s->irq, usr1 || usr2);
}
static void imx_serial_reset(IMXSerialState *s)
return s->ucr3;
case 0x23: /* UCR4 */
+ return s->ucr4;
+
case 0x29: /* BRM Incremental */
return 0x0; /* TODO */
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&s->chr, &ch, 1);
s->usr1 &= ~USR1_TRDY;
+ s->usr2 &= ~USR2_TXDC;
imx_update(s);
s->usr1 |= USR1_TRDY;
+ s->usr2 |= USR2_TXDC;
imx_update(s);
}
break;
s->ucr3 = value & 0xffff;
break;
- case 0x2d: /* UTS1 */
case 0x23: /* UCR4 */
+ s->ucr4 = value & 0xffff;
+ imx_update(s);
+ break;
+
+ case 0x2d: /* UTS1 */
qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
/* TODO */
/* Fill in optional s3/s4 related properties */
o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL);
if (o) {
- pm->s3_disabled = qnum_get_uint(qobject_to_qnum(o));
+ pm->s3_disabled = qnum_get_uint(qobject_to(QNum, o));
} else {
pm->s3_disabled = false;
}
qobject_decref(o);
o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL);
if (o) {
- pm->s4_disabled = qnum_get_uint(qobject_to_qnum(o));
+ pm->s4_disabled = qnum_get_uint(qobject_to(QNum, o));
} else {
pm->s4_disabled = false;
}
qobject_decref(o);
o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL);
if (o) {
- pm->s4_val = qnum_get_uint(qobject_to_qnum(o));
+ pm->s4_val = qnum_get_uint(qobject_to(QNum, o));
} else {
pm->s4_val = false;
}
static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
bool pcihp_bridge_en)
{
- Aml *dev, *notify_method, *method;
+ Aml *dev, *notify_method = NULL, *method;
QObject *bsel;
PCIBus *sec;
int i;
bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
if (bsel) {
- uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel));
+ uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
/* If bus supports hotplug select it and notify about local events */
if (bsel) {
- uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel));
+ uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM")));
aml_append(method,
if (!o) {
return false;
}
- mcfg->mcfg_base = qnum_get_uint(qobject_to_qnum(o));
+ mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o));
qobject_decref(o);
o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL);
assert(o);
- mcfg->mcfg_size = qnum_get_uint(qobject_to_qnum(o));
+ mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o));
qobject_decref(o);
return true;
}
static void imx_eth_update(IMXFECState *s)
{
- if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_TS_TIMER) {
+ /*
+ * Previous versions of qemu had the ENET_INT_MAC and ENET_INT_TS_TIMER
+ * interrupts swapped. This worked with older versions of Linux (4.14
+ * and older) since Linux associated both interrupt lines with Ethernet
+ * MAC interrupts. Specifically,
+ * - Linux 4.15 and later have separate interrupt handlers for the MAC and
+ * timer interrupts. Those versions of Linux fail with versions of QEMU
+ * with swapped interrupt assignments.
+ * - In linux 4.14, both interrupt lines were registered with the Ethernet
+ * MAC interrupt handler. As a result, all versions of qemu happen to
+ * work, though that is accidental.
+ * - In Linux 4.9 and older, the timer interrupt was registered directly
+ * with the Ethernet MAC interrupt handler. The MAC interrupt was
+ * redirected to a GPIO interrupt to work around erratum ERR006687.
+ * This was implemented using the SOC's IOMUX block. In qemu, this GPIO
+ * interrupt never fired since IOMUX is currently not supported in qemu.
+ * Linux instead received MAC interrupts on the timer interrupt.
+ * As a result, qemu versions with the swapped interrupt assignment work,
+ * albeit accidentally, but qemu versions with the correct interrupt
+ * assignment fail.
+ *
+ * To ensure that all versions of Linux work, generate ENET_INT_MAC
+ * interrrupts on both interrupt lines. This should be changed if and when
+ * qemu supports IOMUX.
+ */
+ if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] &
+ (ENET_INT_MAC | ENET_INT_TS_TIMER)) {
qemu_set_irq(s->irq[1], 1);
} else {
qemu_set_irq(s->irq[1], 0);
#include "qemu/timed-average.h"
#include "qemu/thread.h"
+#include "qapi/qapi-builtin-types.h"
typedef struct BlockAcctTimedStats BlockAcctTimedStats;
typedef struct BlockAcctStats BlockAcctStats;
QSLIST_ENTRY(BlockAcctTimedStats) entries;
};
+typedef struct BlockLatencyHistogram {
+ /* The following histogram is represented like this:
+ *
+ * 5| *
+ * 4| *
+ * 3| * *
+ * 2| * * *
+ * 1| * * * *
+ * +------------------
+ * 10 50 100
+ *
+ * BlockLatencyHistogram histogram = {
+ * .nbins = 4,
+ * .boundaries = {10, 50, 100},
+ * .bins = {3, 1, 5, 2},
+ * };
+ *
+ * @boundaries array define histogram intervals as follows:
+ * [0, boundaries[0]), [boundaries[0], boundaries[1]), ...
+ * [boundaries[nbins-2], +inf)
+ *
+ * So, for example above, histogram intervals are:
+ * [0, 10), [10, 50), [50, 100), [100, +inf)
+ */
+ int nbins;
+ uint64_t *boundaries; /* @nbins-1 numbers here
+ (all boundaries, except 0 and +inf) */
+ uint64_t *bins;
+} BlockLatencyHistogram;
+
struct BlockAcctStats {
QemuMutex lock;
uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
QSLIST_HEAD(, BlockAcctTimedStats) intervals;
bool account_invalid;
bool account_failed;
+ BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE];
};
typedef struct BlockAcctCookie {
int64_t block_acct_idle_time_ns(BlockAcctStats *stats);
double block_acct_queue_depth(BlockAcctTimedStats *stats,
enum BlockAcctType type);
+int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
+ uint64List *boundaries);
+void block_latency_histograms_clear(BlockAcctStats *stats);
#endif
#include "hw/arm/bcm2835_peripherals.h"
#include "hw/intc/bcm2836_control.h"
-#define TYPE_BCM2836 "bcm2836"
-#define BCM2836(obj) OBJECT_CHECK(BCM2836State, (obj), TYPE_BCM2836)
+#define TYPE_BCM283X "bcm283x"
+#define BCM283X(obj) OBJECT_CHECK(BCM283XState, (obj), TYPE_BCM283X)
+
+#define BCM283X_NCPUS 4
-#define BCM2836_NCPUS 4
+/* These type names are for specific SoCs; other than instantiating
+ * them, code using these devices should always handle them via the
+ * BCM283x base class, so they have no BCM2836(obj) etc macros.
+ */
+#define TYPE_BCM2836 "bcm2836"
+#define TYPE_BCM2837 "bcm2837"
-typedef struct BCM2836State {
+typedef struct BCM283XState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
char *cpu_type;
uint32_t enabled_cpus;
- ARMCPU cpus[BCM2836_NCPUS];
+ ARMCPU cpus[BCM283X_NCPUS];
BCM2836ControlState control;
BCM2835PeripheralState peripherals;
-} BCM2836State;
+} BCM283XState;
+
+typedef struct BCM283XInfo BCM283XInfo;
+
+typedef struct BCM283XClass {
+ DeviceClass parent_class;
+ const BCM283XInfo *info;
+} BCM283XClass;
+
+#define BCM283X_CLASS(klass) \
+ OBJECT_CLASS_CHECK(BCM283XClass, (klass), TYPE_BCM283X)
+#define BCM283X_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(BCM283XClass, (obj), TYPE_BCM283X)
#endif /* BCM2836_H */
#define FSL_IMX6_HDMI_MASTER_IRQ 115
#define FSL_IMX6_HDMI_CEC_IRQ 116
#define FSL_IMX6_MLB150_LOW_IRQ 117
-#define FSL_IMX6_ENET_MAC_1588_IRQ 118
-#define FSL_IMX6_ENET_MAC_IRQ 119
+#define FSL_IMX6_ENET_MAC_IRQ 118
+#define FSL_IMX6_ENET_MAC_1588_IRQ 119
#define FSL_IMX6_PCIE1_IRQ 120
#define FSL_IMX6_PCIE2_IRQ 121
#define FSL_IMX6_PCIE3_IRQ 122
#define UCR2_RXEN (1<<1) /* Receiver enable */
#define UCR2_SRST (1<<0) /* Reset complete */
+#define UCR4_TCEN BIT(3) /* TX complete interrupt enable */
+
#define UTS1_TXEMPTY (1<<6)
#define UTS1_RXEMPTY (1<<5)
#define UTS1_TXFULL (1<<4)
uint32_t ubmr;
uint32_t ubrc;
uint32_t ucr3;
+ uint32_t ucr4;
qemu_irq irq;
CharBackend chr;
bool monitor_cur_is_qmp(void);
-void monitor_init_qmp_commands(void);
+void monitor_init_globals(void);
void monitor_init(Chardev *chr, int flags);
void monitor_cleanup(void);
typedef enum QmpCommandOptions
{
- QCO_NO_OPTIONS = 0x0,
- QCO_NO_SUCCESS_RESP = 0x1,
+ QCO_NO_OPTIONS = 0x0,
+ QCO_NO_SUCCESS_RESP = (1U << 0),
+ QCO_ALLOW_OOB = (1U << 1),
} QmpCommandOptions;
typedef struct QmpCommand
const char *qmp_command_name(const QmpCommand *cmd);
bool qmp_has_success_response(const QmpCommand *cmd);
QObject *qmp_build_error_object(Error *err);
+QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp);
+bool qmp_is_oob(QDict *dict);
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
QBool *qbool_from_bool(bool value);
bool qbool_get_bool(const QBool *qb);
-QBool *qobject_to_qbool(const QObject *obj);
bool qbool_is_equal(const QObject *x, const QObject *y);
void qbool_destroy_obj(QObject *obj);
void qdict_del(QDict *qdict, const char *key);
int qdict_haskey(const QDict *qdict, const char *key);
QObject *qdict_get(const QDict *qdict, const char *key);
-QDict *qobject_to_qdict(const QObject *obj);
bool qdict_is_equal(const QObject *x, const QObject *y);
void qdict_iter(const QDict *qdict,
void (*iter)(const char *key, QObject *obj, void *opaque),
QObject *qlist_peek(QList *qlist);
int qlist_empty(const QList *qlist);
size_t qlist_size(const QList *qlist);
-QList *qobject_to_qlist(const QObject *obj);
bool qlist_is_equal(const QObject *x, const QObject *y);
void qlist_destroy_obj(QObject *obj);
typedef struct QLitObject QLitObject;
struct QLitObject {
- int type;
+ QType type;
union {
bool qbool;
int64_t qnum;
bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs);
+QObject *qobject_from_qlit(const QLitObject *qlit);
+
#endif /* QLIT_H */
char *qnum_to_string(QNum *qn);
-QNum *qobject_to_qnum(const QObject *obj);
bool qnum_is_equal(const QObject *x, const QObject *y);
void qnum_destroy_obj(QObject *obj);
#define QDECREF(obj) \
qobject_decref(obj ? QOBJECT(obj) : NULL)
+/* Required for qobject_to() */
+#define QTYPE_CAST_TO_QNull QTYPE_QNULL
+#define QTYPE_CAST_TO_QNum QTYPE_QNUM
+#define QTYPE_CAST_TO_QString QTYPE_QSTRING
+#define QTYPE_CAST_TO_QDict QTYPE_QDICT
+#define QTYPE_CAST_TO_QList QTYPE_QLIST
+#define QTYPE_CAST_TO_QBool QTYPE_QBOOL
+
+QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7,
+ "The QTYPE_CAST_TO_* list needs to be extended");
+
+#define qobject_to(type, obj) ({ \
+ QObject *_tmp = qobject_check_type(obj, glue(QTYPE_CAST_TO_, type)); \
+ _tmp ? container_of(_tmp, type, base) : (type *)NULL; })
+
/* Initialize an object to default values */
static inline void qobject_init(QObject *obj, QType type)
{
return obj->type;
}
+/**
+ * qobject_check_type(): Helper function for the qobject_to() macro.
+ * Return @obj, but only if @obj is not NULL and @type is equal to
+ * @obj's type. Return NULL otherwise.
+ */
+static inline QObject *qobject_check_type(const QObject *obj, QType type)
+{
+ if (obj && qobject_type(obj) == type) {
+ return (QObject *)obj;
+ } else {
+ return NULL;
+ }
+}
+
#endif /* QOBJECT_H */
QString *qstring_from_substr(const char *str, int start, int end);
size_t qstring_get_length(const QString *qstring);
const char *qstring_get_str(const QString *qstring);
+const char *qstring_get_try_str(const QString *qstring);
+const char *qobject_get_try_str(const QObject *qstring);
void qstring_append_int(QString *qstring, int64_t value);
void qstring_append(QString *qstring, const char *str);
void qstring_append_chr(QString *qstring, int c);
-QString *qobject_to_qstring(const QObject *obj);
bool qstring_is_equal(const QObject *x, const QObject *y);
void qstring_destroy_obj(QObject *obj);
int:(x) ? -1 : 1; \
}
+/* QEMU_BUILD_BUG_MSG() emits the message given if _Static_assert is
+ * supported; otherwise, it will be omitted from the compiler error
+ * message (but as it remains present in the source code, it can still
+ * be useful when debugging). */
#if defined(CONFIG_STATIC_ASSERT)
-#define QEMU_BUILD_BUG_ON(x) _Static_assert(!(x), "not expecting: " #x)
+#define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg)
#elif defined(__COUNTER__)
-#define QEMU_BUILD_BUG_ON(x) typedef QEMU_BUILD_BUG_ON_STRUCT(x) \
+#define QEMU_BUILD_BUG_MSG(x, msg) typedef QEMU_BUILD_BUG_ON_STRUCT(x) \
glue(qemu_build_bug_on__, __COUNTER__) __attribute__((unused))
#else
-#define QEMU_BUILD_BUG_ON(x)
+#define QEMU_BUILD_BUG_MSG(x, msg)
#endif
+#define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x)
+
#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \
sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)))
#include "net/net.h"
#include "net/slirp.h"
#include "chardev/char-fe.h"
+#include "chardev/char-io.h"
+#include "chardev/char-mux.h"
#include "ui/qemu-spice.h"
#include "sysemu/numa.h"
#include "monitor/monitor.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
+#include "qapi/qmp/qlist.h"
#include "qom/object_interfaces.h"
#include "trace-root.h"
#include "trace/control.h"
#include "qapi/qapi-introspect.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
+#include "sysemu/iothread.h"
#include "qemu/cutils.h"
#if defined(TARGET_S390X)
* mode.
*/
QmpCommandList *commands;
+ bool qmp_caps[QMP_CAPABILITY__MAX];
+ /*
+ * Protects qmp request/response queue. Please take monitor_lock
+ * first when used together.
+ */
+ QemuMutex qmp_queue_lock;
+ /* Input queue that holds all the parsed QMP requests */
+ GQueue *qmp_requests;
+ /* Output queue contains all the QMP responses in order */
+ GQueue *qmp_responses;
} MonitorQMP;
/*
CharBackend chr;
int reset_seen;
int flags;
- int suspend_cnt;
+ int suspend_cnt; /* Needs to be accessed atomically */
bool skip_flush;
+ bool use_io_thr;
+ /* We can't access guest memory when holding the lock */
QemuMutex out_lock;
QString *outbuf;
guint out_watch;
void *password_opaque;
mon_cmd_t *cmd_table;
QLIST_HEAD(,mon_fd_t) fds;
- QLIST_ENTRY(Monitor) entry;
+ QTAILQ_ENTRY(Monitor) entry;
};
+/* Let's add monitor global variables to this struct. */
+static struct {
+ IOThread *mon_iothread;
+ /* Bottom half to dispatch the requests received from IO thread */
+ QEMUBH *qmp_dispatcher_bh;
+ /* Bottom half to deliver the responses back to clients */
+ QEMUBH *qmp_respond_bh;
+} mon_global;
+
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
/* Protects mon_list, monitor_event_state. */
static QemuMutex monitor_lock;
-static QLIST_HEAD(mon_list, Monitor) mon_list;
+static QTAILQ_HEAD(mon_list, Monitor) mon_list;
static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets;
static int mon_refcount;
return (mon->flags & MONITOR_USE_CONTROL);
}
+/**
+ * Whether @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);
+}
+
/**
* Is the current monitor, if any, a QMP monitor?
*/
return 0;
}
-static void monitor_json_emitter(Monitor *mon, const QObject *data)
+static void monitor_json_emitter_raw(Monitor *mon,
+ QObject *data)
{
QString *json;
QDECREF(json);
}
+static void monitor_json_emitter(Monitor *mon, QObject *data)
+{
+ if (mon->use_io_thr) {
+ /*
+ * If using IO thread, we need to queue the item so that IO
+ * thread will do the rest for us. Take refcount so that
+ * caller won't free the data (which will be finally freed in
+ * responder thread).
+ */
+ qobject_incref(data);
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ g_queue_push_tail(mon->qmp.qmp_responses, (void *)data);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ qemu_bh_schedule(mon_global.qmp_respond_bh);
+ } else {
+ /*
+ * If not using monitor IO thread, then we are in main thread.
+ * Do the emission right away.
+ */
+ monitor_json_emitter_raw(mon, data);
+ }
+}
+
+struct QMPResponse {
+ Monitor *mon;
+ QObject *data;
+};
+typedef struct QMPResponse QMPResponse;
+
+/*
+ * Return one QMPResponse. The response is only valid if
+ * response.data is not NULL.
+ */
+static QMPResponse monitor_qmp_response_pop_one(void)
+{
+ Monitor *mon;
+ QObject *data = NULL;
+
+ qemu_mutex_lock(&monitor_lock);
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ data = g_queue_pop_head(mon->qmp.qmp_responses);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ if (data) {
+ break;
+ }
+ }
+ qemu_mutex_unlock(&monitor_lock);
+ return (QMPResponse) { .mon = mon, .data = data };
+}
+
+static void monitor_qmp_bh_responder(void *opaque)
+{
+ QMPResponse response;
+
+ while (true) {
+ response = monitor_qmp_response_pop_one();
+ if (!response.data) {
+ break;
+ }
+ monitor_json_emitter_raw(response.mon, response.data);
+ qobject_decref(response.data);
+ }
+}
+
static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
/* Limit guest-triggerable events to 1 per second */
[QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
Monitor *mon;
trace_monitor_protocol_event_emit(event, qdict);
- QLIST_FOREACH(mon, &mon_list, entry) {
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
if (monitor_is_qmp(mon)
&& mon->qmp.commands != &qmp_cap_negotiation_commands) {
monitor_json_emitter(mon, QOBJECT(qdict));
/* Unthrottled event */
monitor_qapi_event_emit(event, qdict);
} else {
- QDict *data = qobject_to_qdict(qdict_get(qdict, "data"));
+ 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);
static void handle_hmp_command(Monitor *mon, const char *cmdline);
-static void monitor_data_init(Monitor *mon)
+static void monitor_data_init(Monitor *mon, bool skip_flush,
+ bool use_io_thr)
{
memset(mon, 0, sizeof(Monitor));
qemu_mutex_init(&mon->out_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_thr = use_io_thr;
+ mon->qmp.qmp_requests = g_queue_new();
+ mon->qmp.qmp_responses = g_queue_new();
}
static void monitor_data_destroy(Monitor *mon)
readline_free(mon->rs);
QDECREF(mon->outbuf);
qemu_mutex_destroy(&mon->out_lock);
+ qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
+ g_queue_free(mon->qmp.qmp_requests);
+ g_queue_free(mon->qmp.qmp_responses);
}
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
char *output = NULL;
Monitor *old_mon, hmp;
- monitor_data_init(&hmp);
- hmp.skip_flush = true;
+ monitor_data_init(&hmp, true, false);
old_mon = cur_mon;
cur_mon = &hmp;
static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp)
{
- *ret_data = qobject_from_json(qmp_schema_json, &error_abort);
+ *ret_data = qobject_from_qlit(&qmp_schema_qlit);
}
/*
#endif
}
-void monitor_init_qmp_commands(void)
+static void monitor_init_qmp_commands(void)
{
/*
* Two command lists:
qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
}
-void qmp_qmp_capabilities(Error **errp)
+static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap)
+{
+ return mon->qmp.qmp_caps[cap];
+}
+
+static bool qmp_oob_enabled(Monitor *mon)
+{
+ return qmp_cap_enabled(mon, QMP_CAPABILITY_OOB);
+}
+
+static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list,
+ Error **errp)
+{
+ for (; list; list = list->next) {
+ assert(list->value < QMP_CAPABILITY__MAX);
+ switch (list->value) {
+ case QMP_CAPABILITY_OOB:
+ if (!mon->use_io_thr) {
+ /*
+ * Out-Of-Band only works with monitors that are
+ * running on dedicated IOThread.
+ */
+ error_setg(errp, "This monitor does not support "
+ "Out-Of-Band (OOB)");
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* This function should only be called after capabilities are checked. */
+static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list)
+{
+ for (; list; list = list->next) {
+ mon->qmp.qmp_caps[list->value] = true;
+ }
+}
+
+/*
+ * Return true if check successful, or false otherwise. When false is
+ * returned, detailed error will be in errp if provided.
+ */
+static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
+{
+ const char *command;
+ QmpCommand *cmd;
+
+ command = qdict_get_try_str(req, "execute");
+ if (!command) {
+ error_setg(errp, "Command field 'execute' missing");
+ return false;
+ }
+
+ cmd = qmp_find_command(mon->qmp.commands, command);
+ if (!cmd) {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "The command %s has not been found", command);
+ return false;
+ }
+
+ if (qmp_is_oob(req)) {
+ if (!qmp_oob_enabled(mon)) {
+ error_setg(errp, "Please enable Out-Of-Band first "
+ "for the session during capabilities negotiation");
+ return false;
+ }
+ if (!(cmd->options & QCO_ALLOW_OOB)) {
+ error_setg(errp, "The command %s does not support OOB",
+ command);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
+ Error **errp)
{
+ Error *local_err = NULL;
+
if (cur_mon->qmp.commands == &qmp_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Capabilities negotiation is already complete, command "
return;
}
+ /* Enable QMP capabilities provided by the client if applicable. */
+ if (has_enable) {
+ qmp_caps_check(cur_mon, enable, &local_err);
+ if (local_err) {
+ /*
+ * Failed check on any of the capabilities will fail the
+ * entire command (and thus not apply any of the other
+ * capabilities that were also requested).
+ */
+ error_propagate(errp, local_err);
+ return;
+ }
+ qmp_caps_apply(cur_mon, enable);
+ }
+
cur_mon->qmp.commands = &qmp_commands;
}
{
Monitor *mon = opaque;
- return (mon->suspend_cnt == 0) ? 1 : 0;
+ return !atomic_mb_read(&mon->suspend_cnt);
}
-static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
+/*
+ * 1. This function takes ownership of rsp, err, and id.
+ * 2. rsp, err, and id may be NULL.
+ * 3. If err != NULL then rsp must be NULL.
+ */
+static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
+ Error *err, QObject *id)
{
- QObject *req, *rsp = NULL, *id = NULL;
QDict *qdict = NULL;
- Monitor *mon = cur_mon;
- Error *err = NULL;
- req = json_parser_parse_err(tokens, NULL, &err);
- if (!req && !err) {
- /* json_parser_parse_err() sucks: can fail without setting @err */
- error_setg(&err, QERR_JSON_PARSING);
- }
if (err) {
- goto err_out;
+ assert(!rsp);
+ qdict = qdict_new();
+ qdict_put_obj(qdict, "error", qmp_build_error_object(err));
+ error_free(err);
+ rsp = QOBJECT(qdict);
+ }
+
+ if (rsp) {
+ if (id) {
+ /* This is for the qdict below. */
+ qobject_incref(id);
+ qdict_put_obj(qobject_to(QDict, rsp), "id", id);
+ }
+
+ monitor_json_emitter(mon, rsp);
}
- qdict = qobject_to_qdict(req);
- if (qdict) {
- id = qdict_get(qdict, "id");
- qobject_incref(id);
- qdict_del(qdict, "id");
- } /* else will fail qmp_dispatch() */
+ qobject_decref(id);
+ qobject_decref(rsp);
+}
+
+struct QMPRequest {
+ /* Owner of the request */
+ Monitor *mon;
+ /* "id" field of the request */
+ QObject *id;
+ /* Request object to be handled */
+ QObject *req;
+ /*
+ * Whether we need to resume the monitor afterward. This flag is
+ * used to emulate the old QMP server behavior that the current
+ * command must be completed before execution of the next one.
+ */
+ bool need_resume;
+};
+typedef struct QMPRequest QMPRequest;
+
+/*
+ * Dispatch one single QMP request. The function will free the req_obj
+ * and objects inside it before return.
+ */
+static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
+{
+ Monitor *mon, *old_mon;
+ QObject *req, *rsp = NULL, *id;
+ QDict *qdict = NULL;
+ bool need_resume;
+
+ req = req_obj->req;
+ mon = req_obj->mon;
+ id = req_obj->id;
+ need_resume = req_obj->need_resume;
+
+ g_free(req_obj);
if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
QString *req_json = qobject_to_json(req);
QDECREF(req_json);
}
- rsp = qmp_dispatch(cur_mon->qmp.commands, req);
+ old_mon = cur_mon;
+ cur_mon = mon;
+
+ rsp = qmp_dispatch(mon->qmp.commands, req);
+
+ cur_mon = old_mon;
if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
- qdict = qdict_get_qdict(qobject_to_qdict(rsp), "error");
+ qdict = qdict_get_qdict(qobject_to(QDict, rsp), "error");
if (qdict
&& !g_strcmp0(qdict_get_try_str(qdict, "class"),
QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
}
}
-err_out:
- if (err) {
- qdict = qdict_new();
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
- rsp = QOBJECT(qdict);
+ /* Respond if necessary */
+ monitor_qmp_respond(mon, rsp, NULL, id);
+
+ /* This pairs with the monitor_suspend() in handle_qmp_command(). */
+ if (need_resume) {
+ monitor_resume(mon);
}
- if (rsp) {
- if (id) {
- qdict_put_obj(qobject_to_qdict(rsp), "id", id);
- id = NULL;
+ qobject_decref(req);
+}
+
+/*
+ * Pop one QMP request from monitor queues, return NULL if not found.
+ * 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.
+ */
+static QMPRequest *monitor_qmp_requests_pop_one(void)
+{
+ QMPRequest *req_obj = NULL;
+ Monitor *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);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ if (req_obj) {
+ break;
}
+ }
- monitor_json_emitter(mon, rsp);
+ 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);
}
- qobject_decref(id);
- qobject_decref(rsp);
- qobject_decref(req);
+ qemu_mutex_unlock(&monitor_lock);
+
+ return req_obj;
}
-static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
+static void monitor_qmp_bh_dispatcher(void *data)
{
- Monitor *old_mon = cur_mon;
+ QMPRequest *req_obj = monitor_qmp_requests_pop_one();
- cur_mon = opaque;
+ if (req_obj) {
+ trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
+ monitor_qmp_dispatch_one(req_obj);
+ /* Reschedule instead of looping so the main loop stays responsive */
+ qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
+ }
+}
- json_message_parser_feed(&cur_mon->qmp.parser, (const char *) buf, size);
+#define QMP_REQ_QUEUE_LEN_MAX (8)
- cur_mon = old_mon;
+static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
+{
+ QObject *req, *id = NULL;
+ QDict *qdict = NULL;
+ MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser);
+ Monitor *mon = container_of(mon_qmp, Monitor, qmp);
+ Error *err = NULL;
+ QMPRequest *req_obj;
+
+ req = json_parser_parse_err(tokens, NULL, &err);
+ if (!req && !err) {
+ /* json_parser_parse_err() sucks: can fail without setting @err */
+ error_setg(&err, QERR_JSON_PARSING);
+ }
+ if (err) {
+ goto err;
+ }
+
+ /* Check against the request in general layout */
+ qdict = qmp_dispatch_check_obj(req, &err);
+ if (!qdict) {
+ goto err;
+ }
+
+ /* Check against OOB specific */
+ if (!qmp_cmd_oob_check(mon, qdict, &err)) {
+ goto err;
+ }
+
+ id = qdict_get(qdict, "id");
+
+ /* When OOB is enabled, the "id" field is mandatory. */
+ if (qmp_oob_enabled(mon) && !id) {
+ error_setg(&err, "Out-Of-Band capability requires that "
+ "every command contains an 'id' field");
+ goto err;
+ }
+
+ qobject_incref(id);
+ qdict_del(qdict, "id");
+
+ req_obj = g_new0(QMPRequest, 1);
+ req_obj->mon = mon;
+ req_obj->id = id;
+ req_obj->req = req;
+ req_obj->need_resume = false;
+
+ if (qmp_is_oob(qdict)) {
+ /* Out-Of-Band (OOB) requests are executed directly in parser. */
+ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id)
+ ?: "");
+ monitor_qmp_dispatch_one(req_obj);
+ return;
+ }
+
+ /* Protect qmp_requests and fetching its length. */
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+
+ /*
+ * If OOB is not enabled on the current monitor, we'll emulate the
+ * old behavior that we won't process the current monitor any more
+ * until it has responded. This helps make sure that as long as
+ * OOB is not enabled, the server will never drop any command.
+ */
+ if (!qmp_oob_enabled(mon)) {
+ monitor_suspend(mon);
+ req_obj->need_resume = true;
+ } else {
+ /* Drop the request if queue is full. */
+ if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) {
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ qapi_event_send_command_dropped(id,
+ COMMAND_DROP_REASON_QUEUE_FULL,
+ &error_abort);
+ qobject_decref(id);
+ qobject_decref(req);
+ g_free(req_obj);
+ return;
+ }
+ }
+
+ /*
+ * Put the request to the end of queue so that requests will be
+ * handled in time order. Ownership for req_obj, req, id,
+ * etc. will be delivered to the handler side.
+ */
+ g_queue_push_tail(mon->qmp.qmp_requests, req_obj);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+
+ /* Kick the dispatcher routine */
+ qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
+ return;
+
+err:
+ monitor_qmp_respond(mon, NULL, err, NULL);
+ qobject_decref(req);
+}
+
+static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
+{
+ Monitor *mon = opaque;
+
+ json_message_parser_feed(&mon->qmp.parser, (const char *) buf, size);
}
static void monitor_read(void *opaque, const uint8_t *buf, int size)
int monitor_suspend(Monitor *mon)
{
- if (!mon->rs)
+ if (monitor_is_hmp_non_interactive(mon)) {
return -ENOTTY;
- mon->suspend_cnt++;
+ }
+
+ atomic_inc(&mon->suspend_cnt);
+
+ if (monitor_is_qmp(mon)) {
+ /*
+ * Kick iothread to make sure this takes effect. It'll be
+ * evaluated again in prepare() of the watch object.
+ */
+ aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
+ }
+
+ trace_monitor_suspend(mon, 1);
return 0;
}
void monitor_resume(Monitor *mon)
{
- if (!mon->rs)
+ if (monitor_is_hmp_non_interactive(mon)) {
return;
- if (--mon->suspend_cnt == 0)
- readline_show_prompt(mon->rs);
+ }
+
+ if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
+ if (monitor_is_qmp(mon)) {
+ /*
+ * For QMP monitors that are running in IOThread, let's
+ * kick the thread in case it's sleeping.
+ */
+ if (mon->use_io_thr) {
+ aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
+ }
+ } else {
+ assert(mon->rs);
+ readline_show_prompt(mon->rs);
+ }
+ }
+ trace_monitor_suspend(mon, -1);
}
-static QObject *get_qmp_greeting(void)
+static QObject *get_qmp_greeting(Monitor *mon)
{
+ QList *cap_list = qlist_new();
QObject *ver = NULL;
+ QMPCapability cap;
qmp_marshal_query_version(NULL, &ver, NULL);
- return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []}}",
- ver);
+ for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
+ if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) {
+ /* Monitors that are not using IOThread won't support OOB */
+ continue;
+ }
+ qlist_append(cap_list, qstring_from_str(QMPCapability_str(cap)));
+ }
+
+ return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
+ ver, cap_list);
+}
+
+static void monitor_qmp_caps_reset(Monitor *mon)
+{
+ memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps));
}
static void monitor_qmp_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_OPENED:
mon->qmp.commands = &qmp_cap_negotiation_commands;
- data = get_qmp_greeting();
+ monitor_qmp_caps_reset(mon);
+ data = get_qmp_greeting(mon);
monitor_json_emitter(mon, data);
qobject_decref(data);
mon_refcount++;
monitor_resume(mon);
monitor_flush(mon);
} else {
- mon->suspend_cnt = 0;
+ atomic_mb_set(&mon->suspend_cnt, 0);
}
break;
case CHR_EVENT_MUX_OUT:
if (mon->reset_seen) {
- if (mon->suspend_cnt == 0) {
+ if (atomic_mb_read(&mon->suspend_cnt) == 0) {
monitor_printf(mon, "\n");
}
monitor_flush(mon);
monitor_suspend(mon);
} else {
- mon->suspend_cnt++;
+ atomic_inc(&mon->suspend_cnt);
}
qemu_mutex_lock(&mon->out_lock);
mon->mux_out = 1;
qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd);
}
+static GMainContext *monitor_get_io_context(void)
+{
+ return iothread_get_g_main_context(mon_global.mon_iothread);
+}
+
+static AioContext *monitor_get_aio_context(void)
+{
+ return iothread_get_aio_context(mon_global.mon_iothread);
+}
+
+static void monitor_iothread_init(void)
+{
+ mon_global.mon_iothread = iothread_create("mon_iothread",
+ &error_abort);
+
+ /*
+ * This MUST be on main loop thread since we have commands that
+ * have assumption to be run on main loop thread. It would be
+ * nice that one day we can remove this assumption in the future.
+ */
+ mon_global.qmp_dispatcher_bh = aio_bh_new(qemu_get_aio_context(),
+ monitor_qmp_bh_dispatcher,
+ NULL);
+
+ /*
+ * Unlike the dispatcher BH, this must be run on the monitor IO
+ * thread, so that monitors that are using IO thread will make
+ * sure read/write operations are all done on the IO thread.
+ */
+ mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(),
+ monitor_qmp_bh_responder,
+ NULL);
+}
+
+void monitor_init_globals(void)
+{
+ monitor_init_qmp_commands();
+ monitor_qapi_event_init();
+ sortcmdlist();
+ qemu_mutex_init(&monitor_lock);
+ monitor_iothread_init();
+}
+
/* These functions just adapt the readline interface in a typesafe way. We
* could cast function pointers but that discards compiler checks.
*/
}
}
-static void __attribute__((constructor)) monitor_lock_init(void)
+static void monitor_list_append(Monitor *mon)
{
- qemu_mutex_init(&monitor_lock);
+ qemu_mutex_lock(&monitor_lock);
+ QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
+ qemu_mutex_unlock(&monitor_lock);
}
-void monitor_init(Chardev *chr, int flags)
+static void monitor_qmp_setup_handlers_bh(void *opaque)
{
- static int is_first_init = 1;
- Monitor *mon;
-
- if (is_first_init) {
- monitor_qapi_event_init();
- sortcmdlist();
- is_first_init = 0;
+ Monitor *mon = opaque;
+ GMainContext *context;
+
+ if (mon->use_io_thr) {
+ /*
+ * When use_io_thr is set, we use the global shared dedicated
+ * IO thread for this monitor to handle input/output.
+ */
+ context = monitor_get_io_context();
+ /* We should have inited globals before reaching here. */
+ assert(context);
+ } else {
+ /* The default main loop, which is the main thread */
+ context = NULL;
}
- mon = g_malloc(sizeof(*mon));
- monitor_data_init(mon);
+ qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
+ monitor_qmp_event, NULL, mon, context, true);
+ monitor_list_append(mon);
+}
+
+void monitor_init(Chardev *chr, int flags)
+{
+ Monitor *mon = g_malloc(sizeof(*mon));
+ /* Enable IOThread for QMPs that are not using MUX chardev backends. */
+ bool use_io_thr = (!CHARDEV_IS_MUX(chr)) && (flags & MONITOR_USE_CONTROL);
+
+ monitor_data_init(mon, false, use_io_thr);
qemu_chr_fe_init(&mon->chr, chr, &error_abort);
mon->flags = flags;
}
if (monitor_is_qmp(mon)) {
- qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
- monitor_qmp_event, NULL, mon, NULL, true);
qemu_chr_fe_set_echo(&mon->chr, true);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
+ if (mon->use_io_thr) {
+ /*
+ * 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 during the procedure the chardev will be active
+ * and running in monitor iothread, while we'll still do
+ * something before returning from it, which is a possible
+ * race too. To avoid that, we just create a BH to setup
+ * the handlers.
+ */
+ aio_bh_schedule_oneshot(monitor_get_aio_context(),
+ monitor_qmp_setup_handlers_bh, mon);
+ /* We'll add this to mon_list in the BH when setup done */
+ return;
+ } else {
+ qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
+ monitor_qmp_read, monitor_qmp_event,
+ NULL, mon, NULL, true);
+ }
} else {
qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
monitor_event, NULL, mon, NULL, true);
}
- qemu_mutex_lock(&monitor_lock);
- QLIST_INSERT_HEAD(&mon_list, mon, entry);
- qemu_mutex_unlock(&monitor_lock);
+ monitor_list_append(mon);
}
void monitor_cleanup(void)
{
Monitor *mon, *next;
+ /*
+ * We need to explicitly stop the iothread (but not destroy it),
+ * cleanup the monitor resources, then destroy the iothread since
+ * we need to unregister from chardev below in
+ * monitor_data_destroy(), and chardev is not thread-safe yet
+ */
+ iothread_stop(mon_global.mon_iothread);
+
+ /*
+ * After we have IOThread to send responses, it's possible that
+ * when we stop the IOThread there are still replies queued in the
+ * responder queue. Flush all of them. Note that even after this
+ * flush it's still possible that out buffer is not flushed.
+ * It'll be done in below monitor_flush() as the last resort.
+ */
+ monitor_qmp_bh_responder(NULL);
+
qemu_mutex_lock(&monitor_lock);
- QLIST_FOREACH_SAFE(mon, &mon_list, entry, next) {
- QLIST_REMOVE(mon, entry);
+ QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) {
+ QTAILQ_REMOVE(&mon_list, mon, entry);
+ monitor_flush(mon);
monitor_data_destroy(mon);
g_free(mon);
}
qemu_mutex_unlock(&monitor_lock);
+
+ /* QEMUBHs needs to be deleted before destroying the IOThread. */
+ qemu_bh_delete(mon_global.qmp_dispatcher_bh);
+ mon_global.qmp_dispatcher_bh = NULL;
+ qemu_bh_delete(mon_global.qmp_respond_bh);
+ mon_global.qmp_respond_bh = NULL;
+
+ iothread_destroy(mon_global.mon_iothread);
+ mon_global.mon_iothread = NULL;
}
QemuOptsList qemu_mon_opts = {
'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32',
'status': 'DirtyBitmapStatus'} }
+##
+# @BlockLatencyHistogramInfo:
+#
+# Block latency histogram.
+#
+# @boundaries: list of interval boundary values in nanoseconds, all greater
+# than zero and in ascending order.
+# For example, the list [10, 50, 100] produces the following
+# histogram intervals: [0, 10), [10, 50), [50, 100), [100, +inf).
+#
+# @bins: list of io request counts corresponding to histogram intervals.
+# len(@bins) = len(@boundaries) + 1
+# For the example above, @bins may be something like [3, 1, 5, 2],
+# and corresponding histogram looks like:
+#
+# 5| *
+# 4| *
+# 3| * *
+# 2| * * *
+# 1| * * * *
+# +------------------
+# 10 50 100
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockLatencyHistogramInfo',
+ 'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
+
+##
+# @x-block-latency-histogram-set:
+#
+# Manage read, write and flush latency histograms for the device.
+#
+# If only @device parameter is specified, remove all present latency histograms
+# for the device. Otherwise, add/reset some of (or all) latency histograms.
+#
+# @device: device name to set latency histogram for.
+#
+# @boundaries: list of interval boundary values (see description in
+# BlockLatencyHistogramInfo definition). If specified, all
+# latency histograms are removed, and empty ones created for all
+# io types with intervals corresponding to @boundaries (except for
+# io types, for which specific boundaries are set through the
+# following parameters).
+#
+# @boundaries-read: list of interval boundary values for read latency
+# histogram. If specified, old read latency histogram is
+# removed, and empty one created with intervals
+# corresponding to @boundaries-read. The parameter has higher
+# priority then @boundaries.
+#
+# @boundaries-write: list of interval boundary values for write latency
+# histogram.
+#
+# @boundaries-flush: list of interval boundary values for flush latency
+# histogram.
+#
+# Returns: error if device is not found or any boundary arrays are invalid.
+#
+# Since: 2.12
+#
+# Example: set new histograms for all io types with intervals
+# [0, 10), [10, 50), [50, 100), [100, +inf):
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0",
+# "boundaries": [10, 50, 100] } }
+# <- { "return": {} }
+#
+# Example: set new histogram only for write, other histograms will remain
+# not changed (or not created):
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0",
+# "boundaries-write": [10, 50, 100] } }
+# <- { "return": {} }
+#
+# Example: set new histograms with the following intervals:
+# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf)
+# write: [0, 1000), [1000, 5000), [5000, +inf)
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0",
+# "boundaries": [10, 50, 100],
+# "boundaries-write": [1000, 5000] } }
+# <- { "return": {} }
+#
+# Example: remove all latency histograms:
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0" } }
+# <- { "return": {} }
+##
+{ 'command': 'x-block-latency-histogram-set',
+ 'data': {'device': 'str',
+ '*boundaries': ['uint64'],
+ '*boundaries-read': ['uint64'],
+ '*boundaries-write': ['uint64'],
+ '*boundaries-flush': ['uint64'] } }
+
##
# @BlockInfo:
#
# @timed_stats: Statistics specific to the set of previously defined
# intervals of time (Since 2.5)
#
+# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+#
+# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+#
+# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+#
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
'account_invalid': 'bool', 'account_failed': 'bool',
- 'timed_stats': ['BlockDeviceTimedStats'] } }
+ 'timed_stats': ['BlockDeviceTimedStats'],
+ '*x_rd_latency_histogram': 'BlockLatencyHistogramInfo',
+ '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
+ '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
##
# @BlockStats:
# @overlay: reference to the existing block device that will become
# the overlay of @node, as part of creating the snapshot.
# It must not have a current backing file (this can be
-# achieved by passing "backing": "" to blockdev-add).
+# achieved by passing "backing": null to blockdev-add).
#
# Since: 2.5
##
# "node-name": "node1534",
# "file": { "driver": "file",
# "filename": "hd1.qcow2" },
-# "backing": "" } }
+# "backing": null } }
#
# <- { "return": {} }
#
#
# @ret-type: the name of the command's result type.
#
+# @allow-oob: whether the command allows out-of-band execution.
+# (Since: 2.12)
+#
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
- 'data': { 'arg-type': 'str', 'ret-type': 'str' } }
+ 'data': { 'arg-type': 'str', 'ret-type': 'str',
+ 'allow-oob': 'bool' } }
##
# @SchemaInfoEvent:
#
# Enable QMP capabilities.
#
-# Arguments: None.
+# Arguments:
+#
+# @enable: An optional list of QMPCapability values to enable. The
+# client must not enable any capability that is not
+# mentioned in the QMP greeting message. If the field is not
+# provided, it means no QMP capabilities will be enabled.
+# (since 2.12)
#
# Example:
#
-# -> { "execute": "qmp_capabilities" }
+# -> { "execute": "qmp_capabilities",
+# "arguments": { "enable": [ "oob" ] } }
# <- { "return": {} }
#
# Notes: This command is valid exactly when first connecting: it must be
# issued before any other command will be accepted, and will fail once the
# monitor is accepting other commands. (see qemu docs/interop/qmp-spec.txt)
#
+# The QMP client needs to explicitly enable QMP capabilities, otherwise
+# all the QMP capabilities will be turned off by default.
+#
# Since: 0.13
#
##
-{ 'command': 'qmp_capabilities' }
+{ 'command': 'qmp_capabilities',
+ 'data': { '*enable': [ 'QMPCapability' ] } }
+
+##
+# @QMPCapability:
+#
+# Enumeration of capabilities to be advertised during initial client
+# connection, used for agreeing on particular QMP extension behaviors.
+#
+# @oob: QMP ability to support Out-Of-Band requests.
+# (Please refer to qmp-spec.txt for more information on OOB)
+#
+# Since: 2.12
+#
+##
+{ 'enum': 'QMPCapability',
+ 'data': [ 'oob' ] }
##
# @VersionTriple:
#
##
{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability' }
+
+##
+# @CommandDropReason:
+#
+# Reasons that caused one command to be dropped.
+#
+# @queue-full: the command queue is full. This can only occur when
+# the client sends a new non-oob command before the
+# response to the previous non-oob command has been
+# received.
+#
+# Since: 2.12
+##
+{ 'enum': 'CommandDropReason',
+ 'data': [ 'queue-full' ] }
+
+##
+# @COMMAND_DROPPED:
+#
+# Emitted when a command is dropped due to some reason. Commands can
+# only be dropped when the oob capability is enabled.
+#
+# @id: The dropped command's "id" field.
+#
+# @reason: The reason why the command is dropped.
+#
+# Since: 2.12
+#
+# Example:
+#
+# { "event": "COMMAND_DROPPED",
+# "data": {"result": {"id": "libvirt-102",
+# "reason": "queue-full" } } }
+#
+##
+{ 'event': 'COMMAND_DROPPED' ,
+ 'data': { 'id': 'any', 'reason': 'CommandDropReason' } }
+
+##
+# @x-oob-test:
+#
+# Test OOB functionality. When sending this command with lock=true,
+# it'll try to hang the dispatcher. When sending it with lock=false,
+# it'll try to notify the locked thread to continue. Note: it should
+# only be used by QMP test program rather than anything else.
+#
+# Since: 2.12
+#
+# Example:
+#
+# { "execute": "x-oob-test",
+# "arguments": { "lock": true } }
+##
+{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' },
+ 'allow-oob': true }
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qbool.h"
-static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
+QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
{
const QDictEntry *ent;
const char *arg_name;
bool has_exec_key = false;
QDict *dict = NULL;
- dict = qobject_to_qdict(request);
+ dict = qobject_to(QDict, request);
if (!dict) {
error_setg(errp, "QMP input must be a JSON object");
return NULL;
"QMP input member 'arguments' must be an object");
return NULL;
}
+ } else if (!strcmp(arg_name, "id")) {
+ continue;
+ } else if (!strcmp(arg_name, "control")) {
+ if (qobject_type(arg_obj) != QTYPE_QDICT) {
+ error_setg(errp,
+ "QMP input member 'control' must be a dict");
+ return NULL;
+ }
} else {
error_setg(errp, "QMP input member '%s' is unexpected",
arg_name);
error_get_pretty(err));
}
+/*
+ * Detect whether a request should be run out-of-band, by quickly
+ * peeking at whether we have: { "control": { "run-oob": true } }. By
+ * default commands are run in-band.
+ */
+bool qmp_is_oob(QDict *dict)
+{
+ QBool *bool_obj;
+
+ dict = qdict_get_qdict(dict, "control");
+ if (!dict) {
+ return false;
+ }
+
+ bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob"));
+ if (!bool_obj) {
+ return false;
+ }
+
+ return qbool_get_bool(bool_obj);
+}
+
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
{
Error *err = NULL;
if (qobject_type(qobj) == QTYPE_QDICT) {
assert(name);
- ret = qdict_get(qobject_to_qdict(qobj), name);
+ ret = qdict_get(qobject_to(QDict, qobj), name);
if (tos->h && consume && ret) {
bool removed = g_hash_table_remove(tos->h, name);
assert(removed);
return NULL;
}
- qstr = qobject_to_qstring(qobj);
+ qstr = qobject_to(QString, qobj);
if (!qstr) {
switch (qobject_type(qobj)) {
case QTYPE_QDICT:
if (qobject_type(obj) == QTYPE_QDICT) {
h = g_hash_table_new(g_str_hash, g_str_equal);
- qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
+ qdict_iter(qobject_to(QDict, obj), qdict_add_key, h);
tos->h = h;
} else {
assert(qobject_type(obj) == QTYPE_QLIST);
- tos->entry = qlist_first(qobject_to_qlist(obj));
+ tos->entry = qlist_first(qobject_to(QList, obj));
tos->index = -1;
}
QObjectInputVisitor *qiv = to_qiv(v);
StackObject *tos = QSLIST_FIRST(&qiv->stack);
- assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
+ assert(tos && qobject_to(QList, tos->obj));
if (!tos->entry) {
return NULL;
QObjectInputVisitor *qiv = to_qiv(v);
StackObject *tos = QSLIST_FIRST(&qiv->stack);
- assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
+ assert(tos && qobject_to(QList, tos->obj));
if (tos->entry) {
error_setg(errp, "Only %u list elements expected in %s",
if (!qobj) {
return;
}
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
if (!qnum || !qnum_get_try_int(qnum, obj)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "integer");
if (!qobj) {
return;
}
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
if (!qnum) {
goto err;
}
if (!qobj) {
return;
}
- qbool = qobject_to_qbool(qobj);
+ qbool = qobject_to(QBool, qobj);
if (!qbool) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "boolean");
if (!qobj) {
return;
}
- qstr = qobject_to_qstring(qobj);
+ qstr = qobject_to(QString, qobj);
if (!qstr) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "string");
if (!qobj) {
return;
}
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
if (!qnum) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "number");
}
return NULL;
}
- args = qobject_to_qdict(obj);
+ args = qobject_to(QDict, obj);
assert(args);
v = qobject_input_visitor_new(QOBJECT(args));
} else {
switch (qobject_type(cur)) {
case QTYPE_QDICT:
assert(name);
- qdict_put_obj(qobject_to_qdict(cur), name, value);
+ qdict_put_obj(qobject_to(QDict, cur), name, value);
break;
case QTYPE_QLIST:
assert(!name);
- qlist_append_obj(qobject_to_qlist(cur), value);
+ qlist_append_obj(qobject_to(QList, cur), value);
break;
default:
g_assert_not_reached();
The ``xlnx-ep108'' machine has been replaced by the ``xlnx-zcu102'' machine.
The ``xlnx-zcu102'' machine has the same features and capabilites in QEMU.
+@section Block device options
+
+@subsection "backing": "" (since 2.12.0)
+
+In order to prevent QEMU from automatically opening an image's backing
+chain, use ``"backing": null'' instead.
+
@node License
@appendix License
@item backing
Reference to or definition of the backing file block device (default is taken
-from the image file). It is allowed to pass an empty string here in order to
-disable the default backing file.
+from the image file). It is allowed to pass @code{null} here in order to disable
+the default backing file.
@item lazy-refcounts
Whether to enable the lazy refcounts feature (on/off; default is taken from the
g_assert(s && parser);
g_debug("process_event: called");
- qdict = qobject_to_qdict(json_parser_parse_err(tokens, NULL, &err));
+ qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
if (err || !qdict) {
QDECREF(qdict);
qdict = qdict_new();
Object *obj;
if (props) {
- pdict = qobject_to_qdict(props);
+ pdict = qobject_to(QDict, props);
if (!pdict) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
return;
return mem_info;
}
+
+static QemuSemaphore x_oob_test_sem;
+
+static void __attribute__((constructor)) x_oob_test_init(void)
+{
+ qemu_sem_init(&x_oob_test_sem, 0);
+}
+
+void qmp_x_oob_test(bool lock, Error **errp)
+{
+ if (lock) {
+ qemu_sem_wait(&x_oob_test_sem);
+ } else {
+ qemu_sem_post(&x_oob_test_sem);
+ }
+}
*/
static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
{
- QObject *key = NULL, *value;
+ QObject *value;
+ QString *key = NULL;
JSONToken *peek, *token;
peek = parser_context_peek_token(ctxt);
goto out;
}
- key = parse_value(ctxt, ap);
- if (!key || qobject_type(key) != QTYPE_QSTRING) {
+ key = qobject_to(QString, parse_value(ctxt, ap));
+ if (!key) {
parse_error(ctxt, peek, "key is not a string in object");
goto out;
}
goto out;
}
- qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value);
+ qdict_put_obj(dict, qstring_get_str(key), value);
- qobject_decref(key);
+ QDECREF(key);
return 0;
out:
- qobject_decref(key);
+ QDECREF(key);
return -1;
}
return qb->value;
}
-/**
- * qobject_to_qbool(): Convert a QObject into a QBool
- */
-QBool *qobject_to_qbool(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QBOOL) {
- return NULL;
- }
- return container_of(obj, QBool, base);
-}
-
/**
* qbool_is_equal(): Test whether the two QBools are equal
*/
bool qbool_is_equal(const QObject *x, const QObject *y)
{
- return qobject_to_qbool(x)->value == qobject_to_qbool(y)->value;
+ return qobject_to(QBool, x)->value == qobject_to(QBool, y)->value;
}
/**
void qbool_destroy_obj(QObject *obj)
{
assert(obj != NULL);
- g_free(qobject_to_qbool(obj));
+ g_free(qobject_to(QBool, obj));
}
return qdict;
}
-/**
- * qobject_to_qdict(): Convert a QObject into a QDict
- */
-QDict *qobject_to_qdict(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QDICT) {
- return NULL;
- }
- return container_of(obj, QDict, base);
-}
-
/**
* tdb_hash(): based on the hash agorithm from gdbm, via tdb
* (from module-init-tools)
*/
double qdict_get_double(const QDict *qdict, const char *key)
{
- return qnum_get_double(qobject_to_qnum(qdict_get(qdict, key)));
+ return qnum_get_double(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
*/
int64_t qdict_get_int(const QDict *qdict, const char *key)
{
- return qnum_get_int(qobject_to_qnum(qdict_get(qdict, key)));
+ return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
*/
bool qdict_get_bool(const QDict *qdict, const char *key)
{
- return qbool_get_bool(qobject_to_qbool(qdict_get(qdict, key)));
+ return qbool_get_bool(qobject_to(QBool, qdict_get(qdict, key)));
}
/**
*/
QList *qdict_get_qlist(const QDict *qdict, const char *key)
{
- return qobject_to_qlist(qdict_get(qdict, key));
+ return qobject_to(QList, qdict_get(qdict, key));
}
/**
*/
QDict *qdict_get_qdict(const QDict *qdict, const char *key)
{
- return qobject_to_qdict(qdict_get(qdict, key));
+ return qobject_to(QDict, qdict_get(qdict, key));
}
/**
*/
const char *qdict_get_str(const QDict *qdict, const char *key)
{
- return qstring_get_str(qobject_to_qstring(qdict_get(qdict, key)));
+ return qstring_get_str(qobject_to(QString, qdict_get(qdict, key)));
}
/**
int64_t qdict_get_try_int(const QDict *qdict, const char *key,
int64_t def_value)
{
- QNum *qnum = qobject_to_qnum(qdict_get(qdict, key));
+ QNum *qnum = qobject_to(QNum, qdict_get(qdict, key));
int64_t val;
if (!qnum || !qnum_get_try_int(qnum, &val)) {
*/
bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value)
{
- QBool *qbool = qobject_to_qbool(qdict_get(qdict, key));
+ QBool *qbool = qobject_to(QBool, qdict_get(qdict, key));
return qbool ? qbool_get_bool(qbool) : def_value;
}
*/
const char *qdict_get_try_str(const QDict *qdict, const char *key)
{
- QString *qstr = qobject_to_qstring(qdict_get(qdict, key));
+ QString *qstr = qobject_to(QString, qdict_get(qdict, key));
return qstr ? qstring_get_str(qstr) : NULL;
}
*/
bool qdict_is_equal(const QObject *x, const QObject *y)
{
- const QDict *dict_x = qobject_to_qdict(x);
- const QDict *dict_y = qobject_to_qdict(y);
+ const QDict *dict_x = qobject_to(QDict, x);
+ const QDict *dict_y = qobject_to(QDict, y);
const QDictEntry *e;
if (qdict_size(dict_x) != qdict_size(dict_y)) {
QDict *qdict;
assert(obj != NULL);
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
for (i = 0; i < QDICT_BUCKET_MAX; i++) {
QDictEntry *entry = QLIST_FIRST(&qdict->table[i]);
new_key = g_strdup_printf("%s.%i", prefix, i);
if (qobject_type(value) == QTYPE_QDICT) {
- qdict_flatten_qdict(qobject_to_qdict(value), target, new_key);
+ qdict_flatten_qdict(qobject_to(QDict, value), target, new_key);
} else if (qobject_type(value) == QTYPE_QLIST) {
- qdict_flatten_qlist(qobject_to_qlist(value), target, new_key);
+ qdict_flatten_qlist(qobject_to(QList, value), target, new_key);
} else {
/* All other types are moved to the target unchanged. */
qobject_incref(value);
if (qobject_type(value) == QTYPE_QDICT) {
/* Entries of QDicts are processed recursively, the QDict object
* itself disappears. */
- qdict_flatten_qdict(qobject_to_qdict(value), target,
+ qdict_flatten_qdict(qobject_to(QDict, value), target,
new_key ? new_key : entry->key);
delete = true;
} else if (qobject_type(value) == QTYPE_QLIST) {
- qdict_flatten_qlist(qobject_to_qlist(value), target,
+ qdict_flatten_qlist(qobject_to(QList, value), target,
new_key ? new_key : entry->key);
delete = true;
} else if (prefix) {
child = qdict_get(two_level, prefix);
if (suffix) {
- if (child) {
- if (qobject_type(child) != QTYPE_QDICT) {
+ QDict *child_dict = qobject_to(QDict, child);
+ if (!child_dict) {
+ if (child) {
error_setg(errp, "Key %s prefix is already set as a scalar",
prefix);
goto error;
}
- } else {
- child = QOBJECT(qdict_new());
- qdict_put_obj(two_level, prefix, child);
+
+ child_dict = qdict_new();
+ qdict_put_obj(two_level, prefix, QOBJECT(child_dict));
}
+
qobject_incref(ent->value);
- qdict_put_obj(qobject_to_qdict(child), suffix, ent->value);
+ qdict_put_obj(child_dict, suffix, ent->value);
} else {
if (child) {
error_setg(errp, "Key %s prefix is already set as a dict",
multi_level = qdict_new();
for (ent = qdict_first(two_level); ent != NULL;
ent = qdict_next(two_level, ent)) {
-
- if (qobject_type(ent->value) == QTYPE_QDICT) {
- child = qdict_crumple(qobject_to_qdict(ent->value), errp);
+ QDict *dict = qobject_to(QDict, ent->value);
+ if (dict) {
+ child = qdict_crumple(dict, errp);
if (!child) {
goto error;
}
}
qobject_incref(child);
- qlist_append_obj(qobject_to_qlist(dst), child);
+ qlist_append_obj(qobject_to(QList, dst), child);
}
QDECREF(multi_level);
multi_level = NULL;
qstring_append(str, "null");
break;
case QTYPE_QNUM: {
- QNum *val = qobject_to_qnum(obj);
+ QNum *val = qobject_to(QNum, obj);
char *buffer = qnum_to_string(val);
qstring_append(str, buffer);
g_free(buffer);
break;
}
case QTYPE_QSTRING: {
- QString *val = qobject_to_qstring(obj);
+ QString *val = qobject_to(QString, obj);
const char *ptr;
int cp;
char buf[16];
}
case QTYPE_QDICT: {
ToJsonIterState s;
- QDict *val = qobject_to_qdict(obj);
+ QDict *val = qobject_to(QDict, obj);
s.count = 0;
s.str = str;
}
case QTYPE_QLIST: {
ToJsonIterState s;
- QList *val = qobject_to_qlist(obj);
+ QList *val = qobject_to(QList, obj);
s.count = 0;
s.str = str;
break;
}
case QTYPE_QBOOL: {
- QBool *val = qobject_to_qbool(obj);
+ QBool *val = qobject_to(QBool, obj);
if (qbool_get_bool(val)) {
qstring_append(str, "true");
return count;
}
-/**
- * qobject_to_qlist(): Convert a QObject into a QList
- */
-QList *qobject_to_qlist(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QLIST) {
- return NULL;
- }
- return container_of(obj, QList, base);
-}
-
/**
* qlist_is_equal(): Test whether the two QLists are equal
*
*/
bool qlist_is_equal(const QObject *x, const QObject *y)
{
- const QList *list_x = qobject_to_qlist(x);
- const QList *list_y = qobject_to_qlist(y);
+ const QList *list_x = qobject_to(QList, x);
+ const QList *list_y = qobject_to(QList, y);
const QListEntry *entry_x, *entry_y;
entry_x = qlist_first(list_x);
QListEntry *entry, *next_entry;
assert(obj != NULL);
- qlist = qobject_to_qlist(obj);
+ qlist = qobject_to(QList, obj);
QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) {
QTAILQ_REMOVE(&qlist->head, entry, next);
#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qnull.h"
static bool qlit_equal_qdict(const QLitObject *lhs, const QDict *qdict)
{
switch (lhs->type) {
case QTYPE_QBOOL:
- return lhs->value.qbool == qbool_get_bool(qobject_to_qbool(rhs));
+ return lhs->value.qbool == qbool_get_bool(qobject_to(QBool, rhs));
case QTYPE_QNUM:
- return lhs->value.qnum == qnum_get_int(qobject_to_qnum(rhs));
+ return lhs->value.qnum == qnum_get_int(qobject_to(QNum, rhs));
case QTYPE_QSTRING:
return (strcmp(lhs->value.qstr,
- qstring_get_str(qobject_to_qstring(rhs))) == 0);
+ qstring_get_str(qobject_to(QString, rhs))) == 0);
case QTYPE_QDICT:
- return qlit_equal_qdict(lhs, qobject_to_qdict(rhs));
+ return qlit_equal_qdict(lhs, qobject_to(QDict, rhs));
case QTYPE_QLIST:
- return qlit_equal_qlist(lhs, qobject_to_qlist(rhs));
+ return qlit_equal_qlist(lhs, qobject_to(QList, rhs));
case QTYPE_QNULL:
return true;
default:
return false;
}
+
+QObject *qobject_from_qlit(const QLitObject *qlit)
+{
+ switch (qlit->type) {
+ case QTYPE_QNULL:
+ return QOBJECT(qnull());
+ case QTYPE_QNUM:
+ return QOBJECT(qnum_from_int(qlit->value.qnum));
+ case QTYPE_QSTRING:
+ return QOBJECT(qstring_from_str(qlit->value.qstr));
+ case QTYPE_QDICT: {
+ QDict *qdict = qdict_new();
+ QLitDictEntry *e;
+
+ for (e = qlit->value.qdict; e->key; e++) {
+ qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
+ }
+ return QOBJECT(qdict);
+ }
+ case QTYPE_QLIST: {
+ QList *qlist = qlist_new();
+ QLitObject *e;
+
+ for (e = qlit->value.qlist; e->type != QTYPE_NONE; e++) {
+ qlist_append_obj(qlist, qobject_from_qlit(e));
+ }
+ return QOBJECT(qlist);
+ }
+ case QTYPE_QBOOL:
+ return QOBJECT(qbool_from_bool(qlit->value.qbool));
+ default:
+ assert(0);
+ }
+
+ return NULL;
+}
return NULL;
}
-/**
- * qobject_to_qnum(): Convert a QObject into a QNum
- */
-QNum *qobject_to_qnum(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QNUM) {
- return NULL;
- }
- return container_of(obj, QNum, base);
-}
-
/**
* qnum_is_equal(): Test whether the two QNums are equal
*
*/
bool qnum_is_equal(const QObject *x, const QObject *y)
{
- QNum *num_x = qobject_to_qnum(x);
- QNum *num_y = qobject_to_qnum(y);
+ QNum *num_x = qobject_to(QNum, x);
+ QNum *num_y = qobject_to(QNum, y);
switch (num_x->kind) {
case QNUM_I64:
void qnum_destroy_obj(QObject *obj)
{
assert(obj != NULL);
- g_free(qobject_to_qnum(obj));
+ g_free(qobject_to(QNum, obj));
}
qstring->string[qstring->length] = 0;
}
-/**
- * qobject_to_qstring(): Convert a QObject to a QString
- */
-QString *qobject_to_qstring(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QSTRING) {
- return NULL;
- }
- return container_of(obj, QString, base);
-}
-
/**
* qstring_get_str(): Return a pointer to the stored string
*
return qstring->string;
}
+/**
+ * qstring_get_try_str(): Return a pointer to the stored string
+ *
+ * NOTE: will return NULL if qstring is not provided.
+ */
+const char *qstring_get_try_str(const QString *qstring)
+{
+ return qstring ? qstring_get_str(qstring) : NULL;
+}
+
+/**
+ * qobject_get_try_str(): Return a pointer to the corresponding string
+ *
+ * NOTE: the string will only be returned if the object is valid, and
+ * its type is QString, otherwise NULL is returned.
+ */
+const char *qobject_get_try_str(const QObject *qstring)
+{
+ return qstring_get_try_str(qobject_to(QString, qstring));
+}
+
/**
* qstring_is_equal(): Test whether the two QStrings are equal
*/
bool qstring_is_equal(const QObject *x, const QObject *y)
{
- return !strcmp(qobject_to_qstring(x)->string,
- qobject_to_qstring(y)->string);
+ return !strcmp(qobject_to(QString, x)->string,
+ qobject_to(QString, y)->string);
}
/**
QString *qs;
assert(obj != NULL);
- qs = qobject_to_qstring(obj);
+ qs = qobject_to(QString, obj);
g_free(qs->string);
g_free(qs);
}
Error **errp)
{
QObject *ret = object_property_get_qobject(obj, name, errp);
- QString *qstring;
char *retval;
if (!ret) {
return NULL;
}
- qstring = qobject_to_qstring(ret);
- if (!qstring) {
+
+ retval = g_strdup(qobject_get_try_str(ret));
+ if (!retval) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string");
- retval = NULL;
- } else {
- retval = g_strdup(qstring_get_str(qstring));
}
qobject_decref(ret);
if (!ret) {
return false;
}
- qbool = qobject_to_qbool(ret);
+ qbool = qobject_to(QBool, ret);
if (!qbool) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean");
retval = false;
return -1;
}
- qnum = qobject_to_qnum(ret);
+ qnum = qobject_to(QNum, ret);
if (!qnum || !qnum_get_try_int(qnum, &retval)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "int");
retval = -1;
if (!ret) {
return 0;
}
- qnum = qobject_to_qnum(ret);
+ qnum = qobject_to(QNum, ret);
if (!qnum || !qnum_get_try_uint(qnum, &retval)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "uint");
retval = 0;
return ret
-def gen_register_command(name, success_response):
- options = 'QCO_NO_OPTIONS'
+def gen_register_command(name, success_response, allow_oob):
+ options = []
+
if not success_response:
- options = 'QCO_NO_SUCCESS_RESP'
+ options += ['QCO_NO_SUCCESS_RESP']
+ if allow_oob:
+ options += ['QCO_ALLOW_OOB']
+
+ if not options:
+ options = ['QCO_NO_OPTIONS']
+
+ options = " | ".join(options)
ret = mcgen('''
qmp_register_command(cmds, "%(name)s",
genc.add(gen_registry(self._regy, self._prefix))
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
if not gen:
return
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
self._genc.add(gen_marshal_output(ret_type))
self._genh.add(gen_marshal_decl(name))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
- self._regy += gen_register_command(name, success_response)
+ self._regy += gen_register_command(name, success_response, allow_oob)
def gen_commands(schema, output_dir, prefix):
elif 'command' in expr:
meta = 'command'
check_keys(expr_elem, 'command', [],
- ['data', 'returns', 'gen', 'success-response', 'boxed'])
+ ['data', 'returns', 'gen', 'success-response',
+ 'boxed', 'allow-oob'])
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
pass
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
pass
def visit_event(self, name, info, arg_type, boxed):
class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
QAPISchemaEntity.__init__(self, name, info, doc)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
self.gen = gen
self.success_response = success_response
self.boxed = boxed
+ self.allow_oob = allow_oob
def check(self, schema):
if self._arg_type_name:
def visit(self, visitor):
visitor.visit_command(self.name, self.info,
self.arg_type, self.ret_type,
- self.gen, self.success_response, self.boxed)
+ self.gen, self.success_response,
+ self.boxed, self.allow_oob)
class QAPISchemaEvent(QAPISchemaEntity):
gen = expr.get('gen', True)
success_response = expr.get('success-response', True)
boxed = expr.get('boxed', False)
+ allow_oob = expr.get('allow-oob', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info))
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
- gen, success_response, boxed))
+ gen, success_response,
+ boxed, allow_oob))
def _def_event(self, expr, info, doc):
name = expr['event']
def texi_member(member, suffix=''):
"""Format a table of members item for an object type member"""
typ = member.type.doc_type()
- return '@item @code{%s%s%s}%s%s\n' % (
- member.name,
- ': ' if typ else '',
- typ if typ else '',
+ membertype = ': ' + typ if typ else ''
+ return '@item @code{%s%s}%s%s\n' % (
+ member.name, membertype,
' (optional)' if member.optional else '',
suffix)
body=texi_entity(doc, 'Members')))
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
doc = self.cur_doc
if boxed:
body = texi_body(doc)
from qapi.common import *
-# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
-# TODO try to use json.dumps() once we get unstuck
-def to_json(obj, level=0):
+def to_qlit(obj, level=0, suppress_first_indent=False):
+
+ def indent(level):
+ return level * 4 * ' '
+
+ ret = ''
+ if not suppress_first_indent:
+ ret += indent(level)
if obj is None:
- ret = 'null'
+ ret += 'QLIT_QNULL'
elif isinstance(obj, str):
- ret = '"' + obj.replace('"', r'\"') + '"'
+ ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'
elif isinstance(obj, list):
- elts = [to_json(elt, level + 1)
+ elts = [to_qlit(elt, level + 1)
for elt in obj]
- ret = '[' + ', '.join(elts) + ']'
+ elts.append(indent(level + 1) + "{}")
+ ret += 'QLIT_QLIST(((QLitObject[]) {\n'
+ ret += ',\n'.join(elts) + '\n'
+ ret += indent(level) + '}))'
elif isinstance(obj, dict):
- elts = ['"%s": %s' % (key.replace('"', r'\"'),
- to_json(obj[key], level + 1))
- for key in sorted(obj.keys())]
- ret = '{' + ', '.join(elts) + '}'
+ elts = []
+ for key, value in sorted(obj.items()):
+ elts.append(indent(level + 1) + '{ %s, %s }' %
+ (to_c_string(key), to_qlit(value, level + 1, True)))
+ elts.append(indent(level + 1) + '{}')
+ ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
+ ret += ',\n'.join(elts) + '\n'
+ ret += indent(level) + '}))'
+ elif isinstance(obj, bool):
+ ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false')
else:
assert False # not implemented
- if level == 1:
- ret = '\n' + ret
return ret
' * QAPI/QMP schema introspection', __doc__)
self._unmask = unmask
self._schema = None
- self._jsons = []
+ self._qlits = []
self._used_types = []
self._name_map = {}
self._genc.add(mcgen('''
def visit_end(self):
# visit the types that are actually used
- jsons = self._jsons
- self._jsons = []
+ qlits = self._qlits
+ self._qlits = []
for typ in self._used_types:
typ.visit(self)
# generate C
# TODO can generate awfully long lines
- jsons.extend(self._jsons)
- name = c_name(self._prefix, protect=False) + 'qmp_schema_json'
+ qlits.extend(self._qlits)
+ name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
self._genh.add(mcgen('''
-extern const char %(c_name)s[];
+#include "qapi/qmp/qlit.h"
+
+extern const QLitObject %(c_name)s;
''',
c_name=c_name(name)))
- lines = to_json(jsons).split('\n')
- c_string = '\n '.join([to_c_string(line) for line in lines])
self._genc.add(mcgen('''
-const char %(c_name)s[] = %(c_string)s;
+const QLitObject %(c_name)s = %(c_string)s;
''',
c_name=c_name(name),
- c_string=c_string))
+ c_string=to_qlit(qlits)))
self._schema = None
- self._jsons = []
+ self._qlits = []
self._used_types = []
self._name_map = {}
return '[' + self._use_type(typ.element_type) + ']'
return self._name(typ.name)
- def _gen_json(self, name, mtype, obj):
+ def _gen_qlit(self, name, mtype, obj):
if mtype not in ('command', 'event', 'builtin', 'array'):
name = self._name(name)
obj['name'] = name
obj['meta-type'] = mtype
- self._jsons.append(obj)
+ self._qlits.append(obj)
def _gen_member(self, member):
ret = {'name': member.name, 'type': self._use_type(member.type)}
return {'case': variant.name, 'type': self._use_type(variant.type)}
def visit_builtin_type(self, name, info, json_type):
- self._gen_json(name, 'builtin', {'json-type': json_type})
+ self._gen_qlit(name, 'builtin', {'json-type': json_type})
def visit_enum_type(self, name, info, values, prefix):
- self._gen_json(name, 'enum', {'values': values})
+ self._gen_qlit(name, 'enum', {'values': values})
def visit_array_type(self, name, info, element_type):
element = self._use_type(element_type)
- self._gen_json('[' + element + ']', 'array', {'element-type': element})
+ self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
def visit_object_type_flat(self, name, info, members, variants):
obj = {'members': [self._gen_member(m) for m in members]}
if variants:
obj.update(self._gen_variants(variants.tag_member.name,
variants.variants))
- self._gen_json(name, 'object', obj)
+ self._gen_qlit(name, 'object', obj)
def visit_alternate_type(self, name, info, variants):
- self._gen_json(name, 'alternate',
+ self._gen_qlit(name, 'alternate',
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]})
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
- self._gen_json(name, 'command',
+ self._gen_qlit(name, 'command',
{'arg-type': self._use_type(arg_type),
- 'ret-type': self._use_type(ret_type)})
+ 'ret-type': self._use_type(ret_type),
+ 'allow-oob': allow_oob})
def visit_event(self, name, info, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
- self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
+ self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
def gen_introspect(schema, output_dir, prefix, opt_unmask):
xc = x86_cpu_from_model(model->name,
model->has_props ?
- qobject_to_qdict(model->props) :
+ qobject_to(QDict, model->props) :
NULL, &err);
if (err) {
goto out;
char *riscv_isa_string(RISCVCPU *cpu)
{
int i;
- size_t maxlen = 5 + ctz32(cpu->env.misa);
- char *isa_string = g_new0(char, maxlen);
- snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS);
+ const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1;
+ char *isa_str = g_new(char, maxlen);
+ char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS);
for (i = 0; i < sizeof(riscv_exts); i++) {
if (cpu->env.misa & RV(riscv_exts[i])) {
- isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a';
-
+ *p++ = qemu_tolower(riscv_exts[i]);
}
}
- return isa_string;
+ *p = '\0';
+ return isa_str;
}
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
Object *obj;
if (info->props) {
- qdict = qobject_to_qdict(info->props);
+ qdict = qobject_to(QDict, info->props);
if (!qdict) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
return;
$^ >$*.test.out 2>$*.test.err; \
echo $$? >$*.test.exit, \
"TEST","$*.out")
- @diff $(SRC_PATH)/$*.out $*.test.out
@# Sanitize error messages (make them independent of build directory)
- @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff $(SRC_PATH)/$*.err -
- @diff $(SRC_PATH)/$*.exit $*.test.exit
+ @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff -u $(SRC_PATH)/$*.err -
+ @diff -u $(SRC_PATH)/$*.out $*.test.out
+ @diff -u $(SRC_PATH)/$*.exit $*.test.exit
.PHONY: check-tests/qapi-schema/doc-good.texi
check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
- @diff -q $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
+ @diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
.PHONY: check-decodetree
check-decodetree:
g_assert(qdict_size(qdict) == 1);
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
- qn = qobject_to_qnum(ent->value);
+ qn = qobject_to(QNum, ent->value);
g_assert_cmpint(qnum_get_int(qn), ==, num);
QDECREF(qdict);
obj = qdict_get(tests_dict, key);
g_assert(obj != NULL);
- qn = qobject_to_qnum(obj);
+ qn = qobject_to(QNum, obj);
g_assert_cmpint(qnum_get_int(qn), ==, value);
QDECREF(tests_dict);
static void qobject_to_qdict_test(void)
{
QDict *tests_dict = qdict_new();
- g_assert(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
+ g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict);
QDECREF(tests_dict);
}
qdict_array_split(test_dict, &test_list);
- dict1 = qobject_to_qdict(qlist_pop(test_list));
- dict2 = qobject_to_qdict(qlist_pop(test_list));
- int1 = qobject_to_qnum(qlist_pop(test_list));
+ dict1 = qobject_to(QDict, qlist_pop(test_list));
+ dict2 = qobject_to(QDict, qlist_pop(test_list));
+ int1 = qobject_to(QNum, qlist_pop(test_list));
g_assert(dict1);
g_assert(dict2);
qdict_array_split(test_dict, &test_list);
- int1 = qobject_to_qnum(qlist_pop(test_list));
+ int1 = qobject_to(QNum, qlist_pop(test_list));
g_assert(int1);
g_assert(qlist_empty(test_list));
qdict_put_str(src, "vnc.acl..name", "acl0");
qdict_put_str(src, "vnc.acl.rule..name", "acl0");
- dst = qobject_to_qdict(qdict_crumple(src, &error_abort));
+ dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
g_assert(dst);
g_assert_cmpint(qdict_size(dst), ==, 1);
g_assert(rules);
g_assert_cmpint(qlist_size(rules), ==, 2);
- rule = qobject_to_qdict(qlist_pop(rules));
+ rule = qobject_to(QDict, qlist_pop(rules));
g_assert(rule);
g_assert_cmpint(qdict_size(rule), ==, 2);
g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
QDECREF(rule);
- rule = qobject_to_qdict(qlist_pop(rules));
+ rule = qobject_to(QDict, qlist_pop(rules));
g_assert(rule);
g_assert_cmpint(qdict_size(rule), ==, 2);
g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match"));
QString *str;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);
QString *str;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
QString *str;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
obj = qobject_from_json(json_in, utf8_out ? &error_abort : NULL);
if (utf8_out) {
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert_cmpstr(qstring_get_str(str), ==, utf8_out);
} else {
*/
if (0 && json_out != json_in) {
obj = qobject_from_json(json_out, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert_cmpstr(qstring_get_str(str), ==, utf8_out);
}
for (i = 0; test_cases[i].decoded; i++) {
QString *str;
- str = qobject_to_qstring(qobject_from_jsonf("%s",
- test_cases[i].decoded));
+ str = qobject_to(QString,
+ qobject_from_jsonf("%s", test_cases[i].decoded));
g_assert(str);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
QNum *qnum;
int64_t val;
- qnum = qobject_to_qnum(qobject_from_json(test_cases[i].encoded,
- &error_abort));
+ qnum = qobject_to(QNum,
+ qobject_from_json(test_cases[i].encoded,
+ &error_abort));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, test_cases[i].decoded);
uint64_t val;
int64_t ival;
- qnum = qobject_to_qnum(qobject_from_json(maxu64, &error_abort));
+ qnum = qobject_to(QNum, qobject_from_json(maxu64, &error_abort));
g_assert(qnum);
g_assert_cmpuint(qnum_get_uint(qnum), ==, 18446744073709551615U);
g_assert(!qnum_get_try_int(qnum, &ival));
QDECREF(str);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_json(gtu64, &error_abort));
+ qnum = qobject_to(QNum, qobject_from_json(gtu64, &error_abort));
g_assert(qnum);
g_assert_cmpfloat(qnum_get_double(qnum), ==, 18446744073709552e3);
g_assert(!qnum_get_try_uint(qnum, &val));
QDECREF(str);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_json(lti64, &error_abort));
+ qnum = qobject_to(QNum, qobject_from_json(lti64, &error_abort));
g_assert(qnum);
g_assert_cmpfloat(qnum_get_double(qnum), ==, -92233720368547758e2);
g_assert(!qnum_get_try_uint(qnum, &val));
QNum *qnum;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- qnum = qobject_to_qnum(obj);
+ qnum = qobject_to(QNum, obj);
g_assert(qnum);
g_assert(qnum_get_double(qnum) == test_cases[i].decoded);
double valuef = 2.323423423;
int64_t val;
- qnum = qobject_to_qnum(qobject_from_jsonf("%d", value));
+ qnum = qobject_to(QNum, qobject_from_jsonf("%d", value));
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, value);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_jsonf("%lld", value_ll));
+ qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll));
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, value_ll);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_jsonf("%f", valuef));
+ qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef));
g_assert(qnum_get_double(qnum) == valuef);
QDECREF(qnum);
}
QString *str;
obj = qobject_from_json("true", &error_abort);
- qbool = qobject_to_qbool(obj);
+ qbool = qobject_to(QBool, obj);
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
QDECREF(qbool);
obj = qobject_from_json("false", &error_abort);
- qbool = qobject_to_qbool(obj);
+ qbool = qobject_to(QBool, obj);
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == false);
QDECREF(qbool);
- qbool = qobject_to_qbool(qobject_from_jsonf("%i", false));
+ qbool = qobject_to(QBool, qobject_from_jsonf("%i", false));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == false);
QDECREF(qbool);
/* Test that non-zero values other than 1 get collapsed to true */
- qbool = qobject_to_qbool(qobject_from_jsonf("%i", 2));
+ qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
QDECREF(qbool);
qlist = qlist_new();
- g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist);
+ g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist);
QDECREF(qlist);
}
g_assert(opaque == NULL);
- qi = qobject_to_qnum(obj);
+ qi = qobject_to(QNum, obj);
g_assert(qi != NULL);
g_assert(qnum_get_try_int(qi, &val));
#include "qemu/osdep.h"
+#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qlit.h"
+#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qstring.h"
static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) {
g_assert(!qlit_equal_qobject(&qlit_foo, qobj));
- qdict_put(qobject_to_qdict(qobj), "bee", qlist_new());
+ qdict_put(qobject_to(QDict, qobj), "bee", qlist_new());
g_assert(!qlit_equal_qobject(&qlit, qobj));
qobject_decref(qobj);
}
+static void qobject_from_qlit_test(void)
+{
+ QObject *obj, *qobj = qobject_from_qlit(&qlit);
+ QDict *qdict;
+ QList *bee;
+
+ qdict = qobject_to(QDict, qobj);
+ g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
+ g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
+ g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);
+
+ bee = qdict_get_qlist(qdict, "bee");
+ obj = qlist_pop(bee);
+ g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43);
+ qobject_decref(obj);
+ obj = qlist_pop(bee);
+ g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44);
+ qobject_decref(obj);
+ obj = qlist_pop(bee);
+ g_assert(qbool_get_bool(qobject_to(QBool, obj)));
+ qobject_decref(obj);
+
+ qobject_decref(qobj);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/qlit/equal_qobject", qlit_equal_qobject_test);
+ g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
return g_test_run();
}
QNum *qn;
qn = qnum_from_int(0);
- g_assert(qobject_to_qnum(QOBJECT(qn)) == qn);
+ g_assert(qobject_to(QNum, QOBJECT(qn)) == qn);
QDECREF(qn);
qn = qnum_from_double(0);
- g_assert(qobject_to_qnum(QOBJECT(qn)) == qn);
+ g_assert(qobject_to(QNum, QOBJECT(qn)) == qn);
QDECREF(qn);
}
dict_different_null_key, dict_longer, dict_shorter,
dict_nested);
- dict_crumpled = qobject_to_qdict(qdict_crumple(dict_1, &local_err));
+ dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &local_err));
g_assert(!local_err);
check_equal(dict_crumpled, dict_nested);
QString *qstring;
qstring = qstring_from_str("foo");
- g_assert(qobject_to_qstring(QOBJECT(qstring)) == qstring);
+ g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring);
QDECREF(qstring);
}
QListEntry *e;
QLIST_FOREACH_ENTRY(types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
QINCREF(d);
qdict_put(index, name, d);
QListEntry *e;
QLIST_FOREACH_ENTRY(types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *ename = qdict_get_str(d, "name");
if (!strcmp(ename, name)) {
return d;
index = qom_type_index(types);
QLIST_FOREACH_ENTRY(types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
g_assert(qom_has_parent(index, name, parent));
non_abstract = qom_list_types(NULL, false);
QLIST_FOREACH_ENTRY(all_types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
bool abstract = qdict_haskey(d, "abstract") ?
qdict_get_bool(d, "abstract") :
types = device_type_list(false);
QLIST_FOREACH_ENTRY(types, entry) {
- type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
- "name");
+ type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)),
+ "name");
g_assert(type);
test_one_device(type);
}
index = qom_type_index(all_types);
QLIST_FOREACH_ENTRY(all_types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
/*
}
g_assert(!qmp->response);
- qmp->response = qobject_to_qdict(obj);
+ qmp->response = qobject_to(QDict, obj);
g_assert(qmp->response);
}
g_assert(list);
for (p = qlist_first(list); p; p = qlist_next(p)) {
- minfo = qobject_to_qdict(qlist_entry_obj(p));
+ minfo = qobject_to(QDict, qlist_entry_obj(p));
g_assert(minfo);
qobj = qdict_get(minfo, "name");
g_assert(qobj);
- qstr = qobject_to_qstring(qobj);
+ qstr = qobject_to(QString, qobj);
g_assert(qstr);
mname = qstring_get_str(qstr);
cb(mname);
QDict *cpu, *props;
int64_t cpu_idx, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "CPU"));
g_assert(qdict_haskey(cpu, "props"));
QDict *cpu, *props;
int64_t socket, core, thread, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
QDict *cpu, *props;
int64_t core, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
QDict *cpu, *props;
int64_t thread, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
self._print_variants(variants)
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
print(' gen=%s success_response=%s boxed=%s' % \
$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+echo
+echo "=== Testing correct handling of 'backing':null ==="
+echo
+
+_make_test_img -b "$TEST_IMG.base" $IMG_SIZE
+
+# This should read 42
+$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+# This should read 0
+$QEMU_IO -c 'read -P 0 0 512' "json:{\
+ 'driver': '$IMGFMT',
+ 'file': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG'
+ },
+ 'backing': null
+}" | _filter_qemu_io
+
+
# Taken from test 071
echo
echo "=== Testing blkdebug ==="
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+=== Testing correct handling of 'backing':null ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
=== Testing blkdebug ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
#include "qapi/qobject-input-visitor.h"
#include "qapi/util.h"
#include "qapi/visitor.h"
+#include "qapi/qmp/qstring.h"
const char common_args[] = "-nodefaults -machine none";
QDict *resp, *q, *ret;
QList *capabilities;
QTestState *qts;
+ const QListEntry *entry;
+ QString *qstr;
+ int i;
qts = qtest_init_without_qmp_handshake(common_args);
g_assert(q);
test_version(qdict_get(q, "version"));
capabilities = qdict_get_qlist(q, "capabilities");
- g_assert(capabilities && qlist_empty(capabilities));
+ g_assert(capabilities);
+ entry = qlist_first(capabilities);
+ g_assert(entry);
+ qstr = qobject_to(QString, entry->value);
+ g_assert(qstr);
+ g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
QDECREF(resp);
/* Test valid command before handshake */
g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2);
QDECREF(resp);
+ /*
+ * Test command batching. In current test OOB is not enabled, we
+ * should be able to run as many commands in batch as we like.
+ * Using 16 (>8, which is OOB queue length) to make sure OOB won't
+ * break existing clients. Note: this test does not control the
+ * scheduling of QEMU's QMP command processing threads so it may
+ * not really trigger batching inside QEMU. This is just a
+ * best-effort test.
+ */
+ for (i = 0; i < 16; i++) {
+ qtest_async_qmp(qts, "{ 'execute': 'query-version' }");
+ }
+ /* Verify the replies to make sure no command is dropped. */
+ for (i = 0; i < 16; i++) {
+ resp = qtest_qmp_receive(qts);
+ /* It should never be dropped. Each of them should be a reply. */
+ g_assert(qdict_haskey(resp, "return"));
+ g_assert(!qdict_haskey(resp, "event"));
+ QDECREF(resp);
+ }
+
qtest_quit(qts);
}
+/* Tests for Out-Of-Band support. */
+static void test_qmp_oob(void)
+{
+ QDict *resp;
+ int acks = 0;
+ const char *cmd_id;
+
+ global_qtest = qtest_init_without_qmp_handshake(common_args);
+
+ /* Ignore the greeting message. */
+ resp = qmp_receive();
+ g_assert(qdict_get_qdict(resp, "QMP"));
+ QDECREF(resp);
+
+ /* Try a fake capability, it should fail. */
+ resp = qmp("{ 'execute': 'qmp_capabilities', "
+ " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
+ g_assert(qdict_haskey(resp, "error"));
+ QDECREF(resp);
+
+ /* Now, enable OOB in current QMP session, it should succeed. */
+ resp = qmp("{ 'execute': 'qmp_capabilities', "
+ " 'arguments': { 'enable': [ 'oob' ] } }");
+ g_assert(qdict_haskey(resp, "return"));
+ QDECREF(resp);
+
+ /*
+ * Try any command that does not support OOB but with OOB flag. We
+ * should get failure.
+ */
+ resp = qmp("{ 'execute': 'query-cpus',"
+ " 'control': { 'run-oob': true } }");
+ g_assert(qdict_haskey(resp, "error"));
+ QDECREF(resp);
+
+ /*
+ * First send the "x-oob-test" command with lock=true and
+ * oob=false, it should hang the dispatcher and main thread;
+ * later, we send another lock=false with oob=true to continue
+ * that thread processing. Finally we should receive replies from
+ * both commands.
+ */
+ qmp_async("{ 'execute': 'x-oob-test',"
+ " 'arguments': { 'lock': true }, "
+ " 'id': 'lock-cmd'}");
+ qmp_async("{ 'execute': 'x-oob-test', "
+ " 'arguments': { 'lock': false }, "
+ " 'control': { 'run-oob': true }, "
+ " 'id': 'unlock-cmd' }");
+
+ /* Ignore all events. Wait for 2 acks */
+ while (acks < 2) {
+ resp = qmp_receive();
+ cmd_id = qdict_get_str(resp, "id");
+ if (!g_strcmp0(cmd_id, "lock-cmd") ||
+ !g_strcmp0(cmd_id, "unlock-cmd")) {
+ acks++;
+ }
+ QDECREF(resp);
+ }
+
+ qtest_end();
+}
+
static int query_error_class(const char *cmd)
{
static struct {
g_test_init(&argc, &argv, NULL);
qtest_add_func("qmp/protocol", test_qmp_protocol);
+ qtest_add_func("qmp/oob", test_qmp_oob);
qmp_schema_init(&schema);
add_query_tests(&schema);
}
g_assert(qdict_haskey(response, "return"));
- list = qobject_to_qlist(qdict_get(response, "return"));
+ list = qobject_to(QList, qdict_get(response, "return"));
QLIST_FOREACH_ENTRY(list, entry) {
- tuple = qobject_to_qdict(qlist_entry_obj(entry));
+ tuple = qobject_to(QDict, qlist_entry_obj(entry));
bool is_child = strstart(qdict_get_str(tuple, "type"), "child<", NULL);
bool is_link = strstart(qdict_get_str(tuple, "type"), "link<", NULL);
g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort));
addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort);
- qdict = qobject_to_qdict(addr);
+ qdict = qobject_to(QDict, addr);
port = qdict_get_str(qdict, "port");
tmp = g_strdup_printf("tcp:127.0.0.1:%s", port);
QDECREF(qdict);
g_assert(qlist);
for (i = 0; i < ARRAY_SIZE(expected); i++) {
- qstr = qobject_to_qstring(qlist_pop(qlist));
+ qstr = qobject_to(QString, qlist_pop(qlist));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]);
QDECREF(qstr);
QDECREF(qdict);
visit_start_struct(v, NULL, NULL, 0, &error_abort);
visit_type_any(v, "a", &any, &error_abort);
- qlist = qobject_to_qlist(any);
+ qlist = qobject_to(QList, any);
g_assert(qlist);
- qstr = qobject_to_qstring(qlist_pop(qlist));
+ qstr = qobject_to(QString, qlist_pop(qlist));
g_assert_cmpstr(qstring_get_str(qstr), ==, "null");
QDECREF(qstr);
- qstr = qobject_to_qstring(qlist_pop(qlist));
+ qstr = qobject_to(QString, qlist_pop(qlist));
g_assert_cmpstr(qstring_get_str(qstr), ==, "1");
g_assert(qlist_empty(qlist));
QDECREF(qstr);
/* check there is at least a cpu */
list = qdict_get_qlist(ret, "return");
entry = qlist_first(list);
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id"));
QDECREF(ret);
}
list = qdict_get_qlist(ret, "return");
entry = qlist_first(list);
if (entry) {
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk"));
}
QDECREF(ret);
entry = qlist_first(list);
/* newer versions of qga may return empty list without error */
if (entry) {
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value),
+ "phys-index"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
}
}
/* check there is at least an interface */
list = qdict_get_qlist(ret, "return");
entry = qlist_first(list);
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
QDECREF(ret);
}
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp != NULL);
- assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+ assert(!qdict_haskey(qobject_to(QDict, resp), "error"));
qobject_decref(resp);
QDECREF(req);
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp != NULL);
- assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+ assert(qdict_haskey(qobject_to(QDict, resp), "error"));
qobject_decref(resp);
QDECREF(req);
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp != NULL);
- assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+ assert(qdict_haskey(qobject_to(QDict, resp), "error"));
qobject_decref(resp);
QDECREF(req);
resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp_obj);
- resp = qobject_to_qdict(resp_obj);
+ resp = qobject_to(QDict, resp_obj);
assert(resp && !qdict_haskey(resp, "error"));
ret = qdict_get(resp, "return");
assert(ret);
qdict_put(req, "arguments", args);
qdict_put_str(req, "execute", "user_def_cmd2");
- ret = qobject_to_qdict(test_qmp_dispatch(req));
+ ret = qobject_to(QDict, test_qmp_dispatch(req));
assert(!strcmp(qdict_get_str(ret, "string0"), "blah1"));
ret_dict = qdict_get_qdict(ret, "dict1");
qdict_put(req, "arguments", args3);
qdict_put_str(req, "execute", "guest-get-time");
- ret3 = qobject_to_qnum(test_qmp_dispatch(req));
+ ret3 = qobject_to(QNum, test_qmp_dispatch(req));
g_assert(qnum_get_try_int(ret3, &val));
g_assert_cmpint(val, ==, 66);
QDECREF(ret3);
switch (qobject_type(obj1)) {
case QTYPE_QBOOL:
- d->result = (qbool_get_bool(qobject_to_qbool(obj1)) ==
- qbool_get_bool(qobject_to_qbool(obj2)));
+ d->result = (qbool_get_bool(qobject_to(QBool, obj1)) ==
+ qbool_get_bool(qobject_to(QBool, obj2)));
return;
case QTYPE_QNUM:
- g_assert(qnum_get_try_int(qobject_to_qnum(obj1), &val1));
- g_assert(qnum_get_try_int(qobject_to_qnum(obj2), &val2));
+ g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1));
+ g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2));
d->result = val1 == val2;
return;
case QTYPE_QSTRING:
- d->result = g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)),
- qstring_get_str(qobject_to_qstring(obj2))) == 0;
+ d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)),
+ qstring_get_str(qobject_to(QString, obj2))) == 0;
return;
case QTYPE_QDICT:
- d_new.expect = qobject_to_qdict(obj2);
+ d_new.expect = qobject_to(QDict, obj2);
d_new.result = true;
- qdict_iter(qobject_to_qdict(obj1), qdict_cmp_do_simple, &d_new);
+ qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new);
d->result = d_new.result;
return;
default:
v = visitor_input_test_init(data, "-42");
visit_type_any(v, NULL, &res, &error_abort);
- qnum = qobject_to_qnum(res);
+ qnum = qobject_to(QNum, res);
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
visit_type_any(v, NULL, &res, &error_abort);
- qdict = qobject_to_qdict(res);
+ qdict = qobject_to(QDict, res);
g_assert(qdict && qdict_size(qdict) == 3);
qobj = qdict_get(qdict, "integer");
g_assert(qobj);
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
qobj = qdict_get(qdict, "boolean");
g_assert(qobj);
- qbool = qobject_to_qbool(qobj);
+ qbool = qobject_to(QBool, qobj);
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
qobj = qdict_get(qdict, "string");
g_assert(qobj);
- qstring = qobject_to_qstring(qobj);
+ qstring = qobject_to(QString, qobj);
g_assert(qstring);
g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
qobject_decref(res);
}
static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
- const char *schema_json)
+ const QLitObject *qlit)
{
SchemaInfoList *schema = NULL;
+ QObject *obj = qobject_from_qlit(qlit);
Visitor *v;
- v = visitor_input_test_init_raw(data, schema_json);
+ v = qobject_input_visitor_new(obj);
visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
g_assert(schema);
qapi_free_SchemaInfoList(schema);
+ qobject_decref(obj);
+ visit_free(v);
}
static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
const void *unused)
{
- do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
- do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
+ do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
+ do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
}
int main(int argc, char **argv)
visit_type_int(data->ov, NULL, &value, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, value);
visit_type_bool(data->ov, NULL, &value, &error_abort);
- qbool = qobject_to_qbool(visitor_get(data));
+ qbool = qobject_to(QBool, visitor_get(data));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == value);
}
visit_type_number(data->ov, NULL, &value, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_double(qnum) == value);
}
visit_type_str(data->ov, NULL, &string, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, string);
}
/* A null string should return "" */
visit_type_str(data->ov, NULL, &string, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, "");
}
for (i = 0; i < ENUM_ONE__MAX; i++) {
visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, EnumOne_str(i));
visitor_reset(data);
visit_type_TestStruct(data->ov, NULL, &p, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 2);
g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]);
visit_type_TestStructList(data->ov, NULL, &head, &error_abort);
- qlist = qobject_to_qlist(visitor_get(data));
+ qlist = qobject_to(QList, visitor_get(data));
g_assert(qlist);
g_assert(!qlist_empty(qlist));
QLIST_FOREACH_ENTRY(qlist, entry) {
QDict *qdict;
- qdict = qobject_to_qdict(entry->value);
+ qdict = qobject_to(QDict, entry->value);
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i);
qobj = QOBJECT(qnum_from_int(-42));
visit_type_any(data->ov, NULL, &qobj, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
qobj = QOBJECT(qdict);
visit_type_any(data->ov, NULL, &qobj, &error_abort);
qobject_decref(qobj);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
- qnum = qobject_to_qnum(qdict_get(qdict, "integer"));
+ qnum = qobject_to(QNum, qdict_get(qdict, "integer"));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
- qbool = qobject_to_qbool(qdict_get(qdict, "boolean"));
+ qbool = qobject_to(QBool, qdict_get(qdict, "boolean"));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
- qstring = qobject_to_qstring(qdict_get(qdict, "string"));
+ qstring = qobject_to(QString, qdict_get(qdict, "string"));
g_assert(qstring);
g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
}
tmp->u.value1.boolean = true;
visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1");
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str");
tmp->u.i = 42;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, 42);
tmp->u.e = ENUM_ONE_VALUE1;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, "value1");
tmp->u.udfu.u.value1.boolean = true;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 4);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1);
visit_type_null(data->ov, "a", &null, &error_abort);
visit_check_struct(data->ov, &error_abort);
visit_end_struct(data->ov, NULL);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 1);
nil = qdict_get(qdict, "a");
QList *qlist;
int i;
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
g_assert(qdict);
g_assert(qdict_haskey(qdict, "data"));
- qlist = qlist_copy(qobject_to_qlist(qdict_get(qdict, "data")));
+ qlist = qlist_copy(qobject_to(QList, qdict_get(qdict, "data")));
switch (kind) {
case USER_DEF_NATIVE_LIST_UNION_KIND_U8:
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qnum(tmp);
+ qvalue = qobject_to(QNum, tmp);
g_assert(qnum_get_try_uint(qvalue, &val));
g_assert_cmpint(val, ==, i);
qobject_decref(qlist_pop(qlist));
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qnum(tmp);
+ qvalue = qobject_to(QNum, tmp);
g_assert(qnum_get_try_int(qvalue, &val));
g_assert_cmpint(val, ==, i);
qobject_decref(qlist_pop(qlist));
QBool *qvalue;
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qbool(tmp);
+ qvalue = qobject_to(QBool, tmp);
g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0);
qobject_decref(qlist_pop(qlist));
}
gchar str[8];
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qstring(tmp);
+ qvalue = qobject_to(QString, tmp);
sprintf(str, "%d", i);
g_assert_cmpstr(qstring_get_str(qvalue), ==, str);
qobject_decref(qlist_pop(qlist));
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qnum(tmp);
+ qvalue = qobject_to(QNum, tmp);
g_string_printf(double_expected, "%.6f", (double)i / 3);
g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue));
g_assert_cmpstr(double_actual->str, ==, double_expected->str);
g_assert(qdict_haskey(resp, "return"));
ret = qdict_get_qlist(resp, "return");
- cpu0 = qobject_to_qdict(qlist_peek(ret));
+ cpu0 = qobject_to(QDict, qlist_peek(ret));
path = g_strdup(qdict_get_str(cpu0, "qom_path"));
QDECREF(resp);
return path;
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
static bool qom_get_bool(const char *path, const char *prop)
{
- QBool *value = qobject_to_qbool(qom_get(path, prop));
+ QBool *value = qobject_to(QBool, qom_get(path, prop));
bool b = qbool_get_bool(value);
QDECREF(value);
qtest_start(args->cmdline);
path = get_cpu0_qom_path();
- value = qobject_to_qnum(qom_get(path, args->property));
+ value = qobject_to(QNum, qom_get(path, args->property));
g_assert(qnum_get_try_int(value, &val));
g_assert_cmpint(val, ==, args->expected_value);
qtest_end();
const QListEntry *e;
for (e = qlist_first(features); e; e = qlist_next(e)) {
- QDict *w = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *w = qobject_to(QDict, qlist_entry_obj(e));
const char *rreg = qdict_get_str(w, "cpuid-register");
uint32_t reax = qdict_get_int(w, "cpuid-input-eax");
bool has_ecx = qdict_haskey(w, "cpuid-input-ecx");
recx = qdict_get_int(w, "cpuid-input-ecx");
}
if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) {
- g_assert(qnum_get_try_int(qobject_to_qnum(qdict_get(w, "features")),
- &val));
+ g_assert(qnum_get_try_int(qobject_to(QNum,
+ qdict_get(w, "features")),
+ &val));
return val;
}
}
qtest_start(args->cmdline);
path = get_cpu0_qom_path();
- present = qobject_to_qlist(qom_get(path, "feature-words"));
- filtered = qobject_to_qlist(qom_get(path, "filtered-features"));
+ present = qobject_to(QList, qom_get(path, "feature-words"));
+ filtered = qobject_to(QList, qom_get(path, "filtered-features"));
value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg);
value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg);
qtest_end();
monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
handle_hmp_command(void *mon, const char *cmdline) "mon %p cmdline: %s"
handle_qmp_command(void *mon, const char *req) "mon %p req: %s"
+monitor_suspend(void *ptr, int cnt) "mon %p: %d"
+monitor_qmp_cmd_in_band(const char *id) "%s"
+monitor_qmp_cmd_out_of_band(const char *id) "%s"
# dma-helpers.c
dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d"
if (!next) {
return NULL;
}
- cur = qobject_to_qdict(next);
+ cur = qobject_to(QDict, next);
assert(cur);
}
has_member = true;
}
- qdict = qobject_to_qdict(ent->value);
+ qdict = qobject_to(QDict, ent->value);
if (!qdict) {
continue;
}
}
QLIST_FOREACH_ENTRY(list, list_entry) {
- QDict *section = qobject_to_qdict(qlist_entry_obj(list_entry));
+ QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry));
char *opt_name;
if (!section) {
switch (qobject_type(obj)) {
case QTYPE_QSTRING:
- value = qstring_get_str(qobject_to_qstring(obj));
+ value = qstring_get_str(qobject_to(QString, obj));
break;
case QTYPE_QNUM:
- tmp = qnum_to_string(qobject_to_qnum(obj));
+ tmp = qnum_to_string(qobject_to(QNum, obj));
value = tmp;
break;
case QTYPE_QBOOL:
pstrcpy(buf, sizeof(buf),
- qbool_get_bool(qobject_to_qbool(obj)) ? "on" : "off");
+ qbool_get_bool(qobject_to(QBool, obj)) ? "on" : "off");
value = buf;
break;
default:
qemu_init_exec_dir(argv[0]);
module_call_init(MODULE_INIT_QOM);
- monitor_init_qmp_commands();
qemu_add_opts(&qemu_drive_opts);
qemu_add_drive_opts(&qemu_legacy_drive_opts);
default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);
+ /*
+ * Note: qtest_enabled() (which is used in monitor_qapi_event_init())
+ * depends on configure_accelerator() above.
+ */
+ monitor_init_globals();
+
if (qemu_opts_foreach(qemu_find_opts("mon"),
mon_init_func, NULL, NULL)) {
exit(1);