diff --git a/block.c b/block.c index f7f9d8eca7..a2caadf0a0 100644 --- a/block.c +++ b/block.c @@ -33,6 +33,7 @@ #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" @@ -1457,7 +1458,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp) 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"); @@ -2433,7 +2434,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) } 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 @@ -2645,7 +2646,13 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, /* 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"); } diff --git a/block/accounting.c b/block/accounting.c index 87ef5bbfaa..70a3d9a426 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -94,6 +94,94 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, 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) { @@ -116,6 +204,9 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, 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; diff --git a/block/parallels.c b/block/parallels.c index 2da5e56a9d..e2515dec81 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -647,7 +647,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, qobj = qdict_crumple(qdict, errp); QDECREF(qdict); - qdict = qobject_to_qdict(qobj); + qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; goto done; diff --git a/block/qapi.c b/block/qapi.c index 4c9923d262..04c6fc69b9 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -394,6 +394,37 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, 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); @@ -459,6 +490,16 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *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, @@ -647,29 +688,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f, { 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; } @@ -730,7 +771,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, 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); diff --git a/block/qcow.c b/block/qcow.c index 2e3770ca63..f92891676c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -996,7 +996,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, qobj = qdict_crumple(qdict, errp); QDECREF(qdict); - qdict = qobject_to_qdict(qobj); + qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; goto fail; diff --git a/block/qcow2.c b/block/qcow2.c index 7472af6931..cf4f3becae 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3125,7 +3125,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt /* 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; diff --git a/block/qed.c b/block/qed.c index 46a84beeed..35ff505066 100644 --- a/block/qed.c +++ b/block/qed.c @@ -764,7 +764,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, qobj = qdict_crumple(qdict, errp); QDECREF(qdict); - qdict = qobject_to_qdict(qobj); + qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; goto fail; diff --git a/block/rbd.c b/block/rbd.c index 294ed07ac4..5b64849dc6 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -263,14 +263,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, 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); diff --git a/block/sheepdog.c b/block/sheepdog.c index 797ea5953b..387f59c8aa 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1887,7 +1887,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, return -EINVAL; } - qdict = qobject_to_qdict(obj); + qdict = qobject_to(QDict, obj); qdict_flatten(qdict); qdict_put_str(qdict, "driver", "sheepdog"); diff --git a/block/vhdx.c b/block/vhdx.c index f1b97f4b49..d2c54b7891 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1997,7 +1997,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, qobj = qdict_crumple(qdict, errp); QDECREF(qdict); - qdict = qobject_to_qdict(qobj); + qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; goto fail; diff --git a/block/vpc.c b/block/vpc.c index 28ffa0d2f8..44f99a4d1b 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1120,7 +1120,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, qobj = qdict_crumple(qdict, errp); QDECREF(qdict); - qdict = qobject_to_qdict(qobj); + qdict = qobject_to(QDict, qobj); if (qdict == NULL) { ret = -EINVAL; goto fail; diff --git a/blockdev.c b/blockdev.c index a6758c1220..c31bf3d98d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -334,7 +334,8 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, 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); @@ -346,7 +347,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, } 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); @@ -4044,7 +4045,6 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) 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); @@ -4054,23 +4054,10 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) } 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; @@ -4252,6 +4239,49 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, 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), diff --git a/chardev/char-socket.c b/chardev/char-socket.c index d92c5aee73..d057192ced 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -32,6 +32,7 @@ #include "qapi/error.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-sockets.h" +#include "sysemu/sysemu.h" #include "chardev/char-io.h" @@ -722,6 +723,11 @@ static void tcp_chr_tls_init(Chardev *chr) 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, @@ -1162,6 +1168,10 @@ static int tcp_chr_machine_done_hook(Chardev *chr) tcp_chr_connect_async(chr); } + if (s->ioc && s->tls_creds) { + tcp_chr_tls_init(chr); + } + return 0; } diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 25b7180a18..a569d24745 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -554,9 +554,12 @@ following example objects: === 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 @@ -636,6 +639,49 @@ possible, the command expression should include the optional key '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 === @@ -739,10 +785,12 @@ references by name. 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" @@ -1319,18 +1367,27 @@ Example: #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") }, + { } + })), + ... + { } + })); diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index f8b5356015..6fa193a80b 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -83,16 +83,27 @@ The greeting message format is: 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, @@ -102,10 +113,16 @@ The format for command execution is: 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 ---------------------- @@ -113,6 +130,11 @@ The format for command execution is: 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 ------------- diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index deb440f286..a66fb2dcd2 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -154,21 +154,21 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) /* 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; } @@ -507,14 +507,14 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot) 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); @@ -624,7 +624,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, /* 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, @@ -2638,12 +2638,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) 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; } diff --git a/include/block/accounting.h b/include/block/accounting.h index b833d26d6c..d1f67b10dd 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -27,6 +27,7 @@ #include "qemu/timed-average.h" #include "qemu/thread.h" +#include "qapi/qapi-builtin-types.h" typedef struct BlockAcctTimedStats BlockAcctTimedStats; typedef struct BlockAcctStats BlockAcctStats; @@ -45,6 +46,36 @@ struct BlockAcctTimedStats { 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]; @@ -57,6 +88,7 @@ struct BlockAcctStats { QSLIST_HEAD(, BlockAcctTimedStats) intervals; bool account_invalid; bool account_failed; + BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE]; }; typedef struct BlockAcctCookie { @@ -82,5 +114,8 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, 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 diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index d1024d4bdc..0cb0538a31 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -16,7 +16,7 @@ extern Monitor *cur_mon; 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); diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 1e694b5ecf..ffb4652f71 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -20,8 +20,9 @@ typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); 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 @@ -47,6 +48,8 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); 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); diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h index 629c508d34..b9a44a1bfe 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qapi/qmp/qbool.h @@ -23,7 +23,6 @@ struct QBool { 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); diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 7c6d844549..2cc3e906f7 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -39,7 +39,6 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value); 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), diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h index 5fd976a398..5c673acb06 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qapi/qmp/qlist.h @@ -53,7 +53,6 @@ QObject *qlist_pop(QList *qlist); 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); diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h index 56f9d97bd9..c0676d5daf 100644 --- a/include/qapi/qmp/qlit.h +++ b/include/qapi/qmp/qlit.h @@ -20,7 +20,7 @@ typedef struct QLitDictEntry QLitDictEntry; typedef struct QLitObject QLitObject; struct QLitObject { - int type; + QType type; union { bool qbool; int64_t qnum; @@ -50,4 +50,6 @@ struct QLitDictEntry { bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs); +QObject *qobject_from_qlit(const QLitObject *qlit); + #endif /* QLIT_H */ diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 15e3971c7f..3e47475b2c 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -68,7 +68,6 @@ double qnum_get_double(QNum *qn); 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); diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index 012439a2e3..e022707578 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -50,6 +50,21 @@ struct QObject { #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) { @@ -102,4 +117,18 @@ static inline QType qobject_type(const QObject *obj) 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 */ diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index 98070ef3d6..30ae260a7f 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -27,10 +27,11 @@ QString *qstring_from_str(const char *str); 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); diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 2cbe6a4f16..9f762695d1 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -82,15 +82,21 @@ 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))) diff --git a/monitor.c b/monitor.c index 3117a3e689..6ccd2fc089 100644 --- a/monitor.c +++ b/monitor.c @@ -35,6 +35,8 @@ #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" @@ -58,6 +60,7 @@ #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" @@ -79,6 +82,7 @@ #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) @@ -168,6 +172,16 @@ typedef struct { * 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; /* @@ -190,9 +204,11 @@ struct Monitor { 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; @@ -207,16 +223,25 @@ struct Monitor { 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; @@ -240,6 +265,21 @@ static inline bool monitor_is_qmp(const Monitor *mon) 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? */ @@ -382,7 +422,8 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...) return 0; } -static void monitor_json_emitter(Monitor *mon, const QObject *data) +static void monitor_json_emitter_raw(Monitor *mon, + QObject *data) { QString *json; @@ -396,6 +437,71 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) 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 }, @@ -417,7 +523,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) 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)); @@ -447,7 +553,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) /* 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); @@ -570,13 +676,19 @@ static void monitor_qapi_event_init(void) 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) @@ -589,6 +701,9 @@ 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, @@ -597,8 +712,7 @@ 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; @@ -956,7 +1070,7 @@ EventInfoList *qmp_query_events(Error **errp) 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); } /* @@ -1006,7 +1120,7 @@ static void qmp_unregister_commands_hack(void) #endif } -void monitor_init_qmp_commands(void) +static void monitor_init_qmp_commands(void) { /* * Two command lists: @@ -1032,8 +1146,90 @@ void monitor_init_qmp_commands(void) 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 " @@ -1041,6 +1237,21 @@ void qmp_qmp_capabilities(Error **errp) 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; } @@ -3758,31 +3969,74 @@ static int monitor_can_read(void *opaque) { 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); } - qdict = qobject_to_qdict(req); - if (qdict) { - id = qdict_get(qdict, "id"); - qobject_incref(id); - qdict_del(qdict, "id"); - } /* else will fail qmp_dispatch() */ + 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); + } + + 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); @@ -3790,10 +4044,15 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) 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))) { @@ -3804,37 +4063,171 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) } } -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); } - qobject_decref(id); - qobject_decref(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); + } + + qemu_mutex_unlock(&monitor_lock); + + return req_obj; +} + +static void monitor_qmp_bh_dispatcher(void *data) +{ + QMPRequest *req_obj = monitor_qmp_requests_pop_one(); + + 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); + } +} + +#define QMP_REQ_QUEUE_LEN_MAX (8) + +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 *old_mon = cur_mon; + Monitor *mon = opaque; - cur_mon = opaque; - - json_message_parser_feed(&cur_mon->qmp.parser, (const char *) buf, size); - - cur_mon = old_mon; + json_message_parser_feed(&mon->qmp.parser, (const char *) buf, size); } static void monitor_read(void *opaque, const uint8_t *buf, int size) @@ -3869,28 +4262,70 @@ static void monitor_command_cb(void *opaque, const char *cmdline, 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) @@ -3901,7 +4336,8 @@ 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++; @@ -3929,19 +4365,19 @@ static void monitor_event(void *opaque, int event) 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; @@ -3985,6 +4421,49 @@ static void sortcmdlist(void) 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. */ @@ -4025,24 +4504,43 @@ void error_vprintf_unless_qmp(const char *fmt, va_list ap) } } -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); +} + +static void monitor_qmp_setup_handlers_bh(void *opaque) +{ + 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; + } + + 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) { - static int is_first_init = 1; - Monitor *mon; + 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); - if (is_first_init) { - monitor_qapi_event_init(); - sortcmdlist(); - is_first_init = 0; - } - - mon = g_malloc(sizeof(*mon)); - monitor_data_init(mon); + monitor_data_init(mon, false, use_io_thr); qemu_chr_fe_init(&mon->chr, chr, &error_abort); mon->flags = flags; @@ -4055,31 +4553,77 @@ void monitor_init(Chardev *chr, int 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 = { diff --git a/qapi/block-core.json b/qapi/block-core.json index 5b0ad1a8b7..1088ab0c78 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -453,6 +453,106 @@ '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: # @@ -733,6 +833,12 @@ # @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', @@ -745,7 +851,10 @@ '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: @@ -1174,7 +1283,7 @@ # @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 ## @@ -1347,7 +1456,7 @@ # "node-name": "node1534", # "file": { "driver": "file", # "filename": "hd1.qcow2" }, -# "backing": "" } } +# "backing": null } } # # <- { "return": {} } # diff --git a/qapi/introspect.json b/qapi/introspect.json index 5b3e6e9d78..c7f67b7d78 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -259,12 +259,16 @@ # # @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: diff --git a/qapi/misc.json b/qapi/misc.json index 6150b9a003..c31fc983f3 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -10,21 +10,47 @@ # # 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: @@ -3364,3 +3390,58 @@ # ## { '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 } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index e31ac4be1f..dd05907265 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -17,8 +17,9 @@ #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; @@ -26,7 +27,7 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) 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; @@ -50,6 +51,14 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) "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); @@ -120,6 +129,28 @@ QObject *qmp_build_error_object(Error *err) 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; diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 023317b05f..a7569d5dce 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -137,7 +137,7 @@ static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, 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); @@ -185,7 +185,7 @@ static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, return NULL; } - qstr = qobject_to_qstring(qobj); + qstr = qobject_to(QString, qobj); if (!qstr) { switch (qobject_type(qobj)) { case QTYPE_QDICT: @@ -224,11 +224,11 @@ static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, 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; } @@ -339,7 +339,7 @@ static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, 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; @@ -353,7 +353,7 @@ static void qobject_input_check_list(Visitor *v, Error **errp) 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", @@ -395,7 +395,7 @@ static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, 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"); @@ -430,7 +430,7 @@ static void qobject_input_type_uint64(Visitor *v, const char *name, if (!qobj) { return; } - qnum = qobject_to_qnum(qobj); + qnum = qobject_to(QNum, qobj); if (!qnum) { goto err; } @@ -477,7 +477,7 @@ static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj, 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"); @@ -518,7 +518,7 @@ static void qobject_input_type_str(Visitor *v, const char *name, char **obj, 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"); @@ -547,7 +547,7 @@ static void qobject_input_type_number(Visitor *v, const char *name, double *obj, 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"); @@ -734,7 +734,7 @@ Visitor *qobject_input_visitor_new_str(const char *str, } return NULL; } - args = qobject_to_qdict(obj); + args = qobject_to(QDict, obj); assert(args); v = qobject_input_visitor_new(QOBJECT(args)); } else { diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 7c3b42cfe2..877e37eeb8 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -92,11 +92,11 @@ static void qobject_output_add_obj(QObjectOutputVisitor *qov, const char *name, 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(); diff --git a/qemu-doc.texi b/qemu-doc.texi index f8da9b8135..89fa80518a 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2789,6 +2789,13 @@ support page sizes < 4096 any longer. 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 diff --git a/qemu-options.hx b/qemu-options.hx index 6113bce08a..74158e7493 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -743,8 +743,8 @@ Reference to or definition of the data source block driver node @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 diff --git a/qga/main.c b/qga/main.c index 837a75a46a..df1888edc1 100644 --- a/qga/main.c +++ b/qga/main.c @@ -607,7 +607,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens) 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(); diff --git a/qmp.c b/qmp.c index 8c7d1cc479..ea3760acb1 100644 --- a/qmp.c +++ b/qmp.c @@ -705,7 +705,7 @@ void qmp_object_add(const char *type, const char *id, 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; @@ -770,3 +770,19 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp) 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); + } +} diff --git a/qobject/json-parser.c b/qobject/json-parser.c index b724562415..769b960c9f 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -276,7 +276,8 @@ static void parser_context_free(JSONParserContext *ctxt) */ 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); @@ -285,8 +286,8 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) 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; } @@ -308,14 +309,14 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) 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; } diff --git a/qobject/qbool.c b/qobject/qbool.c index e5a7a53879..b58249925c 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -39,23 +39,12 @@ bool qbool_get_bool(const QBool *qb) 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; } /** @@ -65,5 +54,5 @@ bool qbool_is_equal(const QObject *x, const QObject *y) void qbool_destroy_obj(QObject *obj) { assert(obj != NULL); - g_free(qobject_to_qbool(obj)); + g_free(qobject_to(QBool, obj)); } diff --git a/qobject/qdict.c b/qobject/qdict.c index 229b8c840b..d1997a0d8a 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -37,17 +37,6 @@ QDict *qdict_new(void) 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) @@ -206,7 +195,7 @@ size_t qdict_size(const QDict *qdict) */ 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))); } /** @@ -219,7 +208,7 @@ double qdict_get_double(const QDict *qdict, const char *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))); } /** @@ -232,7 +221,7 @@ int64_t qdict_get_int(const QDict *qdict, const char *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))); } /** @@ -240,7 +229,7 @@ bool qdict_get_bool(const QDict *qdict, const char *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)); } /** @@ -248,7 +237,7 @@ QList *qdict_get_qlist(const QDict *qdict, const char *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)); } /** @@ -262,7 +251,7 @@ QDict *qdict_get_qdict(const QDict *qdict, const char *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))); } /** @@ -275,7 +264,7 @@ const char *qdict_get_str(const QDict *qdict, const char *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)) { @@ -294,7 +283,7 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, */ 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; } @@ -309,7 +298,7 @@ bool qdict_get_try_bool(const QDict *qdict, const char *key, bool 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; } @@ -432,8 +421,8 @@ void qdict_del(QDict *qdict, const char *key) */ 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)) { @@ -461,7 +450,7 @@ void qdict_destroy_obj(QObject *obj) 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]); @@ -532,9 +521,9 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) 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); @@ -568,11 +557,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) 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) { @@ -893,18 +882,20 @@ QObject *qdict_crumple(const QDict *src, Error **errp) 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", @@ -924,9 +915,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp) 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; } @@ -961,7 +952,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp) } 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; diff --git a/qobject/qjson.c b/qobject/qjson.c index e1ce75651c..655d38adf1 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -137,14 +137,14 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) 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]; @@ -201,7 +201,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) } case QTYPE_QDICT: { ToJsonIterState s; - QDict *val = qobject_to_qdict(obj); + QDict *val = qobject_to(QDict, obj); s.count = 0; s.str = str; @@ -220,7 +220,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) } case QTYPE_QLIST: { ToJsonIterState s; - QList *val = qobject_to_qlist(obj); + QList *val = qobject_to(QList, obj); s.count = 0; s.str = str; @@ -238,7 +238,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) 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"); diff --git a/qobject/qlist.c b/qobject/qlist.c index 613a95c12b..954fe98375 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -151,17 +151,6 @@ size_t qlist_size(const QList *qlist) 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 * @@ -173,8 +162,8 @@ QList *qobject_to_qlist(const QObject *obj) */ 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); @@ -203,7 +192,7 @@ void qlist_destroy_obj(QObject *obj) 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); diff --git a/qobject/qlit.c b/qobject/qlit.c index 948e0b860c..be8332136c 100644 --- a/qobject/qlit.c +++ b/qobject/qlit.c @@ -21,6 +21,7 @@ #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) { @@ -68,16 +69,16 @@ bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs) 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: @@ -86,3 +87,39 @@ bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs) 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; +} diff --git a/qobject/qnum.c b/qobject/qnum.c index 60c395c1bc..1501c82832 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -199,17 +199,6 @@ char *qnum_to_string(QNum *qn) 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 * @@ -221,8 +210,8 @@ QNum *qobject_to_qnum(const QObject *obj) */ 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: @@ -271,5 +260,5 @@ bool qnum_is_equal(const QObject *x, const QObject *y) void qnum_destroy_obj(QObject *obj) { assert(obj != NULL); - g_free(qobject_to_qnum(obj)); + g_free(qobject_to(QNum, obj)); } diff --git a/qobject/qstring.c b/qobject/qstring.c index 05b4bbc2d6..afca54b47a 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -105,17 +105,6 @@ void qstring_append_chr(QString *qstring, int c) 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 * @@ -127,13 +116,34 @@ const char *qstring_get_str(const QString *qstring) 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); } /** @@ -145,7 +155,7 @@ void qstring_destroy_obj(QObject *obj) QString *qs; assert(obj != NULL); - qs = qobject_to_qstring(obj); + qs = qobject_to(QString, obj); g_free(qs->string); g_free(qs); } diff --git a/qom/object.c b/qom/object.c index 6088f55943..467795189c 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1136,18 +1136,15 @@ char *object_property_get_str(Object *obj, const char *name, 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); @@ -1203,7 +1200,7 @@ bool object_property_get_bool(Object *obj, const char *name, 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; @@ -1235,7 +1232,7 @@ int64_t object_property_get_int(Object *obj, const char *name, 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; @@ -1264,7 +1261,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, 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; diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 21a7e0dbe6..0c5da3a54d 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -193,10 +193,18 @@ out: 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", @@ -268,7 +276,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); 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)) @@ -277,7 +285,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); 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): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 97e9060b67..2c05e3c284 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -921,7 +921,8 @@ def check_exprs(exprs): 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']) @@ -1044,7 +1045,7 @@ class QAPISchemaVisitor(object): 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): @@ -1421,7 +1422,7 @@ class QAPISchemaAlternateType(QAPISchemaType): 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) @@ -1432,6 +1433,7 @@ class QAPISchemaCommand(QAPISchemaEntity): 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: @@ -1455,7 +1457,8 @@ class QAPISchemaCommand(QAPISchemaEntity): 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): @@ -1674,6 +1677,7 @@ class QAPISchema(object): 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)) @@ -1681,7 +1685,8 @@ class QAPISchema(object): 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'] diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 0ea68bf813..9b312b2c51 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -134,10 +134,9 @@ def texi_enum_value(value): 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) @@ -228,7 +227,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): 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) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f66c397fb0..f9e67e8227 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -13,26 +13,38 @@ See the COPYING file in the top-level directory. 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 @@ -48,7 +60,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): ' * 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(''' @@ -63,27 +75,27 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): 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 = {} @@ -117,12 +129,12 @@ const char %(c_name)s[] = %(c_string)s; 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)} @@ -138,38 +150,39 @@ const char %(c_name)s[] = %(c_string)s; 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): diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6bb4ce8719..739a1cf47a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3141,7 +3141,7 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, 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; diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 1d5f0da4fe..2741b6803f 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -453,7 +453,7 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, 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; diff --git a/tests/Makefile.include b/tests/Makefile.include index 42fd426cd0..412aed1737 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -929,14 +929,14 @@ $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json $^ >$*.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: diff --git a/tests/check-qdict.c b/tests/check-qdict.c index a3faea8bfc..2e73c2f86e 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -51,7 +51,7 @@ static void qdict_put_obj_test(void) 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); @@ -81,7 +81,7 @@ static void qdict_get_test(void) 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); @@ -216,7 +216,7 @@ static void qdict_del_test(void) 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); } @@ -381,9 +381,9 @@ static void qdict_array_split_test(void) 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); @@ -450,7 +450,7 @@ static void qdict_array_split_test(void) 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)); @@ -607,7 +607,7 @@ static void qdict_crumple_test_recursive(void) 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); @@ -629,14 +629,14 @@ static void qdict_crumple_test_recursive(void) 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")); diff --git a/tests/check-qjson.c b/tests/check-qjson.c index a18ea47cb7..997f4d3d2c 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -60,7 +60,7 @@ static void escaped_string(void) 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); @@ -92,7 +92,7 @@ static void simple_string(void) 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); @@ -123,7 +123,7 @@ static void single_quote_string(void) 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); @@ -817,7 +817,7 @@ static void utf8_string(void) 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 { @@ -843,7 +843,7 @@ static void utf8_string(void) */ 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); } @@ -864,8 +864,8 @@ static void vararg_string(void) 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); @@ -893,8 +893,9 @@ static void simple_number(void) 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); @@ -920,7 +921,7 @@ static void large_number(void) 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)); @@ -930,7 +931,7 @@ static void large_number(void) 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)); @@ -941,7 +942,7 @@ static void large_number(void) 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)); @@ -973,7 +974,7 @@ static void float_number(void) 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); @@ -997,17 +998,17 @@ static void vararg_number(void) 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); } @@ -1020,7 +1021,7 @@ static void keyword_literal(void) 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); @@ -1031,7 +1032,7 @@ static void keyword_literal(void) 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); @@ -1041,13 +1042,13 @@ static void keyword_literal(void) 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); diff --git a/tests/check-qlist.c b/tests/check-qlist.c index 259980d523..a1c69ed648 100644 --- a/tests/check-qlist.c +++ b/tests/check-qlist.c @@ -56,7 +56,7 @@ static void qobject_to_qlist_test(void) qlist = qlist_new(); - g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist); + g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); QDECREF(qlist); } @@ -71,7 +71,7 @@ static void iter_func(QObject *obj, void *opaque) 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)); diff --git a/tests/check-qlit.c b/tests/check-qlit.c index 5d0f65b9c7..96bbb06f2c 100644 --- a/tests/check-qlit.c +++ b/tests/check-qlit.c @@ -9,9 +9,11 @@ #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[]) { @@ -57,17 +59,43 @@ static void qlit_equal_qobject_test(void) 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(); } diff --git a/tests/check-qnum.c b/tests/check-qnum.c index 2b667f7ad7..9187da734b 100644 --- a/tests/check-qnum.c +++ b/tests/check-qnum.c @@ -126,11 +126,11 @@ static void qobject_to_qnum_test(void) 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); } diff --git a/tests/check-qobject.c b/tests/check-qobject.c index 7a3670643c..7629b8071b 100644 --- a/tests/check-qobject.c +++ b/tests/check-qobject.c @@ -275,7 +275,7 @@ static void qobject_is_equal_dict_test(void) 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); diff --git a/tests/check-qstring.c b/tests/check-qstring.c index 112ec08967..9c4dd3f94f 100644 --- a/tests/check-qstring.c +++ b/tests/check-qstring.c @@ -79,7 +79,7 @@ static void qobject_to_qstring_test(void) 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); } diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index b80058fe98..a01321aced 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -52,7 +52,7 @@ static QDict *qom_type_index(QList *types) 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); @@ -85,7 +85,7 @@ static QDict *type_list_find(QList *types, const char *name) 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; @@ -151,7 +151,7 @@ static void test_qom_list_parents(const char *parent) 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)); @@ -173,7 +173,7 @@ static void test_qom_list_fields(void) 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") : @@ -216,8 +216,8 @@ static void test_device_intro_concrete(void) 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); } @@ -238,7 +238,7 @@ static void test_abstract_interfaces(void) 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"); /* diff --git a/tests/libqtest.c b/tests/libqtest.c index 13c910069b..200b2b9e92 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -430,7 +430,7 @@ static void qmp_response(JSONMessageParser *parser, GQueue *tokens) } g_assert(!qmp->response); - qmp->response = qobject_to_qdict(obj); + qmp->response = qobject_to(QDict, obj); g_assert(qmp->response); } @@ -1008,11 +1008,11 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) 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); diff --git a/tests/numa-test.c b/tests/numa-test.c index 68aca9cb38..0f861d8176 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -98,7 +98,7 @@ static void test_query_cpus(const void *data) 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")); @@ -140,7 +140,7 @@ static void pc_numa_cpu(const void *data) 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"); @@ -193,7 +193,7 @@ static void spapr_numa_cpu(const void *data) 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"); @@ -236,7 +236,7 @@ static void aarch64_numa_cpu(const void *data) 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"); diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 67e417e298..10e68b01d9 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -42,7 +42,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): 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' % \ diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index 0b059aba90..aa1ba4a98e 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -82,6 +82,26 @@ $QEMU_IO_PROG --cache $CACHEMODE \ $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 ===" diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out index 0bf5a13ec1..89e3e4340a 100644 --- a/tests/qemu-iotests/089.out +++ b/tests/qemu-iotests/089.out @@ -19,6 +19,14 @@ Pattern verification failed at offset 0, 512 bytes 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 diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 7470c6b754..07c0b87e27 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -20,6 +20,7 @@ #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"; @@ -79,6 +80,9 @@ static void test_qmp_protocol(void) QDict *resp, *q, *ret; QList *capabilities; QTestState *qts; + const QListEntry *entry; + QString *qstr; + int i; qts = qtest_init_without_qmp_handshake(common_args); @@ -88,7 +92,12 @@ static void test_qmp_protocol(void) 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 */ @@ -131,9 +140,94 @@ static void test_qmp_protocol(void) 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 { @@ -318,6 +412,7 @@ int main(int argc, char *argv[]) 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); diff --git a/tests/qom-test.c b/tests/qom-test.c index 9dab7ac61e..a34ff6ba53 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -62,9 +62,9 @@ static void test_properties(const char *path, bool recurse) } 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); diff --git a/tests/test-char.c b/tests/test-char.c index df939cc5c4..306c728335 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -319,7 +319,7 @@ static void char_socket_test_common(Chardev *chr) 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); diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 94eb4df28d..029f05202a 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -195,7 +195,7 @@ static void check_list012(QList *qlist) 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); @@ -654,12 +654,12 @@ static void test_keyval_visit_any(void) 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); diff --git a/tests/test-qga.c b/tests/test-qga.c index 5c5b661f8a..e6ab788f31 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -297,8 +297,8 @@ static void test_qga_get_vcpus(gconstpointer fix) /* 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); } @@ -318,10 +318,10 @@ static void test_qga_get_fsinfo(gconstpointer fix) 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); @@ -363,8 +363,9 @@ static void test_qga_get_memory_blocks(gconstpointer fix) 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")); } } @@ -385,7 +386,7 @@ static void test_qga_network_get_interfaces(gconstpointer fix) /* 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); } diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 5b1cee6912..93fbbb1b73 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -100,7 +100,7 @@ static void test_dispatch_cmd(void) 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); @@ -117,7 +117,7 @@ static void test_dispatch_cmd_failure(void) 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); @@ -131,7 +131,7 @@ static void test_dispatch_cmd_failure(void) 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); @@ -145,7 +145,7 @@ static QObject *test_qmp_dispatch(QDict *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); @@ -176,7 +176,7 @@ static void test_dispatch_cmd_io(void) 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"); @@ -197,7 +197,7 @@ static void test_dispatch_cmd_io(void) 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); diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 31f35b3e66..bb1036615f 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -60,22 +60,22 @@ void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) 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: diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index 79b1a8cb17..6dc59c6211 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -479,7 +479,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, 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); @@ -487,22 +487,22 @@ static void test_visitor_in_any(TestInputVisitorData *data, 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); @@ -1250,24 +1250,27 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data, } 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) diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c index 11e8c5aa40..ecf21c0f31 100644 --- a/tests/test-qobject-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -66,7 +66,7 @@ static void test_visitor_out_int(TestOutputVisitorData *data, 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); @@ -80,7 +80,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data, 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); } @@ -93,7 +93,7 @@ static void test_visitor_out_number(TestOutputVisitorData *data, 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); } @@ -106,7 +106,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data, 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); } @@ -120,7 +120,7 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, /* 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), ==, ""); } @@ -134,7 +134,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, 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); @@ -167,7 +167,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *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); @@ -206,7 +206,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, 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]); @@ -280,7 +280,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, 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)); @@ -289,7 +289,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, 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); @@ -342,7 +342,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, 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); @@ -356,16 +356,16 @@ static void test_visitor_out_any(TestOutputVisitorData *data, 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"); } @@ -382,7 +382,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, 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"); @@ -406,7 +406,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, 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); @@ -419,7 +419,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, 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"); @@ -444,7 +444,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, 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); @@ -466,7 +466,7 @@ static void test_visitor_out_null(TestOutputVisitorData *data, 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"); @@ -610,10 +610,10 @@ static void check_native_list(QObject *qobj, 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: @@ -627,7 +627,7 @@ static void check_native_list(QObject *qobj, 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)); @@ -651,7 +651,7 @@ static void check_native_list(QObject *qobj, 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)); @@ -663,7 +663,7 @@ static void check_native_list(QObject *qobj, 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)); } @@ -675,7 +675,7 @@ static void check_native_list(QObject *qobj, 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)); @@ -690,7 +690,7 @@ static void check_native_list(QObject *qobj, 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); diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c index 495dd1e7ef..02e41843fc 100644 --- a/tests/test-x86-cpuid-compat.c +++ b/tests/test-x86-cpuid-compat.c @@ -17,7 +17,7 @@ static char *get_cpu0_qom_path(void) 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; @@ -38,7 +38,7 @@ static QObject *qom_get(const char *path, const char *prop) #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); @@ -61,7 +61,7 @@ static void test_cpuid_prop(const void *data) 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(); @@ -105,7 +105,7 @@ static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx, 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"); @@ -116,8 +116,9 @@ static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t 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; } } @@ -133,8 +134,8 @@ static void test_feature_flag(const void *data) 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(); diff --git a/trace-events b/trace-events index 855b0ab240..22c7dd4ac6 100644 --- a/trace-events +++ b/trace-events @@ -47,6 +47,9 @@ monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p" 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" diff --git a/util/keyval.c b/util/keyval.c index 212ae90d00..1c7351a233 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -221,7 +221,7 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, if (!next) { return NULL; } - cur = qobject_to_qdict(next); + cur = qobject_to(QDict, next); assert(cur); } @@ -314,7 +314,7 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp) has_member = true; } - qdict = qobject_to_qdict(ent->value); + qdict = qobject_to(QDict, ent->value); if (!qdict) { continue; } diff --git a/util/qemu-config.c b/util/qemu-config.c index c651c4826e..20f7d1429d 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -528,7 +528,7 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, } 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) { diff --git a/util/qemu-option.c b/util/qemu-option.c index 2b412eff5e..d0756fda58 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -919,15 +919,15 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) 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: diff --git a/vl.c b/vl.c index 19340a324a..518ba43ab6 100644 --- a/vl.c +++ b/vl.c @@ -3066,7 +3066,6 @@ int main(int argc, char **argv, char **envp) 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); @@ -4537,6 +4536,12 @@ int main(int argc, char **argv, char **envp) 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);