* statistics subsystem

* virtio reset cleanups
 * build system cleanups
 * fix Cirrus CI
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmKpooQUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroNlFwf+OugLGRZl3KVc7akQwUJe9gg2T31h
 VkC+7Tei8FAwe8vDppVd+CYEIi0M3acxD2amRrv2etCCGSuySN1PbkfRcSfPBX01
 pRWpasdhfqnZR8Iidi7YW1Ou5CcGqKH49nunBhW10+osb/mu5sVscMuOJgTDj/lK
 CpsmDyk6572yGmczjNLlmhYcTU36clHpAZgazZHwk1PU+B3fCKlYYyvUpT3ItJvd
 cK92aIUWrfofl3yTy0k4IwvZwNjTBirlstOIomZ333xzSA+mm5TR+mTvGRTZ69+a
 v+snpMp4ILDMoB5kxQ42kK5WpdiN//LnriA9CBFDtOidsDDn8kx7gJe2RA==
 =Dxwa
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging

* statistics subsystem
* virtio reset cleanups
* build system cleanups
* fix Cirrus CI

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmKpooQUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroNlFwf+OugLGRZl3KVc7akQwUJe9gg2T31h
# VkC+7Tei8FAwe8vDppVd+CYEIi0M3acxD2amRrv2etCCGSuySN1PbkfRcSfPBX01
# pRWpasdhfqnZR8Iidi7YW1Ou5CcGqKH49nunBhW10+osb/mu5sVscMuOJgTDj/lK
# CpsmDyk6572yGmczjNLlmhYcTU36clHpAZgazZHwk1PU+B3fCKlYYyvUpT3ItJvd
# cK92aIUWrfofl3yTy0k4IwvZwNjTBirlstOIomZ333xzSA+mm5TR+mTvGRTZ69+a
# v+snpMp4ILDMoB5kxQ42kK5WpdiN//LnriA9CBFDtOidsDDn8kx7gJe2RA==
# =Dxwa
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 15 Jun 2022 02:12:36 AM PDT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [undefined]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [undefined]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (21 commits)
  build: include pc-bios/ part in the ROMS variable
  meson: put cross compiler info in a separate section
  q35:Enable TSEG only when G_SMRAME and TSEG_EN both enabled
  build: fix check for -fsanitize-coverage-allowlist
  tests/vm: allow running tests in an unconfigured source tree
  configure: cleanup -fno-pie detection
  configure: update list of preserved environment variables
  virtio-mmio: cleanup reset
  virtio: stop ioeventfd on reset
  virtio-mmio: stop ioeventfd on legacy reset
  s390x: simplify virtio_ccw_reset_virtio
  block: add more commands to preconfig mode
  hmp: add filtering of statistics by name
  qmp: add filtering of statistics by name
  hmp: add filtering of statistics by provider
  qmp: add filtering of statistics by provider
  hmp: add basic "info stats" implementation
  cutils: add functions for IEC and SI prefixes
  qmp: add filtering of statistics by target vCPU
  kvm: Support for querying fd-based stats
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-06-16 07:13:04 -07:00
commit def6fd6c9c
25 changed files with 1368 additions and 115 deletions

View File

@ -186,16 +186,14 @@ include $(SRC_PATH)/tests/Makefile.include
all: recurse-all
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
# Only keep -O and -g cflags
.PHONY: $(ROM_DIRS_RULES)
$(ROM_DIRS_RULES):
ROMS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROMS)))
.PHONY: $(ROMS_RULES)
$(ROMS_RULES):
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
.PHONY: recurse-all recurse-clean
recurse-all: $(addsuffix /all, $(ROM_DIRS))
recurse-clean: $(addsuffix /clean, $(ROM_DIRS))
recurse-all: $(addsuffix /all, $(ROMS))
recurse-clean: $(addsuffix /clean, $(ROMS))
######################################################################

View File

@ -47,6 +47,7 @@
#include "kvm-cpus.h"
#include "hw/boards.h"
#include "monitor/stats.h"
/* This check must be after config-host.h is included */
#ifdef CONFIG_EVENTFD
@ -2310,6 +2311,10 @@ bool kvm_dirty_ring_enabled(void)
return kvm_state->kvm_dirty_ring_size ? true : false;
}
static void query_stats_cb(StatsResultList **result, StatsTarget target,
strList *names, strList *targets, Error **errp);
static void query_stats_schemas_cb(StatsSchemaList **result, Error **errp);
static int kvm_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
@ -2638,6 +2643,11 @@ static int kvm_init(MachineState *ms)
}
}
if (kvm_check_extension(kvm_state, KVM_CAP_BINARY_STATS_FD)) {
add_stats_callbacks(STATS_PROVIDER_KVM, query_stats_cb,
query_stats_schemas_cb);
}
return 0;
err:
@ -3697,3 +3707,396 @@ static void kvm_type_init(void)
}
type_init(kvm_type_init);
typedef struct StatsArgs {
union StatsResultsType {
StatsResultList **stats;
StatsSchemaList **schema;
} result;
strList *names;
Error **errp;
} StatsArgs;
static StatsList *add_kvmstat_entry(struct kvm_stats_desc *pdesc,
uint64_t *stats_data,
StatsList *stats_list,
Error **errp)
{
Stats *stats;
uint64List *val_list = NULL;
/* Only add stats that we understand. */
switch (pdesc->flags & KVM_STATS_TYPE_MASK) {
case KVM_STATS_TYPE_CUMULATIVE:
case KVM_STATS_TYPE_INSTANT:
case KVM_STATS_TYPE_PEAK:
case KVM_STATS_TYPE_LINEAR_HIST:
case KVM_STATS_TYPE_LOG_HIST:
break;
default:
return stats_list;
}
switch (pdesc->flags & KVM_STATS_UNIT_MASK) {
case KVM_STATS_UNIT_NONE:
case KVM_STATS_UNIT_BYTES:
case KVM_STATS_UNIT_CYCLES:
case KVM_STATS_UNIT_SECONDS:
break;
default:
return stats_list;
}
switch (pdesc->flags & KVM_STATS_BASE_MASK) {
case KVM_STATS_BASE_POW10:
case KVM_STATS_BASE_POW2:
break;
default:
return stats_list;
}
/* Alloc and populate data list */
stats = g_new0(Stats, 1);
stats->name = g_strdup(pdesc->name);
stats->value = g_new0(StatsValue, 1);;
if (pdesc->size == 1) {
stats->value->u.scalar = *stats_data;
stats->value->type = QTYPE_QNUM;
} else {
int i;
for (i = 0; i < pdesc->size; i++) {
QAPI_LIST_PREPEND(val_list, stats_data[i]);
}
stats->value->u.list = val_list;
stats->value->type = QTYPE_QLIST;
}
QAPI_LIST_PREPEND(stats_list, stats);
return stats_list;
}
static StatsSchemaValueList *add_kvmschema_entry(struct kvm_stats_desc *pdesc,
StatsSchemaValueList *list,
Error **errp)
{
StatsSchemaValueList *schema_entry = g_new0(StatsSchemaValueList, 1);
schema_entry->value = g_new0(StatsSchemaValue, 1);
switch (pdesc->flags & KVM_STATS_TYPE_MASK) {
case KVM_STATS_TYPE_CUMULATIVE:
schema_entry->value->type = STATS_TYPE_CUMULATIVE;
break;
case KVM_STATS_TYPE_INSTANT:
schema_entry->value->type = STATS_TYPE_INSTANT;
break;
case KVM_STATS_TYPE_PEAK:
schema_entry->value->type = STATS_TYPE_PEAK;
break;
case KVM_STATS_TYPE_LINEAR_HIST:
schema_entry->value->type = STATS_TYPE_LINEAR_HISTOGRAM;
schema_entry->value->bucket_size = pdesc->bucket_size;
schema_entry->value->has_bucket_size = true;
break;
case KVM_STATS_TYPE_LOG_HIST:
schema_entry->value->type = STATS_TYPE_LOG2_HISTOGRAM;
break;
default:
goto exit;
}
switch (pdesc->flags & KVM_STATS_UNIT_MASK) {
case KVM_STATS_UNIT_NONE:
break;
case KVM_STATS_UNIT_BYTES:
schema_entry->value->has_unit = true;
schema_entry->value->unit = STATS_UNIT_BYTES;
break;
case KVM_STATS_UNIT_CYCLES:
schema_entry->value->has_unit = true;
schema_entry->value->unit = STATS_UNIT_CYCLES;
break;
case KVM_STATS_UNIT_SECONDS:
schema_entry->value->has_unit = true;
schema_entry->value->unit = STATS_UNIT_SECONDS;
break;
default:
goto exit;
}
schema_entry->value->exponent = pdesc->exponent;
if (pdesc->exponent) {
switch (pdesc->flags & KVM_STATS_BASE_MASK) {
case KVM_STATS_BASE_POW10:
schema_entry->value->has_base = true;
schema_entry->value->base = 10;
break;
case KVM_STATS_BASE_POW2:
schema_entry->value->has_base = true;
schema_entry->value->base = 2;
break;
default:
goto exit;
}
}
schema_entry->value->name = g_strdup(pdesc->name);
schema_entry->next = list;
return schema_entry;
exit:
g_free(schema_entry->value);
g_free(schema_entry);
return list;
}
/* Cached stats descriptors */
typedef struct StatsDescriptors {
const char *ident; /* cache key, currently the StatsTarget */
struct kvm_stats_desc *kvm_stats_desc;
struct kvm_stats_header *kvm_stats_header;
QTAILQ_ENTRY(StatsDescriptors) next;
} StatsDescriptors;
static QTAILQ_HEAD(, StatsDescriptors) stats_descriptors =
QTAILQ_HEAD_INITIALIZER(stats_descriptors);
/*
* Return the descriptors for 'target', that either have already been read
* or are retrieved from 'stats_fd'.
*/
static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd,
Error **errp)
{
StatsDescriptors *descriptors;
const char *ident;
struct kvm_stats_desc *kvm_stats_desc;
struct kvm_stats_header *kvm_stats_header;
size_t size_desc;
ssize_t ret;
ident = StatsTarget_str(target);
QTAILQ_FOREACH(descriptors, &stats_descriptors, next) {
if (g_str_equal(descriptors->ident, ident)) {
return descriptors;
}
}
descriptors = g_new0(StatsDescriptors, 1);
/* Read stats header */
kvm_stats_header = g_malloc(sizeof(*kvm_stats_header));
ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header));
if (ret != sizeof(*kvm_stats_header)) {
error_setg(errp, "KVM stats: failed to read stats header: "
"expected %zu actual %zu",
sizeof(*kvm_stats_header), ret);
return NULL;
}
size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size;
/* Read stats descriptors */
kvm_stats_desc = g_malloc0_n(kvm_stats_header->num_desc, size_desc);
ret = pread(stats_fd, kvm_stats_desc,
size_desc * kvm_stats_header->num_desc,
kvm_stats_header->desc_offset);
if (ret != size_desc * kvm_stats_header->num_desc) {
error_setg(errp, "KVM stats: failed to read stats descriptors: "
"expected %zu actual %zu",
size_desc * kvm_stats_header->num_desc, ret);
g_free(descriptors);
g_free(kvm_stats_desc);
return NULL;
}
descriptors->kvm_stats_header = kvm_stats_header;
descriptors->kvm_stats_desc = kvm_stats_desc;
descriptors->ident = ident;
QTAILQ_INSERT_TAIL(&stats_descriptors, descriptors, next);
return descriptors;
}
static void query_stats(StatsResultList **result, StatsTarget target,
strList *names, int stats_fd, Error **errp)
{
struct kvm_stats_desc *kvm_stats_desc;
struct kvm_stats_header *kvm_stats_header;
StatsDescriptors *descriptors;
g_autofree uint64_t *stats_data = NULL;
struct kvm_stats_desc *pdesc;
StatsList *stats_list = NULL;
size_t size_desc, size_data = 0;
ssize_t ret;
int i;
descriptors = find_stats_descriptors(target, stats_fd, errp);
if (!descriptors) {
return;
}
kvm_stats_header = descriptors->kvm_stats_header;
kvm_stats_desc = descriptors->kvm_stats_desc;
size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size;
/* Tally the total data size; read schema data */
for (i = 0; i < kvm_stats_header->num_desc; ++i) {
pdesc = (void *)kvm_stats_desc + i * size_desc;
size_data += pdesc->size * sizeof(*stats_data);
}
stats_data = g_malloc0(size_data);
ret = pread(stats_fd, stats_data, size_data, kvm_stats_header->data_offset);
if (ret != size_data) {
error_setg(errp, "KVM stats: failed to read data: "
"expected %zu actual %zu", size_data, ret);
return;
}
for (i = 0; i < kvm_stats_header->num_desc; ++i) {
uint64_t *stats;
pdesc = (void *)kvm_stats_desc + i * size_desc;
/* Add entry to the list */
stats = (void *)stats_data + pdesc->offset;
if (!apply_str_list_filter(pdesc->name, names)) {
continue;
}
stats_list = add_kvmstat_entry(pdesc, stats, stats_list, errp);
}
if (!stats_list) {
return;
}
switch (target) {
case STATS_TARGET_VM:
add_stats_entry(result, STATS_PROVIDER_KVM, NULL, stats_list);
break;
case STATS_TARGET_VCPU:
add_stats_entry(result, STATS_PROVIDER_KVM,
current_cpu->parent_obj.canonical_path,
stats_list);
break;
default:
break;
}
}
static void query_stats_schema(StatsSchemaList **result, StatsTarget target,
int stats_fd, Error **errp)
{
struct kvm_stats_desc *kvm_stats_desc;
struct kvm_stats_header *kvm_stats_header;
StatsDescriptors *descriptors;
struct kvm_stats_desc *pdesc;
StatsSchemaValueList *stats_list = NULL;
size_t size_desc;
int i;
descriptors = find_stats_descriptors(target, stats_fd, errp);
if (!descriptors) {
return;
}
kvm_stats_header = descriptors->kvm_stats_header;
kvm_stats_desc = descriptors->kvm_stats_desc;
size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size;
/* Tally the total data size; read schema data */
for (i = 0; i < kvm_stats_header->num_desc; ++i) {
pdesc = (void *)kvm_stats_desc + i * size_desc;
stats_list = add_kvmschema_entry(pdesc, stats_list, errp);
}
add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list);
}
static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data)
{
StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr;
int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
Error *local_err = NULL;
if (stats_fd == -1) {
error_setg_errno(&local_err, errno, "KVM stats: ioctl failed");
error_propagate(kvm_stats_args->errp, local_err);
return;
}
query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU,
kvm_stats_args->names, stats_fd, kvm_stats_args->errp);
close(stats_fd);
}
static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data)
{
StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr;
int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL);
Error *local_err = NULL;
if (stats_fd == -1) {
error_setg_errno(&local_err, errno, "KVM stats: ioctl failed");
error_propagate(kvm_stats_args->errp, local_err);
return;
}
query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd,
kvm_stats_args->errp);
close(stats_fd);
}
static void query_stats_cb(StatsResultList **result, StatsTarget target,
strList *names, strList *targets, Error **errp)
{
KVMState *s = kvm_state;
CPUState *cpu;
int stats_fd;
switch (target) {
case STATS_TARGET_VM:
{
stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL);
if (stats_fd == -1) {
error_setg_errno(errp, errno, "KVM stats: ioctl failed");
return;
}
query_stats(result, target, names, stats_fd, errp);
close(stats_fd);
break;
}
case STATS_TARGET_VCPU:
{
StatsArgs stats_args;
stats_args.result.stats = result;
stats_args.names = names;
stats_args.errp = errp;
CPU_FOREACH(cpu) {
if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) {
continue;
}
run_on_cpu(cpu, query_stats_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args));
}
break;
}
default:
break;
}
}
void query_stats_schemas_cb(StatsSchemaList **result, Error **errp)
{
StatsArgs stats_args;
KVMState *s = kvm_state;
int stats_fd;
stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL);
if (stats_fd == -1) {
error_setg_errno(errp, errno, "KVM stats: ioctl failed");
return;
}
query_stats_schema(result, STATS_TARGET_VM, stats_fd, errp);
close(stats_fd);
stats_args.result.schema = result;
stats_args.errp = errp;
run_on_cpu(first_cpu, query_stats_schema_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args));
}

22
configure vendored
View File

@ -1351,13 +1351,6 @@ static THREAD int tls_var;
int main(void) { return tls_var; }
EOF
# Check we support -fno-pie and -no-pie first; we will need the former for
# building ROMs, and both for everything if --disable-pie is passed.
if compile_prog "-Werror -fno-pie" "-no-pie"; then
CFLAGS_NOPIE="-fno-pie"
LDFLAGS_NOPIE="-no-pie"
fi
if test "$static" = "yes"; then
if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
@ -1370,8 +1363,10 @@ if test "$static" = "yes"; then
pie="no"
fi
elif test "$pie" = "no"; then
CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS"
if compile_prog "-Werror -fno-pie" "-no-pie"; then
CONFIGURE_CFLAGS="-fno-pie $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="-no-pie $CONFIGURE_LDFLAGS"
fi
elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS"
@ -2257,7 +2252,7 @@ if test -n "$target_cc" &&
fi
done
if test -n "$ld_i386_emulation"; then
roms="optionrom"
roms="pc-bios/optionrom"
config_mak=pc-bios/optionrom/config.mak
echo "# Automatically generated by configure - do not modify" > $config_mak
echo "TOPSRC_DIR=$source_path" >> $config_mak
@ -2268,7 +2263,7 @@ fi
probe_target_compilers ppc ppc64
if test -n "$target_cc" && test "$softmmu" = yes; then
roms="$roms vof"
roms="$roms pc-bios/vof"
config_mak=pc-bios/vof/config.mak
echo "# Automatically generated by configure - do not modify" > $config_mak
echo "SRC_DIR=$source_path/pc-bios/vof" >> $config_mak
@ -2287,7 +2282,7 @@ if test -n "$target_cc" && test "$softmmu" = yes; then
echo "WARNING: Your compiler does not support the z900!"
echo " The s390-ccw bios will only work with guest CPUs >= z10."
fi
roms="$roms s390-ccw"
roms="$roms pc-bios/s390-ccw"
config_mak=pc-bios/s390-ccw/config-host.mak
echo "# Automatically generated by configure - do not modify" > $config_mak
echo "SRC_PATH=$source_path/pc-bios/s390-ccw" >> $config_mak
@ -2739,13 +2734,12 @@ preserve_env CC
preserve_env CFLAGS
preserve_env CXX
preserve_env CXXFLAGS
preserve_env INSTALL
preserve_env LD
preserve_env LDFLAGS
preserve_env LD_LIBRARY_PATH
preserve_env LIBTOOL
preserve_env MAKE
preserve_env NM
preserve_env OBJCFLAGS
preserve_env OBJCOPY
preserve_env PATH
preserve_env PKG_CONFIG

View File

@ -894,3 +894,17 @@ SRST
``info via``
Show guest mos6522 VIA devices.
ERST
{
.name = "stats",
.args_type = "target:s,names:s?,provider:s?",
.params = "target [names] [provider]",
.help = "show statistics for the given target (vm or vcpu); optionally filter by"
"name (comma-separated list, or * for all) and provider",
.cmd = hmp_info_stats,
},
SRST
``stats``
Show runtime-collected statistics
ERST

View File

@ -78,6 +78,7 @@ ERST
.help = "resize a block image",
.cmd = hmp_block_resize,
.coroutine = true,
.flags = "p",
},
SRST
@ -94,6 +95,7 @@ ERST
.params = "device [speed [base]]",
.help = "copy data from a backing file into a block device",
.cmd = hmp_block_stream,
.flags = "p",
},
SRST
@ -107,6 +109,7 @@ ERST
.params = "device speed",
.help = "set maximum speed for a background block operation",
.cmd = hmp_block_job_set_speed,
.flags = "p",
},
SRST
@ -122,6 +125,7 @@ ERST
"\n\t\t\t if you want to abort the operation immediately"
"\n\t\t\t instead of keep running until data is in sync)",
.cmd = hmp_block_job_cancel,
.flags = "p",
},
SRST
@ -135,6 +139,7 @@ ERST
.params = "device",
.help = "stop an active background block operation",
.cmd = hmp_block_job_complete,
.flags = "p",
},
SRST
@ -149,6 +154,7 @@ ERST
.params = "device",
.help = "pause an active background block operation",
.cmd = hmp_block_job_pause,
.flags = "p",
},
SRST
@ -162,6 +168,7 @@ ERST
.params = "device",
.help = "resume a paused background block operation",
.cmd = hmp_block_job_resume,
.flags = "p",
},
SRST
@ -1406,6 +1413,7 @@ ERST
.params = "nbd_server_start [-a] [-w] host:port",
.help = "serve block devices on the given host and port",
.cmd = hmp_nbd_server_start,
.flags = "p",
},
SRST
``nbd_server_start`` *host*:*port*
@ -1421,6 +1429,7 @@ ERST
.params = "nbd_server_add [-w] device [name]",
.help = "export a block device via NBD",
.cmd = hmp_nbd_server_add,
.flags = "p",
},
SRST
``nbd_server_add`` *device* [ *name* ]
@ -1436,6 +1445,7 @@ ERST
.params = "nbd_server_remove [-f] name",
.help = "remove an export previously exposed via NBD",
.cmd = hmp_nbd_server_remove,
.flags = "p",
},
SRST
``nbd_server_remove [-f]`` *name*
@ -1452,6 +1462,7 @@ ERST
.params = "nbd_server_stop",
.help = "stop serving block devices using the NBD protocol",
.cmd = hmp_nbd_server_stop,
.flags = "p",
},
SRST
``nbd_server_stop``
@ -1481,6 +1492,7 @@ ERST
.params = "getfd name",
.help = "receive a file descriptor via SCM rights and assign it a name",
.cmd = hmp_getfd,
.flags = "p",
},
SRST
@ -1496,6 +1508,7 @@ ERST
.params = "closefd name",
.help = "close a file descriptor previously passed via SCM rights",
.cmd = hmp_closefd,
.flags = "p",
},
SRST
@ -1511,6 +1524,7 @@ ERST
.params = "device bps bps_rd bps_wr iops iops_rd iops_wr",
.help = "change I/O throttle limits for a block drive",
.cmd = hmp_block_set_io_throttle,
.flags = "p",
},
SRST

View File

@ -379,7 +379,8 @@ static void mch_update_smram(MCHPCIState *mch)
memory_region_set_enabled(&mch->high_smram, false);
}
if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) {
if ((pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) &&
(pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME)) {
switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] &
MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) {
case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB:

View File

@ -249,12 +249,11 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
return 0;
}
static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev)
{
CcwDevice *ccw_dev = CCW_DEVICE(dev);
virtio_ccw_stop_ioeventfd(dev);
virtio_reset(vdev);
virtio_bus_reset(&dev->bus);
if (dev->indicators) {
release_indicator(&dev->routes.adapter, dev->indicators);
dev->indicators = NULL;
@ -359,7 +358,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1);
break;
case CCW_CMD_VDEV_RESET:
virtio_ccw_reset_virtio(dev, vdev);
virtio_ccw_reset_virtio(dev);
ret = 0;
break;
case CCW_CMD_READ_FEAT:
@ -536,7 +535,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
}
if (virtio_set_status(vdev, status) == 0) {
if (vdev->status == 0) {
virtio_ccw_reset_virtio(dev, vdev);
virtio_ccw_reset_virtio(dev);
}
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
virtio_ccw_start_ioeventfd(dev);
@ -921,10 +920,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
static void virtio_ccw_reset(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
virtio_ccw_reset_virtio(dev, vdev);
virtio_ccw_reset_virtio(dev);
if (vdc->parent_reset) {
vdc->parent_reset(d);
}

View File

@ -104,6 +104,7 @@ void virtio_bus_reset(VirtioBusState *bus)
VirtIODevice *vdev = virtio_bus_get_device(bus);
DPRINTF("%s: reset device.\n", BUS(bus)->name);
virtio_bus_stop_ioeventfd(bus);
if (vdev != NULL) {
virtio_reset(vdev);
}

View File

@ -72,12 +72,12 @@ static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy)
{
int i;
if (proxy->legacy) {
return;
}
virtio_bus_reset(&proxy->bus);
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
proxy->vqs[i].enabled = 0;
if (!proxy->legacy) {
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
proxy->vqs[i].enabled = 0;
}
}
}
@ -376,7 +376,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
return;
}
if (value == 0) {
virtio_reset(vdev);
virtio_mmio_soft_reset(proxy);
} else {
virtio_queue_set_addr(vdev, vdev->queue_sel,
value << proxy->guest_page_shift);
@ -432,7 +432,6 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
}
if (vdev->status == 0) {
virtio_reset(vdev);
virtio_mmio_soft_reset(proxy);
}
break;
@ -627,8 +626,8 @@ static void virtio_mmio_reset(DeviceState *d)
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
int i;
virtio_mmio_stop_ioeventfd(proxy);
virtio_bus_reset(&proxy->bus);
virtio_mmio_soft_reset(proxy);
proxy->host_features_sel = 0;
proxy->guest_features_sel = 0;
proxy->guest_page_shift = 0;
@ -637,7 +636,6 @@ static void virtio_mmio_reset(DeviceState *d)
proxy->guest_features[0] = proxy->guest_features[1] = 0;
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
proxy->vqs[i].enabled = 0;
proxy->vqs[i].num = 0;
proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0;
proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0;

View File

@ -1945,7 +1945,6 @@ static void virtio_pci_reset(DeviceState *qdev)
PCIDevice *dev = PCI_DEVICE(qdev);
int i;
virtio_pci_stop_ioeventfd(proxy);
virtio_bus_reset(bus);
msix_unuse_all_vectors(&proxy->pci_dev);

View File

@ -133,5 +133,6 @@ void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict);
void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict);
void hmp_human_readable_text_helper(Monitor *mon,
HumanReadableText *(*qmp_handler)(Error **));
void hmp_info_stats(Monitor *mon, const QDict *qdict);
#endif

45
include/monitor/stats.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#ifndef STATS_H
#define STATS_H
#include "qapi/qapi-types-stats.h"
typedef void StatRetrieveFunc(StatsResultList **result, StatsTarget target,
strList *names, strList *targets, Error **errp);
typedef void SchemaRetrieveFunc(StatsSchemaList **result, Error **errp);
/*
* Register callbacks for the QMP query-stats command.
*
* @provider: stats provider checked against QMP command arguments
* @stats_fn: routine to query stats:
* @schema_fn: routine to query stat schemas:
*/
void add_stats_callbacks(StatsProvider provider,
StatRetrieveFunc *stats_fn,
SchemaRetrieveFunc *schemas_fn);
/*
* Helper routines for adding stats entries to the results lists.
*/
void add_stats_entry(StatsResultList **, StatsProvider, const char *id,
StatsList *stats_list);
void add_stats_schema(StatsSchemaList **, StatsProvider, StatsTarget,
StatsSchemaValueList *);
/*
* True if a string matches the filter passed to the stats_fn callabck,
* false otherwise.
*
* Note that an empty list means no filtering, i.e. all strings will
* return true.
*/
bool apply_str_list_filter(const char *string, strList *list);
#endif /* STATS_H */

View File

@ -1,6 +1,24 @@
#ifndef QEMU_CUTILS_H
#define QEMU_CUTILS_H
/*
* si_prefix:
* @exp10: exponent of 10, a multiple of 3 between -18 and 18 inclusive.
*
* Return a SI prefix (n, u, m, K, M, etc.) corresponding
* to the given exponent of 10.
*/
const char *si_prefix(unsigned int exp10);
/*
* iec_binary_prefix:
* @exp2: exponent of 2, a multiple of 10 between 0 and 60 inclusive.
*
* Return an IEC binary prefix (Ki, Mi, etc.) corresponding
* to the given exponent of 2.
*/
const char *iec_binary_prefix(unsigned int exp2);
/**
* pstrcpy:
* @buf: buffer to copy string into

View File

@ -209,9 +209,13 @@ if get_option('fuzzing')
configure_file(output: 'instrumentation-filter',
input: 'scripts/oss-fuzz/instrumentation-filter-template',
copy: true)
add_global_arguments(
cc.get_supported_arguments('-fsanitize-coverage-allowlist=instrumentation-filter'),
native: false, language: ['c', 'cpp', 'objc'])
if cc.compiles('int main () { return 0; }',
name: '-fsanitize-coverage-allowlist=/dev/null',
args: ['-fsanitize-coverage-allowlist=/dev/null'] )
add_global_arguments('-fsanitize-coverage-allowlist=instrumentation-filter',
native: false, language: ['c', 'cpp', 'objc'])
endif
if get_option('fuzzing_engine') == ''
# Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the
@ -3767,21 +3771,24 @@ endif
summary_info += {'strip binaries': get_option('strip')}
summary_info += {'sparse': sparse}
summary_info += {'mingw32 support': targetos == 'windows'}
summary(summary_info, bool_yn: true, section: 'Compilation')
# snarf the cross-compilation information for tests
summary_info = {}
have_cross = false
foreach target: target_dirs
tcg_mak = meson.current_build_dir() / 'tests/tcg' / 'config-' + target + '.mak'
if fs.exists(tcg_mak)
config_cross_tcg = keyval.load(tcg_mak)
target = config_cross_tcg['TARGET_NAME']
compiler = ''
if 'CC' in config_cross_tcg
summary_info += {target + ' tests': config_cross_tcg['CC']}
summary_info += {config_cross_tcg['TARGET_NAME']: config_cross_tcg['CC']}
have_cross = true
endif
endif
endif
endforeach
summary(summary_info, bool_yn: true, section: 'Compilation')
if have_cross
summary(summary_info, bool_yn: true, section: 'Cross compilers')
endif
# Targets and accelerators
summary_info = {}

View File

@ -40,6 +40,7 @@
#include "qapi/qapi-commands-pci.h"
#include "qapi/qapi-commands-rocker.h"
#include "qapi/qapi-commands-run-state.h"
#include "qapi/qapi-commands-stats.h"
#include "qapi/qapi-commands-tpm.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/qapi-visit-net.h"
@ -52,6 +53,7 @@
#include "ui/console.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "hw/core/cpu.h"
#include "hw/intc/intc.h"
#include "migration/snapshot.h"
#include "migration/misc.h"
@ -2239,3 +2241,233 @@ void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict)
}
hmp_handle_error(mon, err);
}
static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value)
{
const char *unit = NULL;
monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type),
value->has_unit || value->exponent ? ", " : "");
if (value->has_unit) {
if (value->unit == STATS_UNIT_SECONDS) {
unit = "s";
} else if (value->unit == STATS_UNIT_BYTES) {
unit = "B";
}
}
if (unit && value->base == 10 &&
value->exponent >= -18 && value->exponent <= 18 &&
value->exponent % 3 == 0) {
monitor_printf(mon, "%s", si_prefix(value->exponent));
} else if (unit && value->base == 2 &&
value->exponent >= 0 && value->exponent <= 60 &&
value->exponent % 10 == 0) {
monitor_printf(mon, "%s", iec_binary_prefix(value->exponent));
} else if (value->exponent) {
/* Use exponential notation and write the unit's English name */
monitor_printf(mon, "* %d^%d%s",
value->base, value->exponent,
value->has_unit ? " " : "");
unit = NULL;
}
if (value->has_unit) {
monitor_printf(mon, "%s", unit ? unit : StatsUnit_str(value->unit));
}
/* Print bucket size for linear histograms */
if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) {
monitor_printf(mon, ", bucket size=%d", value->bucket_size);
}
monitor_printf(mon, ")");
}
static StatsSchemaValueList *find_schema_value_list(
StatsSchemaList *list, StatsProvider provider,
StatsTarget target)
{
StatsSchemaList *node;
for (node = list; node; node = node->next) {
if (node->value->provider == provider &&
node->value->target == target) {
return node->value->stats;
}
}
return NULL;
}
static void print_stats_results(Monitor *mon, StatsTarget target,
bool show_provider,
StatsResult *result,
StatsSchemaList *schema)
{
/* Find provider schema */
StatsSchemaValueList *schema_value_list =
find_schema_value_list(schema, result->provider, target);
StatsList *stats_list;
if (!schema_value_list) {
monitor_printf(mon, "failed to find schema list for %s\n",
StatsProvider_str(result->provider));
return;
}
if (show_provider) {
monitor_printf(mon, "provider: %s\n",
StatsProvider_str(result->provider));
}
for (stats_list = result->stats; stats_list;
stats_list = stats_list->next,
schema_value_list = schema_value_list->next) {
Stats *stats = stats_list->value;
StatsValue *stats_value = stats->value;
StatsSchemaValue *schema_value = schema_value_list->value;
/* Find schema entry */
while (!g_str_equal(stats->name, schema_value->name)) {
if (!schema_value_list->next) {
monitor_printf(mon, "failed to find schema entry for %s\n",
stats->name);
return;
}
schema_value_list = schema_value_list->next;
schema_value = schema_value_list->value;
}
print_stats_schema_value(mon, schema_value);
if (stats_value->type == QTYPE_QNUM) {
monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar);
} else if (stats_value->type == QTYPE_QLIST) {
uint64List *list;
int i;
monitor_printf(mon, ": ");
for (list = stats_value->u.list, i = 1;
list;
list = list->next, i++) {
monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value);
}
monitor_printf(mon, "\n");
}
}
}
/* Create the StatsFilter that is needed for an "info stats" invocation. */
static StatsFilter *stats_filter(StatsTarget target, const char *names,
int cpu_index, StatsProvider provider)
{
StatsFilter *filter = g_malloc0(sizeof(*filter));
StatsProvider provider_idx;
StatsRequestList *request_list = NULL;
filter->target = target;
switch (target) {
case STATS_TARGET_VM:
break;
case STATS_TARGET_VCPU:
{
strList *vcpu_list = NULL;
CPUState *cpu = qemu_get_cpu(cpu_index);
char *canonical_path = object_get_canonical_path(OBJECT(cpu));
QAPI_LIST_PREPEND(vcpu_list, canonical_path);
filter->u.vcpu.has_vcpus = true;
filter->u.vcpu.vcpus = vcpu_list;
break;
}
default:
break;
}
if (!names && provider == STATS_PROVIDER__MAX) {
return filter;
}
/*
* "info stats" can only query either one or all the providers. Querying
* by name, but not by provider, requires the creation of one filter per
* provider.
*/
for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) {
if (provider == STATS_PROVIDER__MAX || provider == provider_idx) {
StatsRequest *request = g_new0(StatsRequest, 1);
request->provider = provider_idx;
if (names && !g_str_equal(names, "*")) {
request->has_names = true;
request->names = strList_from_comma_list(names);
}
QAPI_LIST_PREPEND(request_list, request);
}
}
filter->has_providers = true;
filter->providers = request_list;
return filter;
}
void hmp_info_stats(Monitor *mon, const QDict *qdict)
{
const char *target_str = qdict_get_str(qdict, "target");
const char *provider_str = qdict_get_try_str(qdict, "provider");
const char *names = qdict_get_try_str(qdict, "names");
StatsProvider provider = STATS_PROVIDER__MAX;
StatsTarget target;
Error *err = NULL;
g_autoptr(StatsSchemaList) schema = NULL;
g_autoptr(StatsResultList) stats = NULL;
g_autoptr(StatsFilter) filter = NULL;
StatsResultList *entry;
target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err);
if (err) {
monitor_printf(mon, "invalid stats target %s\n", target_str);
goto exit_no_print;
}
if (provider_str) {
provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err);
if (err) {
monitor_printf(mon, "invalid stats provider %s\n", provider_str);
goto exit_no_print;
}
}
schema = qmp_query_stats_schemas(provider_str ? true : false,
provider, &err);
if (err) {
goto exit;
}
switch (target) {
case STATS_TARGET_VM:
filter = stats_filter(target, names, -1, provider);
break;
case STATS_TARGET_VCPU: {}
int cpu_index = monitor_get_cpu_index(mon);
filter = stats_filter(target, names, cpu_index, provider);
break;
default:
abort();
}
stats = qmp_query_stats(filter, &err);
if (err) {
goto exit;
}
for (entry = stats; entry; entry = entry->next) {
print_stats_results(mon, target, provider_str == NULL, entry->value, schema);
}
exit:
if (err) {
monitor_printf(mon, "%s\n", error_get_pretty(err));
}
exit_no_print:
error_free(err);
}

View File

@ -35,6 +35,7 @@
#include "qapi/qapi-commands-control.h"
#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/qapi-commands-stats.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/type-helpers.h"
#include "qapi/qmp/qerror.h"
@ -43,6 +44,7 @@
#include "hw/acpi/acpi_dev_interface.h"
#include "hw/intc/intc.h"
#include "hw/rdma/rdma.h"
#include "monitor/stats.h"
NameInfo *qmp_query_name(Error **errp)
{
@ -441,3 +443,156 @@ HumanReadableText *qmp_x_query_irq(Error **errp)
return human_readable_text_from_str(buf);
}
typedef struct StatsCallbacks {
StatsProvider provider;
StatRetrieveFunc *stats_cb;
SchemaRetrieveFunc *schemas_cb;
QTAILQ_ENTRY(StatsCallbacks) next;
} StatsCallbacks;
static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks =
QTAILQ_HEAD_INITIALIZER(stats_callbacks);
void add_stats_callbacks(StatsProvider provider,
StatRetrieveFunc *stats_fn,
SchemaRetrieveFunc *schemas_fn)
{
StatsCallbacks *entry = g_new(StatsCallbacks, 1);
entry->provider = provider;
entry->stats_cb = stats_fn;
entry->schemas_cb = schemas_fn;
QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next);
}
static bool invoke_stats_cb(StatsCallbacks *entry,
StatsResultList **stats_results,
StatsFilter *filter, StatsRequest *request,
Error **errp)
{
strList *targets = NULL;
strList *names = NULL;
ERRP_GUARD();
if (request) {
if (request->provider != entry->provider) {
return true;
}
if (request->has_names && !request->names) {
return true;
}
names = request->has_names ? request->names : NULL;
}
switch (filter->target) {
case STATS_TARGET_VM:
break;
case STATS_TARGET_VCPU:
if (filter->u.vcpu.has_vcpus) {
if (!filter->u.vcpu.vcpus) {
/* No targets allowed? Return no statistics. */
return true;
}
targets = filter->u.vcpu.vcpus;
}
break;
default:
abort();
}
entry->stats_cb(stats_results, filter->target, names, targets, errp);
if (*errp) {
qapi_free_StatsResultList(*stats_results);
*stats_results = NULL;
return false;
}
return true;
}
StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp)
{
StatsResultList *stats_results = NULL;
StatsCallbacks *entry;
StatsRequestList *request;
QTAILQ_FOREACH(entry, &stats_callbacks, next) {
if (filter->has_providers) {
for (request = filter->providers; request; request = request->next) {
if (!invoke_stats_cb(entry, &stats_results, filter,
request->value, errp)) {
break;
}
}
} else {
if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) {
break;
}
}
}
return stats_results;
}
StatsSchemaList *qmp_query_stats_schemas(bool has_provider,
StatsProvider provider,
Error **errp)
{
StatsSchemaList *stats_results = NULL;
StatsCallbacks *entry;
ERRP_GUARD();
QTAILQ_FOREACH(entry, &stats_callbacks, next) {
if (!has_provider || provider == entry->provider) {
entry->schemas_cb(&stats_results, errp);
if (*errp) {
qapi_free_StatsSchemaList(stats_results);
return NULL;
}
}
}
return stats_results;
}
void add_stats_entry(StatsResultList **stats_results, StatsProvider provider,
const char *qom_path, StatsList *stats_list)
{
StatsResult *entry = g_new0(StatsResult, 1);
entry->provider = provider;
if (qom_path) {
entry->has_qom_path = true;
entry->qom_path = g_strdup(qom_path);
}
entry->stats = stats_list;
QAPI_LIST_PREPEND(*stats_results, entry);
}
void add_stats_schema(StatsSchemaList **schema_results,
StatsProvider provider, StatsTarget target,
StatsSchemaValueList *stats_list)
{
StatsSchema *entry = g_new0(StatsSchema, 1);
entry->provider = provider;
entry->target = target;
entry->stats = stats_list;
QAPI_LIST_PREPEND(*schema_results, entry);
}
bool apply_str_list_filter(const char *string, strList *list)
{
strList *str_list = NULL;
if (!list) {
return true;
}
for (str_list = list; str_list; str_list = str_list->next) {
if (g_str_equal(string, str_list->value)) {
return true;
}
}
return false;
}

View File

@ -737,7 +737,8 @@
# }
#
##
{ 'command': 'query-block', 'returns': ['BlockInfo'] }
{ 'command': 'query-block', 'returns': ['BlockInfo'],
'allow-preconfig': true }
##
# @BlockDeviceTimedStats:
@ -1113,7 +1114,8 @@
##
{ 'command': 'query-blockstats',
'data': { '*query-nodes': 'bool' },
'returns': ['BlockStats'] }
'returns': ['BlockStats'],
'allow-preconfig': true }
##
# @BlockdevOnError:
@ -1262,7 +1264,8 @@
#
# Since: 1.1
##
{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] }
{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'],
'allow-preconfig': true }
##
# @block_resize:
@ -1293,7 +1296,8 @@
'data': { '*device': 'str',
'*node-name': 'str',
'size': 'int' },
'coroutine': true }
'coroutine': true,
'allow-preconfig': true }
##
# @NewImageMode:
@ -1509,7 +1513,8 @@
#
##
{ 'command': 'blockdev-snapshot-sync',
'data': 'BlockdevSnapshotSync' }
'data': 'BlockdevSnapshotSync',
'allow-preconfig': true }
##
# @blockdev-snapshot:
@ -1550,7 +1555,8 @@
##
{ 'command': 'blockdev-snapshot',
'data': 'BlockdevSnapshot',
'features': [ 'allow-write-only-overlay' ] }
'features': [ 'allow-write-only-overlay' ],
'allow-preconfig': true }
##
# @change-backing-file:
@ -1582,7 +1588,8 @@
##
{ 'command': 'change-backing-file',
'data': { 'device': 'str', 'image-node-name': 'str',
'backing-file': 'str' } }
'backing-file': 'str' },
'allow-preconfig': true }
##
# @block-commit:
@ -1692,7 +1699,8 @@
'*backing-file': 'str', '*speed': 'int',
'*on-error': 'BlockdevOnError',
'*filter-node-name': 'str',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
'allow-preconfig': true }
##
# @drive-backup:
@ -1721,7 +1729,8 @@
#
##
{ 'command': 'drive-backup', 'boxed': true,
'data': 'DriveBackup', 'features': ['deprecated'] }
'data': 'DriveBackup', 'features': ['deprecated'],
'allow-preconfig': true }
##
# @blockdev-backup:
@ -1747,7 +1756,8 @@
#
##
{ 'command': 'blockdev-backup', 'boxed': true,
'data': 'BlockdevBackup' }
'data': 'BlockdevBackup',
'allow-preconfig': true }
##
# @query-named-block-nodes:
@ -1813,7 +1823,8 @@
##
{ 'command': 'query-named-block-nodes',
'returns': [ 'BlockDeviceInfo' ],
'data': { '*flat': 'bool' } }
'data': { '*flat': 'bool' },
'allow-preconfig': true }
##
# @XDbgBlockGraphNodeType:
@ -1922,7 +1933,8 @@
# Since: 4.0
##
{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph',
'features': [ 'unstable' ] }
'features': [ 'unstable' ],
'allow-preconfig': true }
##
# @drive-mirror:
@ -1950,7 +1962,8 @@
#
##
{ 'command': 'drive-mirror', 'boxed': true,
'data': 'DriveMirror' }
'data': 'DriveMirror',
'allow-preconfig': true }
##
# @DriveMirror:
@ -2123,7 +2136,8 @@
#
##
{ 'command': 'block-dirty-bitmap-add',
'data': 'BlockDirtyBitmapAdd' }
'data': 'BlockDirtyBitmapAdd',
'allow-preconfig': true }
##
# @block-dirty-bitmap-remove:
@ -2147,7 +2161,8 @@
#
##
{ 'command': 'block-dirty-bitmap-remove',
'data': 'BlockDirtyBitmap' }
'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
##
# @block-dirty-bitmap-clear:
@ -2170,7 +2185,8 @@
#
##
{ 'command': 'block-dirty-bitmap-clear',
'data': 'BlockDirtyBitmap' }
'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
##
# @block-dirty-bitmap-enable:
@ -2191,7 +2207,8 @@
#
##
{ 'command': 'block-dirty-bitmap-enable',
'data': 'BlockDirtyBitmap' }
'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
##
# @block-dirty-bitmap-disable:
@ -2212,7 +2229,8 @@
#
##
{ 'command': 'block-dirty-bitmap-disable',
'data': 'BlockDirtyBitmap' }
'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
##
# @block-dirty-bitmap-merge:
@ -2244,7 +2262,8 @@
#
##
{ 'command': 'block-dirty-bitmap-merge',
'data': 'BlockDirtyBitmapMerge' }
'data': 'BlockDirtyBitmapMerge',
'allow-preconfig': true }
##
# @BlockDirtyBitmapSha256:
@ -2275,7 +2294,8 @@
##
{ 'command': 'x-debug-block-dirty-bitmap-sha256',
'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256',
'features': [ 'unstable' ] }
'features': [ 'unstable' ],
'allow-preconfig': true }
##
# @blockdev-mirror:
@ -2361,7 +2381,8 @@
'*on-target-error': 'BlockdevOnError',
'*filter-node-name': 'str',
'*copy-mode': 'MirrorCopyMode',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
'allow-preconfig': true }
##
# @BlockIOThrottle:
@ -2663,7 +2684,8 @@
'*base-node': 'str', '*backing-file': 'str', '*bottom': 'str',
'*speed': 'int', '*on-error': 'BlockdevOnError',
'*filter-node-name': 'str',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
'allow-preconfig': true }
##
# @block-job-set-speed:
@ -2687,7 +2709,8 @@
# Since: 1.1
##
{ 'command': 'block-job-set-speed',
'data': { 'device': 'str', 'speed': 'int' } }
'data': { 'device': 'str', 'speed': 'int' },
'allow-preconfig': true }
##
# @block-job-cancel:
@ -2726,7 +2749,8 @@
#
# Since: 1.1
##
{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' } }
{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' },
'allow-preconfig': true }
##
# @block-job-pause:
@ -2750,7 +2774,8 @@
#
# Since: 1.3
##
{ 'command': 'block-job-pause', 'data': { 'device': 'str' } }
{ 'command': 'block-job-pause', 'data': { 'device': 'str' },
'allow-preconfig': true }
##
# @block-job-resume:
@ -2772,7 +2797,8 @@
#
# Since: 1.3
##
{ 'command': 'block-job-resume', 'data': { 'device': 'str' } }
{ 'command': 'block-job-resume', 'data': { 'device': 'str' },
'allow-preconfig': true }
##
# @block-job-complete:
@ -2800,7 +2826,8 @@
#
# Since: 1.3
##
{ 'command': 'block-job-complete', 'data': { 'device': 'str' } }
{ 'command': 'block-job-complete', 'data': { 'device': 'str' },
'allow-preconfig': true }
##
# @block-job-dismiss:
@ -2820,7 +2847,8 @@
#
# Since: 2.12
##
{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } }
{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' },
'allow-preconfig': true }
##
# @block-job-finalize:
@ -2838,7 +2866,8 @@
#
# Since: 2.12
##
{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } }
{ 'command': 'block-job-finalize', 'data': { 'id': 'str' },
'allow-preconfig': true }
##
# @BlockdevDiscardOptions:
@ -4354,7 +4383,8 @@
# <- { "return": {} }
#
##
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true,
'allow-preconfig': true }
##
# @blockdev-reopen:
@ -4398,7 +4428,8 @@
# Since: 6.1
##
{ 'command': 'blockdev-reopen',
'data': { 'options': ['BlockdevOptions'] } }
'data': { 'options': ['BlockdevOptions'] },
'allow-preconfig': true }
##
# @blockdev-del:
@ -4431,7 +4462,8 @@
# <- { "return": {} }
#
##
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } }
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' },
'allow-preconfig': true }
##
# @BlockdevCreateOptionsFile:
@ -4872,7 +4904,8 @@
##
{ 'command': 'blockdev-create',
'data': { 'job-id': 'str',
'options': 'BlockdevCreateOptions' } }
'options': 'BlockdevCreateOptions' },
'allow-preconfig': true }
##
# @BlockdevAmendOptionsLUKS:
@ -4944,7 +4977,8 @@
'node-name': 'str',
'options': 'BlockdevAmendOptions',
'*force': 'bool' },
'features': [ 'unstable' ] }
'features': [ 'unstable' ],
'allow-preconfig': true }
##
# @BlockErrorAction:
@ -5294,7 +5328,8 @@
#
##
{ 'command': 'block-set-write-threshold',
'data': { 'node-name': 'str', 'write-threshold': 'uint64' } }
'data': { 'node-name': 'str', 'write-threshold': 'uint64' },
'allow-preconfig': true }
##
# @x-blockdev-change:
@ -5355,7 +5390,8 @@
'data' : { 'parent': 'str',
'*child': 'str',
'*node': 'str' },
'features': [ 'unstable' ] }
'features': [ 'unstable' ],
'allow-preconfig': true }
##
# @x-blockdev-set-iothread:
@ -5397,7 +5433,8 @@
'data' : { 'node-name': 'str',
'iothread': 'StrOrNull',
'*force': 'bool' },
'features': [ 'unstable' ] }
'features': [ 'unstable' ],
'allow-preconfig': true }
##
# @QuorumOpType:
@ -5529,7 +5566,8 @@
#
##
{ 'command': 'blockdev-snapshot-internal-sync',
'data': 'BlockdevSnapshotInternal' }
'data': 'BlockdevSnapshotInternal',
'allow-preconfig': true }
##
# @blockdev-snapshot-delete-internal-sync:
@ -5576,4 +5614,5 @@
##
{ 'command': 'blockdev-snapshot-delete-internal-sync',
'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
'returns': 'SnapshotInfo' }
'returns': 'SnapshotInfo',
'allow-preconfig': true }

View File

@ -65,7 +65,8 @@
'data': { 'addr': 'SocketAddressLegacy',
'*tls-creds': 'str',
'*tls-authz': 'str',
'*max-connections': 'uint32' } }
'*max-connections': 'uint32' },
'allow-preconfig': true }
##
# @BlockExportOptionsNbdBase:
@ -215,7 +216,8 @@
# Since: 1.3
##
{ 'command': 'nbd-server-add',
'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'] }
'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'],
'allow-preconfig': true }
##
# @BlockExportRemoveMode:
@ -260,7 +262,8 @@
##
{ 'command': 'nbd-server-remove',
'data': {'name': 'str', '*mode': 'BlockExportRemoveMode'},
'features': ['deprecated'] }
'features': ['deprecated'],
'allow-preconfig': true }
##
# @nbd-server-stop:
@ -270,7 +273,8 @@
#
# Since: 1.3
##
{ 'command': 'nbd-server-stop' }
{ 'command': 'nbd-server-stop',
'allow-preconfig': true }
##
# @BlockExportType:
@ -342,7 +346,8 @@
# Since: 5.2
##
{ 'command': 'block-export-add',
'data': 'BlockExportOptions', 'boxed': true }
'data': 'BlockExportOptions', 'boxed': true,
'allow-preconfig': true }
##
# @block-export-del:
@ -362,7 +367,8 @@
# Since: 5.2
##
{ 'command': 'block-export-del',
'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' } }
'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' },
'allow-preconfig': true }
##
# @BLOCK_EXPORT_DELETED:
@ -406,4 +412,5 @@
#
# Since: 5.2
##
{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'] }
{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'],
'allow-preconfig': true }

View File

@ -496,7 +496,8 @@
# <- { "return": {} }
##
{ 'command': 'block_set_io_throttle', 'boxed': true,
'data': 'BlockIOThrottle' }
'data': 'BlockIOThrottle',
'allow-preconfig': true }
##
# @block-latency-histogram-set:
@ -572,4 +573,5 @@
'*boundaries': ['uint64'],
'*boundaries-read': ['uint64'],
'*boundaries-write': ['uint64'],
'*boundaries-flush': ['uint64'] } }
'*boundaries-flush': ['uint64'] },
'allow-preconfig': true }

View File

@ -46,6 +46,7 @@ qapi_all_modules = [
'replay',
'run-state',
'sockets',
'stats',
'trace',
'transaction',
'yank',

View File

@ -93,3 +93,4 @@
{ 'include': 'audio.json' }
{ 'include': 'acpi.json' }
{ 'include': 'pci.json' }
{ 'include': 'stats.json' }

249
qapi/stats.json Normal file
View File

@ -0,0 +1,249 @@
# -*- Mode: Python -*-
# vim: filetype=python
#
# Copyright (c) 2022 Oracle and/or its affiliates.
#
# This work is licensed under the terms of the GNU GPL, version 2 or later.
# See the COPYING file in the top-level directory.
#
# SPDX-License-Identifier: GPL-2.0-or-later
##
# = Statistics
##
##
# @StatsType:
#
# Enumeration of statistics types
#
# @cumulative: stat is cumulative; value can only increase.
# @instant: stat is instantaneous; value can increase or decrease.
# @peak: stat is the peak value; value can only increase.
# @linear-histogram: stat is a linear histogram.
# @log2-histogram: stat is a logarithmic histogram, with one bucket
# for each power of two.
#
# Since: 7.1
##
{ 'enum' : 'StatsType',
'data' : [ 'cumulative', 'instant', 'peak', 'linear-histogram',
'log2-histogram' ] }
##
# @StatsUnit:
#
# Enumeration of unit of measurement for statistics
#
# @bytes: stat reported in bytes.
# @seconds: stat reported in seconds.
# @cycles: stat reported in clock cycles.
#
# Since: 7.1
##
{ 'enum' : 'StatsUnit',
'data' : [ 'bytes', 'seconds', 'cycles' ] }
##
# @StatsProvider:
#
# Enumeration of statistics providers.
#
# Since: 7.1
##
{ 'enum': 'StatsProvider',
'data': [ 'kvm' ] }
##
# @StatsTarget:
#
# The kinds of objects on which one can request statistics.
#
# @vm: statistics that apply to the entire virtual machine or
# the entire QEMU process.
#
# @vcpu: statistics that apply to a single virtual CPU.
#
# Since: 7.1
##
{ 'enum': 'StatsTarget',
'data': [ 'vm', 'vcpu' ] }
##
# @StatsRequest:
#
# Indicates a set of statistics that should be returned by query-stats.
#
# @provider: provider for which to return statistics.
# @names: statistics to be returned (all if omitted).
#
# Since: 7.1
##
{ 'struct': 'StatsRequest',
'data': { 'provider': 'StatsProvider',
'*names': [ 'str' ] } }
##
# @StatsVCPUFilter:
#
# @vcpus: list of QOM paths for the desired vCPU objects.
#
# Since: 7.1
##
{ 'struct': 'StatsVCPUFilter',
'data': { '*vcpus': [ 'str' ] } }
##
# @StatsFilter:
#
# The arguments to the query-stats command; specifies a target for which to
# request statistics and optionally the required subset of information for
# that target:
# - which vCPUs to request statistics for
# - which providers to request statistics from
# - which named values to return within each provider
#
# Since: 7.1
##
{ 'union': 'StatsFilter',
'base': {
'target': 'StatsTarget',
'*providers': [ 'StatsRequest' ] },
'discriminator': 'target',
'data': { 'vcpu': 'StatsVCPUFilter' } }
##
# @StatsValue:
#
# @scalar: single unsigned 64-bit integers.
# @list: list of unsigned 64-bit integers (used for histograms).
#
# Since: 7.1
##
{ 'alternate': 'StatsValue',
'data': { 'scalar': 'uint64',
'list': [ 'uint64' ] } }
##
# @Stats:
#
# @name: name of stat.
# @value: stat value.
#
# Since: 7.1
##
{ 'struct': 'Stats',
'data': { 'name': 'str',
'value' : 'StatsValue' } }
##
# @StatsResult:
#
# @provider: provider for this set of statistics.
#
# @qom-path: Path to the object for which the statistics are returned,
# if the object is exposed in the QOM tree
#
# @stats: list of statistics.
#
# Since: 7.1
##
{ 'struct': 'StatsResult',
'data': { 'provider': 'StatsProvider',
'*qom-path': 'str',
'stats': [ 'Stats' ] } }
##
# @query-stats:
#
# Return runtime-collected statistics for objects such as the
# VM or its vCPUs.
#
# The arguments are a StatsFilter and specify the provider and objects
# to return statistics about.
#
# Returns: a list of StatsResult, one for each provider and object
# (e.g., for each vCPU).
#
# Since: 7.1
##
{ 'command': 'query-stats',
'data': 'StatsFilter',
'boxed': true,
'returns': [ 'StatsResult' ] }
##
# @StatsSchemaValue:
#
# Schema for a single statistic.
#
# @name: name of the statistic; each element of the schema is uniquely
# identified by a target, a provider (both available in @StatsSchema)
# and the name.
#
# @type: kind of statistic.
#
# @unit: basic unit of measure for the statistic; if missing, the statistic
# is a simple number or counter.
#
# @base: base for the multiple of @unit in which the statistic is measured.
# Only present if @exponent is non-zero; @base and @exponent together
# form a SI prefix (e.g., _nano-_ for ``base=10`` and ``exponent=-9``)
# or IEC binary prefix (e.g. _kibi-_ for ``base=2`` and ``exponent=10``)
#
# @exponent: exponent for the multiple of @unit in which the statistic is
# expressed, or 0 for the basic unit
#
# @bucket-size: Present when @type is "linear-histogram", contains the width
# of each bucket of the histogram.
#
# Since: 7.1
##
{ 'struct': 'StatsSchemaValue',
'data': { 'name': 'str',
'type': 'StatsType',
'*unit': 'StatsUnit',
'*base': 'int8',
'exponent': 'int16',
'*bucket-size': 'uint32' } }
##
# @StatsSchema:
#
# Schema for all available statistics for a provider and target.
#
# @provider: provider for this set of statistics.
#
# @target: the kind of object that can be queried through the provider.
#
# @stats: list of statistics.
#
# Since: 7.1
##
{ 'struct': 'StatsSchema',
'data': { 'provider': 'StatsProvider',
'target': 'StatsTarget',
'stats': [ 'StatsSchemaValue' ] } }
##
# @query-stats-schemas:
#
# Return the schema for all available runtime-collected statistics.
#
# Note: runtime-collected statistics and their names fall outside QEMU's usual
# deprecation policies. QEMU will try to keep the set of available data
# stable, together with their names, but will not guarantee stability
# at all costs; the same is true of providers that source statistics
# externally, e.g. from Linux. For example, if the same value is being
# tracked with different names on different architectures or by different
# providers, one of them might be renamed. A statistic might go away if
# an algorithm is changed or some code is removed; changing a default
# might cause previously useful statistics to always report 0. Such
# changes, however, are expected to be rare.
#
# Since: 7.1
##
{ 'command': 'query-stats-schemas',
'data': { '*provider': 'StatsProvider' },
'returns': [ 'StatsSchema' ] }

View File

@ -2450,6 +2450,50 @@ static void test_qemu_strtosz_metric(void)
g_assert(endptr == str + 7);
}
static void test_freq_to_str(void)
{
g_assert_cmpstr(freq_to_str(999), ==, "999 Hz");
g_assert_cmpstr(freq_to_str(1000), ==, "1 KHz");
g_assert_cmpstr(freq_to_str(1010), ==, "1.01 KHz");
}
static void test_size_to_str(void)
{
g_assert_cmpstr(size_to_str(0), ==, "0 B");
g_assert_cmpstr(size_to_str(1), ==, "1 B");
g_assert_cmpstr(size_to_str(1016), ==, "0.992 KiB");
g_assert_cmpstr(size_to_str(1024), ==, "1 KiB");
g_assert_cmpstr(size_to_str(512ull << 20), ==, "512 MiB");
}
static void test_iec_binary_prefix(void)
{
g_assert_cmpstr(iec_binary_prefix(0), ==, "");
g_assert_cmpstr(iec_binary_prefix(10), ==, "Ki");
g_assert_cmpstr(iec_binary_prefix(20), ==, "Mi");
g_assert_cmpstr(iec_binary_prefix(30), ==, "Gi");
g_assert_cmpstr(iec_binary_prefix(40), ==, "Ti");
g_assert_cmpstr(iec_binary_prefix(50), ==, "Pi");
g_assert_cmpstr(iec_binary_prefix(60), ==, "Ei");
}
static void test_si_prefix(void)
{
g_assert_cmpstr(si_prefix(-18), ==, "a");
g_assert_cmpstr(si_prefix(-15), ==, "f");
g_assert_cmpstr(si_prefix(-12), ==, "p");
g_assert_cmpstr(si_prefix(-9), ==, "n");
g_assert_cmpstr(si_prefix(-6), ==, "u");
g_assert_cmpstr(si_prefix(-3), ==, "m");
g_assert_cmpstr(si_prefix(0), ==, "");
g_assert_cmpstr(si_prefix(3), ==, "K");
g_assert_cmpstr(si_prefix(6), ==, "M");
g_assert_cmpstr(si_prefix(9), ==, "G");
g_assert_cmpstr(si_prefix(12), ==, "T");
g_assert_cmpstr(si_prefix(15), ==, "P");
g_assert_cmpstr(si_prefix(18), ==, "E");
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@ -2729,5 +2773,13 @@ int main(int argc, char **argv)
g_test_add_func("/cutils/strtosz/metric",
test_qemu_strtosz_metric);
g_test_add_func("/cutils/size_to_str",
test_size_to_str);
g_test_add_func("/cutils/freq_to_str",
test_freq_to_str);
g_test_add_func("/cutils/iec_binary_prefix",
test_iec_binary_prefix);
g_test_add_func("/cutils/si_prefix",
test_si_prefix);
return g_test_run();
}

View File

@ -1,8 +1,17 @@
# Makefile for VM tests
.PHONY: vm-build-all vm-clean-all
# Hack to allow running in an unconfigured build tree
ifeq ($(wildcard $(SRC_PATH)/config-host.mak),)
VM_PYTHON = PYTHONPATH=$(SRC_PATH)/python /usr/bin/env python3
VM_VENV =
HOST_ARCH := $(shell uname -m)
else
VM_PYTHON = $(TESTS_PYTHON)
VM_VENV = check-venv
HOST_ARCH = $(ARCH)
endif
HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m))
.PHONY: vm-build-all vm-clean-all
EFI_AARCH64 = $(wildcard $(BUILD_DIR)/pc-bios/edk2-aarch64-code.fd)
@ -85,10 +94,10 @@ vm-clean-all:
$(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
$(SRC_PATH)/tests/vm/basevm.py \
$(SRC_PATH)/tests/vm/Makefile.include \
check-venv
$(VM_VENV)
@mkdir -p $(IMAGES_DIR)
$(call quiet-command, \
$(TESTS_PYTHON) $< \
$(VM_PYTHON) $< \
$(if $(V)$(DEBUG), --debug) \
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
@ -100,11 +109,10 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
--build-image $@, \
" VM-IMAGE $*")
# Build in VM $(IMAGE)
vm-build-%: $(IMAGES_DIR)/%.img check-venv
vm-build-%: $(IMAGES_DIR)/%.img $(VM_VENV)
$(call quiet-command, \
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \
$(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \
$(if $(V)$(DEBUG), --debug) \
$(if $(DEBUG), --interactive) \
$(if $(J),--jobs $(J)) \
@ -128,9 +136,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img
-device virtio-net-pci,netdev=vnet \
|| true
vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv
vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV)
$(call quiet-command, \
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \
$(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \
$(if $(J),--jobs $(J)) \
$(if $(V)$(DEBUG), --debug) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \

View File

@ -872,6 +872,25 @@ int parse_debug_env(const char *name, int max, int initial)
return debug;
}
const char *si_prefix(unsigned int exp10)
{
static const char *prefixes[] = {
"a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E"
};
exp10 += 18;
assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes));
return prefixes[exp10 / 3];
}
const char *iec_binary_prefix(unsigned int exp2)
{
static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes));
return prefixes[exp2 / 10];
}
/*
* Return human readable string for size @val.
* @val can be anything that uint64_t allows (no more than "16 EiB").
@ -880,7 +899,6 @@ int parse_debug_env(const char *name, int max, int initial)
*/
char *size_to_str(uint64_t val)
{
static const char *suffixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
uint64_t div;
int i;
@ -891,25 +909,23 @@ char *size_to_str(uint64_t val)
* (see e41b509d68afb1f for more info)
*/
frexp(val / (1000.0 / 1024.0), &i);
i = (i - 1) / 10;
div = 1ULL << (i * 10);
i = (i - 1) / 10 * 10;
div = 1ULL << i;
return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]);
return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i));
}
char *freq_to_str(uint64_t freq_hz)
{
static const char *const suffixes[] = { "", "K", "M", "G", "T", "P", "E" };
double freq = freq_hz;
size_t idx = 0;
size_t exp10 = 0;
while (freq >= 1000.0) {
freq /= 1000.0;
idx++;
exp10 += 3;
}
assert(idx < ARRAY_SIZE(suffixes));
return g_strdup_printf("%0.3g %sHz", freq, suffixes[idx]);
return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10));
}
int qemu_pstrcmp0(const char **str1, const char **str2)