qapi patches for 2018-03-12, 2.12 softfreeze
- Marc-André Lureau: 0/4 qapi: generate a literal qobject for introspection - Max Reitz: 0/7 block: Handle null backing link - Daniel P. Berrange: chardev: tcp: postpone TLS work until machine done - Peter Xu: 00/23 QMP: out-of-band (OOB) execution support - Vladimir Sementsov-Ogievskiy: 0/2 block latency histogram - Eric Blake: qapi: Pass '-u' when doing non-silent diff -----BEGIN PGP SIGNATURE----- Comment: Public key at http://people.redhat.com/eblake/eblake.gpg iQEcBAABCAAGBQJasBaIAAoJEKeha0olJ0NqEdEH/0pKMS2sErLZBE6G9qkZmvx2 bkbuiUx0skknCpGKLJ09s+wcZl2HtHS8U4+E8yQO/VujoWmhYxaufBGAtPQTyHm3 GC4mUDoPsGnVaQinZ8uT8284QmlgRoiWCt/LYvfP3D6gd9dHtMv3deqQGZrkOYZJ 7pWcSkr6BBTH/+7wgVzKC9FcriRq0va4/hee1juFVK2KuvBHJCALi01CQmzR0C+w YFObgFfRJb262gtuZQmMe/Y9ZuP4fUdwoz3n9sGZbUu4Jh74SWL4hIw2BWCh4Txl VXWrc/RfurycKAFKMcaXfpkjXx8f+tO/WRk2t05biiRJ1VYK0f686YSwd6vV9dw= =Ed01 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-qapi-2018-03-12-v4' into staging qapi patches for 2018-03-12, 2.12 softfreeze - Marc-André Lureau: 0/4 qapi: generate a literal qobject for introspection - Max Reitz: 0/7 block: Handle null backing link - Daniel P. Berrange: chardev: tcp: postpone TLS work until machine done - Peter Xu: 00/23 QMP: out-of-band (OOB) execution support - Vladimir Sementsov-Ogievskiy: 0/2 block latency histogram - Eric Blake: qapi: Pass '-u' when doing non-silent diff # gpg: Signature made Mon 19 Mar 2018 19:59:04 GMT # gpg: using RSA key A7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" # gpg: aka "[jpeg image of size 6874]" # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-qapi-2018-03-12-v4: (38 commits) qapi: Pass '-u' when doing non-silent diff qapi: add block latency histogram interface block/accounting: introduce latency histogram tests: qmp-test: add oob test tests: qmp-test: verify command batching qmp: add command "x-oob-test" monitor: enable IO thread for (qmp & !mux) typed qmp: isolate responses into io thread qmp: support out-of-band (oob) execution qapi: introduce new cmd option "allow-oob" monitor: send event when command queue full qmp: add new event "command-dropped" monitor: separate QMP parser and dispatcher monitor: let suspend/resume work even with QMPs monitor: let suspend_cnt be thread safe monitor: introduce monitor_qmp_respond() qmp: introduce QMPCapability monitor: allow using IO thread for parsing monitor: let mon_list be tail queue monitor: unify global init ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
4bdc24fa01
13
block.c
13
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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
53
block/qapi.c
53
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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
64
blockdev.c
64
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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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") },
|
||||
{ }
|
||||
})),
|
||||
...
|
||||
{ }
|
||||
}));
|
||||
|
@ -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
|
||||
-------------
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)))
|
||||
|
||||
|
708
monitor.c
708
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 = {
|
||||
|
@ -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": {} }
|
||||
#
|
||||
|
@ -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:
|
||||
|
@ -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 }
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
18
qmp.c
18
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
15
qom/object.c
15
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;
|
||||
|
@ -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):
|
||||
|
@ -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']
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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"));
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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' % \
|
||||
|
@ -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 ==="
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
7
vl.c
7
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);
|
||||
|
Loading…
Reference in New Issue
Block a user