mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+47
-6
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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");
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+47
-17
@@ -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);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user