aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml14
-rw-r--r--MAINTAINERS7
-rw-r--r--Makefile44
-rw-r--r--audio/audio.c28
-rw-r--r--audio/trace-events5
-rw-r--r--block.c12
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/blklogwrites.c547
-rw-r--r--block/crypto.c2
-rw-r--r--block/dirty-bitmap.c12
-rw-r--r--block/file-posix.c21
-rw-r--r--block/qcow2.c138
-rw-r--r--block/qcow2.h3
-rwxr-xr-xconfigure2
-rw-r--r--default-configs/riscv32-softmmu.mak2
-rw-r--r--default-configs/riscv64-softmmu.mak2
-rw-r--r--docs/devel/qapi-code-gen.txt92
-rw-r--r--docs/devel/testing.rst21
-rw-r--r--docs/interop/qmp-intro.txt13
-rw-r--r--docs/interop/qmp-spec.txt85
-rw-r--r--hmp-commands-info.hx2
-rw-r--r--hmp.c9
-rw-r--r--hw/audio/hda-codec.c5
-rw-r--r--hw/riscv/sifive_e.c99
-rw-r--r--hw/riscv/sifive_plic.c6
-rw-r--r--hw/riscv/sifive_u.c148
-rw-r--r--hw/riscv/virt.c4
-rw-r--r--include/block/block.h7
-rw-r--r--include/block/dirty-bitmap.h1
-rw-r--r--include/hw/riscv/sifive_e.h16
-rw-r--r--include/hw/riscv/sifive_plic.h1
-rw-r--r--include/hw/riscv/sifive_u.h25
-rw-r--r--include/qapi/qmp/dispatch.h6
-rw-r--r--include/qapi/qmp/qjson.h2
-rw-r--r--linux-user/Makefile.objs2
-rw-r--r--linux-user/exit.c35
-rw-r--r--linux-user/qemu.h8
-rw-r--r--linux-user/strace.list6
-rw-r--r--linux-user/syscall.c14
-rw-r--r--migration/block-dirty-bitmap.c4
-rw-r--r--monitor.c510
-rw-r--r--qapi/block-core.json38
-rw-r--r--qapi/char.json8
-rw-r--r--qapi/misc.json23
-rw-r--r--qapi/qmp-dispatch.c97
-rw-r--r--qapi/qmp-event.c10
-rw-r--r--qapi/ui.json75
-rw-r--r--qemu-img.c5
-rw-r--r--qga/commands-posix.c362
-rw-r--r--qga/commands-win32.c14
-rw-r--r--qga/installer/qemu-ga.wxs2
-rw-r--r--qga/main.c18
-rw-r--r--qga/qapi-schema.json11
-rw-r--r--qmp.c62
-rw-r--r--qobject/qjson.c23
-rw-r--r--scripts/coverity-model.c8
-rwxr-xr-xscripts/create_config2
-rw-r--r--scripts/qapi/commands.py26
-rw-r--r--scripts/qapi/common.py280
-rwxr-xr-x[-rw-r--r--]scripts/qapi/doc.py32
-rw-r--r--scripts/qapi/events.py8
-rw-r--r--scripts/qapi/introspect.py47
-rw-r--r--scripts/qapi/types.py58
-rw-r--r--scripts/qapi/visit.py41
-rwxr-xr-xscripts/qemu-binfmt-conf.sh64
-rwxr-xr-xscripts/travis/coverage-summary.sh27
-rw-r--r--tests/Makefile.include20
-rw-r--r--tests/docker/Makefile.include52
-rwxr-xr-xtests/docker/docker.py4
-rw-r--r--tests/docker/dockerfiles/debian-bootstrap.docker2
-rwxr-xr-xtests/docker/dockerfiles/debian-bootstrap.pre11
-rw-r--r--tests/docker/dockerfiles/debian-powerpc-user-cross.docker15
-rw-r--r--tests/docker/dockerfiles/debian8-mxe.docker2
-rw-r--r--tests/docker/dockerfiles/travis.docker2
-rw-r--r--tests/docker/dockerfiles/ubuntu.docker8
-rw-r--r--tests/migration-test.c23
-rw-r--r--tests/qapi-schema/bad-if-empty-list.err1
-rw-r--r--tests/qapi-schema/bad-if-empty-list.exit1
-rw-r--r--tests/qapi-schema/bad-if-empty-list.json3
-rw-r--r--tests/qapi-schema/bad-if-empty-list.out0
-rw-r--r--tests/qapi-schema/bad-if-empty.err1
-rw-r--r--tests/qapi-schema/bad-if-empty.exit1
-rw-r--r--tests/qapi-schema/bad-if-empty.json3
-rw-r--r--tests/qapi-schema/bad-if-empty.out0
-rw-r--r--tests/qapi-schema/bad-if-list.err1
-rw-r--r--tests/qapi-schema/bad-if-list.exit1
-rw-r--r--tests/qapi-schema/bad-if-list.json3
-rw-r--r--tests/qapi-schema/bad-if-list.out0
-rw-r--r--tests/qapi-schema/bad-if.err1
-rw-r--r--tests/qapi-schema/bad-if.exit1
-rw-r--r--tests/qapi-schema/bad-if.json3
-rw-r--r--tests/qapi-schema/bad-if.out0
-rw-r--r--tests/qapi-schema/doc-good.json2
-rw-r--r--tests/qapi-schema/doc-good.out1
-rw-r--r--tests/qapi-schema/doc-good.texi2
-rw-r--r--tests/qapi-schema/qapi-schema-test.json28
-rw-r--r--tests/qapi-schema/qapi-schema-test.out35
-rw-r--r--tests/qapi-schema/test-qapi.py20
-rwxr-xr-xtests/qemu-iotests/05115
-rw-r--r--tests/qemu-iotests/051.out23
-rw-r--r--tests/qemu-iotests/051.pc.out23
-rw-r--r--tests/qmp-test.c107
-rw-r--r--tests/test-qga.c88
-rw-r--r--tests/test-qmp-cmds.c35
-rw-r--r--ui/vnc.h2
106 files changed, 2755 insertions, 1088 deletions
diff --git a/.gitignore b/.gitignore
index 9da3b3e626..5668d02782 100644
--- a/.gitignore
+++ b/.gitignore
@@ -155,6 +155,7 @@
.sdk
*.gcda
*.gcno
+*.gcov
/pc-bios/bios-pq/status
/pc-bios/vgabios-pq/status
/pc-bios/optionrom/linuxboot.asm
diff --git a/.travis.yml b/.travis.yml
index bd66c18fed..95be6ec59f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,6 +38,7 @@ addons:
- libvte-2.90-dev
- sparse
- uuid-dev
+ - gcovr
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
# to prevent IRC notifications from forks. This was created using:
@@ -50,6 +51,8 @@ notifications:
on_failure: always
env:
global:
+ - SRC_DIR="."
+ - BUILD_DIR="."
- TEST_CMD="make check"
- MAKEFLAGS="-j3"
matrix:
@@ -66,14 +69,17 @@ git:
before_install:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
- - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- - git submodule update --init --recursive
+ - git submodule update --init --recursive capstone dtc ui/keycodemapdb
before_script:
- - ./configure ${CONFIG} || { cat config.log && exit 1; }
+ - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
+ - ${SRC_DIR}/configure ${CONFIG} || { cat config.log && exit 1; }
script:
- make ${MAKEFLAGS} && ${TEST_CMD}
matrix:
include:
+ # Test out-of-tree builds
+ - env: CONFIG="--enable-debug --enable-debug-tcg"
+ BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
# Test with Clang for compile portability (Travis uses clang-5.0)
- env: CONFIG="--disable-system"
compiler: clang
@@ -81,6 +87,8 @@ matrix:
compiler: clang
# gprof/gcov are GCC features
- env: CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
+ after_success:
+ - ${SRC_DIR}/scripts/travis/coverage-summary.sh
compiler: gcc
# We manually include builds which we disable "make check" for
- env: CONFIG="--enable-debug --enable-tcg-interpreter"
diff --git a/MAINTAINERS b/MAINTAINERS
index 6630d691d1..20eef3cb61 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2052,6 +2052,12 @@ S: Supported
F: block/quorum.c
L: qemu-block@nongnu.org
+blklogwrites
+M: Ari Sundholm <ari@tuxera.com>
+L: qemu-block@nongnu.org
+S: Supported
+F: block/blklogwrites.c
+
blkverify
M: Stefan Hajnoczi <stefanha@redhat.com>
L: qemu-block@nongnu.org
@@ -2166,6 +2172,7 @@ R: Philippe Mathieu-Daudé <f4bug@amsat.org>
L: qemu-devel@nongnu.org
S: Maintained
F: .travis.yml
+F: scripts/travis/
F: .shippable.yml
F: tests/docker/
F: tests/vm/
diff --git a/Makefile b/Makefile
index 7ed9cc4a21..2da686be33 100644
--- a/Makefile
+++ b/Makefile
@@ -60,8 +60,8 @@ seems to have been used for an in-tree build. You can fix this by running \
endif
endif
-CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_LIST)),y)
-CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_LIST)),y)
+CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y)
+CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y)
CONFIG_XEN := $(CONFIG_XEN_BACKEND)
CONFIG_ALL=y
-include config-all-devices.mak
@@ -365,8 +365,8 @@ DOCS=
endif
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
-SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_LIST))
-SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_LIST))
+SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
+SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
ifeq ($(SUBDIR_DEVICES_MAK),)
config-all-devices.mak:
@@ -469,7 +469,7 @@ config-host.h-timestamp: config-host.mak
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
-SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_LIST))
+SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
@@ -513,7 +513,7 @@ ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
romsubdir-%:
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/" CFLAGS="$(filter -O% -g%,$(CFLAGS))",)
-ALL_SUBDIRS=$(TARGET_LIST) $(patsubst %,pc-bios/%, $(ROMS))
+ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
@@ -723,6 +723,14 @@ module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
"GEN","$@")
+ifdef CONFIG_GCOV
+.PHONY: clean-coverage
+clean-coverage:
+ $(call quiet-command, \
+ find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \
+ "CLEAN", "coverage files")
+endif
+
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
@@ -770,7 +778,7 @@ distclean: clean
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
rm -f docs/qemu-block-drivers.7
- for d in $(TARGET_LIST); do \
+ for d in $(TARGET_DIRS); do \
rm -rf $$d || exit 1 ; \
done
rm -Rf .sdk
@@ -871,7 +879,7 @@ endif
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
done
$(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all"
- for d in $(TARGET_LIST); do \
+ for d in $(TARGET_DIRS); do \
$(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \
done
@@ -978,6 +986,16 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
+# Reports/Analysis
+
+%/coverage-report.html:
+ @mkdir -p $*
+ $(call quiet-command,\
+ gcovr -p --html --html-details -o $@, \
+ "GEN", "coverage-report.html")
+
+.PHONY: coverage-report
+coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
ifdef CONFIG_WIN32
@@ -1066,13 +1084,16 @@ endif
@echo ' ctags/TAGS - Generate tags file for editors'
@echo ' cscope - Generate cscope index'
@echo ''
- @$(if $(TARGET_LIST), \
+ @$(if $(TARGET_DIRS), \
echo 'Architecture specific targets:'; \
- $(foreach t, $(TARGET_LIST), \
+ $(foreach t, $(TARGET_DIRS), \
printf " %-30s - Build for %s\\n" $(patsubst %,subdir-%,$(t)) $(t);) \
echo '')
@echo 'Cleaning targets:'
@echo ' clean - Remove most generated files but keep the config'
+ifdef CONFIG_GCOV
+ @echo ' clean-coverage - Remove coverage files'
+endif
@echo ' distclean - Remove all generated files'
@echo ' dist - Build a distributable tarball'
@echo ''
@@ -1084,6 +1105,9 @@ endif
@echo 'Documentation targets:'
@echo ' html info pdf txt'
@echo ' - Build documentation in specified format'
+ifdef CONFIG_GCOV
+ @echo ' coverage-report - Create code coverage report'
+endif
@echo ''
ifdef CONFIG_WIN32
@echo 'Windows targets:'
diff --git a/audio/audio.c b/audio/audio.c
index d6e91901aa..1ace47f510 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -29,6 +29,7 @@
#include "sysemu/sysemu.h"
#include "qemu/cutils.h"
#include "sysemu/replay.h"
+#include "trace.h"
#define AUDIO_CAP "audio"
#include "audio_int.h"
@@ -1129,6 +1130,10 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
/*
* Timer
*/
+
+static bool audio_timer_running;
+static uint64_t audio_timer_last;
+
static int audio_is_timer_needed (void)
{
HWVoiceIn *hwi = NULL;
@@ -1148,14 +1153,31 @@ static void audio_reset_timer (AudioState *s)
if (audio_is_timer_needed ()) {
timer_mod_anticipate_ns(s->ts,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
- }
- else {
- timer_del (s->ts);
+ if (!audio_timer_running) {
+ audio_timer_running = true;
+ audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ trace_audio_timer_start(conf.period.ticks / SCALE_MS);
+ }
+ } else {
+ timer_del(s->ts);
+ if (audio_timer_running) {
+ audio_timer_running = false;
+ trace_audio_timer_stop();
+ }
}
}
static void audio_timer (void *opaque)
{
+ int64_t now, diff;
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ diff = now - audio_timer_last;
+ if (diff > conf.period.ticks * 3 / 2) {
+ trace_audio_timer_delayed(diff / SCALE_MS);
+ }
+ audio_timer_last = now;
+
audio_run ("timer");
audio_reset_timer (opaque);
}
diff --git a/audio/trace-events b/audio/trace-events
index d37639e611..c986469319 100644
--- a/audio/trace-events
+++ b/audio/trace-events
@@ -15,3 +15,8 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d"
# audio/ossaudio.c
oss_version(int version) "OSS version = 0x%x"
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
+
+# audio/audio.c
+audio_timer_start(int interval) "interval %d ms"
+audio_timer_stop(void) ""
+audio_timer_delayed(int interval) "interval %d ms"
diff --git a/block.c b/block.c
index 70a46fdd84..ac8b3a3511 100644
--- a/block.c
+++ b/block.c
@@ -1156,6 +1156,12 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
goto out;
}
+ /* Make sure that the node name isn't truncated */
+ if (strlen(node_name) >= sizeof(bs->node_name)) {
+ error_setg(errp, "Node name too long");
+ goto out;
+ }
+
/* copy node name into the bs and insert it into the graph list */
pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
@@ -1948,12 +1954,6 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
return 0;
}
-#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
- | BLK_PERM_WRITE \
- | BLK_PERM_WRITE_UNCHANGED \
- | BLK_PERM_RESIZE)
-#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
-
void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 899bfb5e2c..c8337bf186 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -5,6 +5,7 @@ block-obj-y += qed-check.o
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
block-obj-y += quorum.o
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
+block-obj-y += blklogwrites.o
block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += file-posix.o
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
new file mode 100644
index 0000000000..63bf6b34a9
--- /dev/null
+++ b/block/blklogwrites.c
@@ -0,0 +1,547 @@
+/*
+ * Write logging blk driver based on blkverify and blkdebug.
+ *
+ * Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
+ * Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
+ * Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
+#include "block/block_int.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qstring.h"
+#include "qemu/cutils.h"
+#include "qemu/option.h"
+
+/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
+
+#define LOG_FLUSH_FLAG (1 << 0)
+#define LOG_FUA_FLAG (1 << 1)
+#define LOG_DISCARD_FLAG (1 << 2)
+#define LOG_MARK_FLAG (1 << 3)
+#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
+ | LOG_FUA_FLAG \
+ | LOG_DISCARD_FLAG \
+ | LOG_MARK_FLAG)
+
+#define WRITE_LOG_VERSION 1ULL
+#define WRITE_LOG_MAGIC 0x6a736677736872ULL
+
+/* All fields are little-endian. */
+struct log_write_super {
+ uint64_t magic;
+ uint64_t version;
+ uint64_t nr_entries;
+ uint32_t sectorsize;
+} QEMU_PACKED;
+
+struct log_write_entry {
+ uint64_t sector;
+ uint64_t nr_sectors;
+ uint64_t flags;
+ uint64_t data_len;
+} QEMU_PACKED;
+
+/* End of disk format structures. */
+
+typedef struct {
+ BdrvChild *log_file;
+ uint32_t sectorsize;
+ uint32_t sectorbits;
+ uint64_t cur_log_sector;
+ uint64_t nr_entries;
+ uint64_t update_interval;
+} BDRVBlkLogWritesState;
+
+static QemuOptsList runtime_opts = {
+ .name = "blklogwrites",
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+ .desc = {
+ {
+ .name = "log-append",
+ .type = QEMU_OPT_BOOL,
+ .help = "Append to an existing log",
+ },
+ {
+ .name = "log-sector-size",
+ .type = QEMU_OPT_SIZE,
+ .help = "Log sector size",
+ },
+ {
+ .name = "log-super-update-interval",
+ .type = QEMU_OPT_NUMBER,
+ .help = "Log superblock update interval (# of write requests)",
+ },
+ { /* end of list */ }
+ },
+};
+
+static inline uint32_t blk_log_writes_log2(uint32_t value)
+{
+ assert(value > 0);
+ return 31 - clz32(value);
+}
+
+static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
+{
+ return sector_size < (1ull << 24) && is_power_of_2(sector_size);
+}
+
+static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
+ uint32_t sector_size,
+ uint64_t nr_entries,
+ Error **errp)
+{
+ uint64_t cur_sector = 1;
+ uint64_t cur_idx = 0;
+ uint32_t sector_bits = blk_log_writes_log2(sector_size);
+ struct log_write_entry cur_entry;
+
+ while (cur_idx < nr_entries) {
+ int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
+ sizeof(cur_entry));
+ if (read_ret < 0) {
+ error_setg_errno(errp, -read_ret,
+ "Failed to read log entry %"PRIu64, cur_idx);
+ return (uint64_t)-1ull;
+ }
+
+ if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
+ error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
+ le64_to_cpu(cur_entry.flags), cur_idx);
+ return (uint64_t)-1ull;
+ }
+
+ /* Account for the sector of the entry itself */
+ ++cur_sector;
+
+ /*
+ * Account for the data of the write.
+ * For discards, this data is not present.
+ */
+ if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
+ cur_sector += le64_to_cpu(cur_entry.nr_sectors);
+ }
+
+ ++cur_idx;
+ }
+
+ return cur_sector;
+}
+
+static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
+ Error **errp)
+{
+ BDRVBlkLogWritesState *s = bs->opaque;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+ int ret;
+ uint64_t log_sector_size;
+ bool log_append;
+
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (local_err) {
+ ret = -EINVAL;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ /* Open the file */
+ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
+ &local_err);
+ if (local_err) {
+ ret = -EINVAL;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ /* Open the log file */
+ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
+ &local_err);
+ if (local_err) {
+ ret = -EINVAL;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ log_append = qemu_opt_get_bool(opts, "log-append", false);
+
+ if (log_append) {
+ struct log_write_super log_sb = { 0, 0, 0, 0 };
+
+ if (qemu_opt_find(opts, "log-sector-size")) {
+ ret = -EINVAL;
+ error_setg(errp, "log-append and log-sector-size are mutually "
+ "exclusive");
+ goto fail_log;
+ }
+
+ /* Read log superblock or fake one for an empty log */
+ if (!bdrv_getlength(s->log_file->bs)) {
+ log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
+ log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
+ log_sb.nr_entries = cpu_to_le64(0);
+ log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
+ } else {
+ ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read log superblock");
+ goto fail_log;
+ }
+ }
+
+ if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
+ ret = -EINVAL;
+ error_setg(errp, "Invalid log superblock magic");
+ goto fail_log;
+ }
+
+ if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
+ ret = -EINVAL;
+ error_setg(errp, "Unsupported log version %"PRIu64,
+ le64_to_cpu(log_sb.version));
+ goto fail_log;
+ }
+
+ log_sector_size = le32_to_cpu(log_sb.sectorsize);
+ s->cur_log_sector = 1;
+ s->nr_entries = 0;
+
+ if (blk_log_writes_sector_size_valid(log_sector_size)) {
+ s->cur_log_sector =
+ blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
+ le64_to_cpu(log_sb.nr_entries), &local_err);
+ if (local_err) {
+ ret = -EINVAL;
+ error_propagate(errp, local_err);
+ goto fail_log;
+ }
+
+ s->nr_entries = le64_to_cpu(log_sb.nr_entries);
+ }
+ } else {
+ log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
+ BDRV_SECTOR_SIZE);
+ s->cur_log_sector = 1;
+ s->nr_entries = 0;
+ }
+
+ if (!blk_log_writes_sector_size_valid(log_sector_size)) {
+ ret = -EINVAL;
+ error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
+ goto fail_log;
+ }
+
+ s->sectorsize = log_sector_size;
+ s->sectorbits = blk_log_writes_log2(log_sector_size);
+ s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
+ 4096);
+ if (!s->update_interval) {
+ ret = -EINVAL;
+ error_setg(errp, "Invalid log superblock update interval %"PRIu64,
+ s->update_interval);
+ goto fail_log;
+ }
+
+ ret = 0;
+fail_log:
+ if (ret < 0) {
+ bdrv_unref_child(bs, s->log_file);
+ s->log_file = NULL;
+ }
+fail:
+ if (ret < 0) {
+ bdrv_unref_child(bs, bs->file);
+ bs->file = NULL;
+ }
+ qemu_opts_del(opts);
+ return ret;
+}
+
+static void blk_log_writes_close(BlockDriverState *bs)
+{
+ BDRVBlkLogWritesState *s = bs->opaque;
+
+ bdrv_unref_child(bs, s->log_file);
+ s->log_file = NULL;
+}
+
+static int64_t blk_log_writes_getlength(BlockDriverState *bs)
+{
+ return bdrv_getlength(bs->file->bs);
+}
+
+static void blk_log_writes_refresh_filename(BlockDriverState *bs,
+ QDict *options)
+{
+ BDRVBlkLogWritesState *s = bs->opaque;
+
+ /* bs->file->bs has already been refreshed */
+ bdrv_refresh_filename(s->log_file->bs);
+
+ if (bs->file->bs->full_open_options
+ && s->log_file->bs->full_open_options)
+ {
+ QDict *opts = qdict_new();
+ qdict_put_str(opts, "driver", "blklogwrites");
+
+ qobject_ref(bs->file->bs->full_open_options);
+ qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
+ qobject_ref(s->log_file->bs->full_open_options);
+ qdict_put_obj(opts, "log",
+ QOBJECT(s->log_file->bs->full_open_options));
+ qdict_put_int(opts, "log-sector-size", s->sectorsize);
+
+ bs->full_open_options = opts;
+ }
+}
+
+static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
+ const BdrvChildRole *role,
+ BlockReopenQueue *ro_q,
+ uint64_t perm, uint64_t shrd,
+ uint64_t *nperm, uint64_t *nshrd)
+{
+ if (!c) {
+ *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
+ *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
+ return;
+ }
+
+ if (!strcmp(c->name, "log")) {
+ bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
+ } else {
+ bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
+ }
+}
+
+static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
+{
+ BDRVBlkLogWritesState *s = bs->opaque;
+ bs->bl.request_alignment = s->sectorsize;
+}
+
+static int coroutine_fn
+blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
+}
+
+typedef struct BlkLogWritesFileReq {
+ BlockDriverState *bs;
+ uint64_t offset;
+ uint64_t bytes;
+ int file_flags;
+ QEMUIOVector *qiov;
+ int (*func)(struct BlkLogWritesFileReq *r);
+ int file_ret;
+} BlkLogWritesFileReq;
+
+typedef struct {
+ BlockDriverState *bs;
+ QEMUIOVector *qiov;
+ struct log_write_entry entry;
+ uint64_t zero_size;
+ int log_ret;
+} BlkLogWritesLogReq;
+
+static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
+{
+ BDRVBlkLogWritesState *s = lr->bs->opaque;
+ uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
+
+ s->nr_entries++;
+ s->cur_log_sector +=
+ ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
+
+ lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
+ lr->qiov, 0);
+
+ /* Logging for the "write zeroes" operation */
+ if (lr->log_ret == 0 && lr->zero_size) {
+ cur_log_offset = s->cur_log_sector << s->sectorbits;
+ s->cur_log_sector +=
+ ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
+
+ lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
+ lr->zero_size, 0);
+ }
+
+ /* Update super block on flush or every update interval */
+ if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
+ || (s->nr_entries % s->update_interval == 0)))
+ {
+ struct log_write_super super = {
+ .magic = cpu_to_le64(WRITE_LOG_MAGIC),
+ .version = cpu_to_le64(WRITE_LOG_VERSION),
+ .nr_entries = cpu_to_le64(s->nr_entries),
+ .sectorsize = cpu_to_le32(s->sectorsize),
+ };
+ void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
+ QEMUIOVector qiov;
+
+ qemu_iovec_init(&qiov, 2);
+ qemu_iovec_add(&qiov, &super, sizeof(super));
+ qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
+
+ lr->log_ret =
+ bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
+ if (lr->log_ret == 0) {
+ lr->log_ret = bdrv_co_flush(s->log_file->bs);
+ }
+ qemu_iovec_destroy(&qiov);
+ g_free(zeroes);
+ }
+}
+
+static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
+{
+ fr->file_ret = fr->func(fr);
+}
+
+static int coroutine_fn
+blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags,
+ int (*file_func)(BlkLogWritesFileReq *r),
+ uint64_t entry_flags, bool is_zero_write)
+{
+ QEMUIOVector log_qiov;
+ size_t niov = qiov ? qiov->niov : 0;
+ BDRVBlkLogWritesState *s = bs->opaque;
+ BlkLogWritesFileReq fr = {
+ .bs = bs,
+ .offset = offset,
+ .bytes = bytes,
+ .file_flags = flags,
+ .qiov = qiov,
+ .func = file_func,
+ };
+ BlkLogWritesLogReq lr = {
+ .bs = bs,
+ .qiov = &log_qiov,
+ .entry = {
+ .sector = cpu_to_le64(offset >> s->sectorbits),
+ .nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
+ .flags = cpu_to_le64(entry_flags),
+ .data_len = 0,
+ },
+ .zero_size = is_zero_write ? bytes : 0,
+ };
+ void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
+
+ assert((1 << s->sectorbits) == s->sectorsize);
+ assert(bs->bl.request_alignment == s->sectorsize);
+ assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
+ assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
+
+ qemu_iovec_init(&log_qiov, niov + 2);
+ qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
+ qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
+ if (qiov) {
+ qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
+ }
+
+ blk_log_writes_co_do_file(&fr);
+ blk_log_writes_co_do_log(&lr);
+
+ qemu_iovec_destroy(&log_qiov);
+ g_free(zeroes);
+
+ if (lr.log_ret < 0) {
+ return lr.log_ret;
+ }
+
+ return fr.file_ret;
+}
+
+static int coroutine_fn
+blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
+{
+ return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
+ fr->qiov, fr->file_flags);
+}
+
+static int coroutine_fn
+blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
+{
+ return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
+ fr->file_flags);
+}
+
+static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
+{
+ return bdrv_co_flush(fr->bs->file->bs);
+}
+
+static int coroutine_fn
+blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
+{
+ return bdrv_co_pdiscard(fr->bs->file->bs, fr->offset, fr->bytes);
+}
+
+static int coroutine_fn
+blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
+ blk_log_writes_co_do_file_pwritev, 0, false);
+}
+
+static int coroutine_fn
+blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
+ BdrvRequestFlags flags)
+{
+ return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
+ blk_log_writes_co_do_file_pwrite_zeroes, 0,
+ true);
+}
+
+static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
+{
+ return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
+ blk_log_writes_co_do_file_flush,
+ LOG_FLUSH_FLAG, false);
+}
+
+static int coroutine_fn
+blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
+{
+ return blk_log_writes_co_log(bs, offset, count, NULL, 0,
+ blk_log_writes_co_do_file_pdiscard,
+ LOG_DISCARD_FLAG, false);
+}
+
+static BlockDriver bdrv_blk_log_writes = {
+ .format_name = "blklogwrites",
+ .instance_size = sizeof(BDRVBlkLogWritesState),
+
+ .bdrv_open = blk_log_writes_open,
+ .bdrv_close = blk_log_writes_close,
+ .bdrv_getlength = blk_log_writes_getlength,
+ .bdrv_refresh_filename = blk_log_writes_refresh_filename,
+ .bdrv_child_perm = blk_log_writes_child_perm,
+ .bdrv_refresh_limits = blk_log_writes_refresh_limits,
+
+ .bdrv_co_preadv = blk_log_writes_co_preadv,
+ .bdrv_co_pwritev = blk_log_writes_co_pwritev,
+ .bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
+ .bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
+ .bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
+ .bdrv_co_block_status = bdrv_co_block_status_from_file,
+
+ .is_filter = true,
+};
+
+static void bdrv_blk_log_writes_init(void)
+{
+ bdrv_register(&bdrv_blk_log_writes);
+}
+
+block_init(bdrv_blk_log_writes_init);
diff --git a/block/crypto.c b/block/crypto.c
index 994172a3de..146d81c90a 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -551,7 +551,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
/* Create protocol layer */
ret = bdrv_create_file(filename, opts, errp);
if (ret < 0) {
- return ret;
+ goto fail;
}
bs = bdrv_open(filename, NULL, NULL,
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index db1782ec1f..c9b8a6fd52 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -241,11 +241,18 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
return 0;
}
+void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
+{
+ assert(!bdrv_dirty_bitmap_frozen(bitmap));
+ bitmap->disabled = false;
+}
+
/* Called with BQL taken. */
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
{
+ assert(bitmap->mutex == bitmap->successor->mutex);
qemu_mutex_lock(bitmap->mutex);
- bdrv_enable_dirty_bitmap(bitmap->successor);
+ bdrv_enable_dirty_bitmap_locked(bitmap->successor);
qemu_mutex_unlock(bitmap->mutex);
}
@@ -424,8 +431,7 @@ void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
{
bdrv_dirty_bitmap_lock(bitmap);
- assert(!bdrv_dirty_bitmap_frozen(bitmap));
- bitmap->disabled = false;
+ bdrv_enable_dirty_bitmap_locked(bitmap);
bdrv_dirty_bitmap_unlock(bitmap);
}
diff --git a/block/file-posix.c b/block/file-posix.c
index 829ee538d8..98987b80f1 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -2111,8 +2111,9 @@ static int coroutine_fn
raw_co_create(BlockdevCreateOptions *options, Error **errp)
{
BlockdevCreateOptionsFile *file_opts;
+ Error *local_err = NULL;
int fd;
- int perm, shared;
+ uint64_t perm, shared;
int result = 0;
/* Validate options and set default values */
@@ -2148,7 +2149,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
/* Step one: Take locks */
- result = raw_apply_lock_bytes(fd, perm, shared, false, errp);
+ result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp);
if (result < 0) {
goto out_close;
}
@@ -2156,13 +2157,13 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
/* Step two: Check that nobody else has taken conflicting locks */
result = raw_check_lock_bytes(fd, perm, shared, errp);
if (result < 0) {
- goto out_close;
+ goto out_unlock;
}
/* Clear the file by truncating it to 0 */
result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp);
if (result < 0) {
- goto out_close;
+ goto out_unlock;
}
if (file_opts->nocow) {
@@ -2185,7 +2186,17 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
result = raw_regular_truncate(NULL, fd, file_opts->size,
file_opts->preallocation, errp);
if (result < 0) {
- goto out_close;
+ goto out_unlock;
+ }
+
+out_unlock:
+ raw_apply_lock_bytes(fd, 0, 0, true, &local_err);
+ if (local_err) {
+ /* The above call should not fail, and if it does, that does
+ * not mean the whole creation operation has failed. So
+ * report it the user for their convenience, but do not report
+ * it to the caller. */
+ error_report_err(local_err);
}
out_close:
diff --git a/block/qcow2.c b/block/qcow2.c
index 2f9e58e0c4..33b61b7480 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -23,11 +23,14 @@
*/
#include "qemu/osdep.h"
+
+#define ZLIB_CONST
+#include <zlib.h>
+
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "qemu/module.h"
-#include <zlib.h>
#include "qcow2.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
@@ -41,6 +44,7 @@
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "crypto.h"
+#include "block/thread-pool.h"
/*
Differences with QCOW:
@@ -1541,6 +1545,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
qcow2_check_refcounts(bs, &result, 0);
}
#endif
+
+ qemu_co_queue_init(&s->compress_wait_queue);
+
return ret;
fail:
@@ -3650,6 +3657,104 @@ fail:
return ret;
}
+/*
+ * qcow2_compress()
+ *
+ * @dest - destination buffer, at least of @size-1 bytes
+ * @src - source buffer, @size bytes
+ *
+ * Returns: compressed size on success
+ * -1 if compression is inefficient
+ * -2 on any other error
+ */
+static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
+{
+ ssize_t ret;
+ z_stream strm;
+
+ /* best compression, small window, no zlib header */
+ memset(&strm, 0, sizeof(strm));
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ -12, 9, Z_DEFAULT_STRATEGY);
+ if (ret != 0) {
+ return -2;
+ }
+
+ /* strm.next_in is not const in old zlib versions, such as those used on
+ * OpenBSD/NetBSD, so cast the const away */
+ strm.avail_in = size;
+ strm.next_in = (void *) src;
+ strm.avail_out = size - 1;
+ strm.next_out = dest;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret == Z_STREAM_END) {
+ ret = size - 1 - strm.avail_out;
+ } else {
+ ret = (ret == Z_OK ? -1 : -2);
+ }
+
+ deflateEnd(&strm);
+
+ return ret;
+}
+
+#define MAX_COMPRESS_THREADS 4
+
+typedef struct Qcow2CompressData {
+ void *dest;
+ const void *src;
+ size_t size;
+ ssize_t ret;
+} Qcow2CompressData;
+
+static int qcow2_compress_pool_func(void *opaque)
+{
+ Qcow2CompressData *data = opaque;
+
+ data->ret = qcow2_compress(data->dest, data->src, data->size);
+
+ return 0;
+}
+
+static void qcow2_compress_complete(void *opaque, int ret)
+{
+ qemu_coroutine_enter(opaque);
+}
+
+/* See qcow2_compress definition for parameters description */
+static ssize_t qcow2_co_compress(BlockDriverState *bs,
+ void *dest, const void *src, size_t size)
+{
+ BDRVQcow2State *s = bs->opaque;
+ BlockAIOCB *acb;
+ ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
+ Qcow2CompressData arg = {
+ .dest = dest,
+ .src = src,
+ .size = size,
+ };
+
+ while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) {
+ qemu_co_queue_wait(&s->compress_wait_queue, NULL);
+ }
+
+ s->nb_compress_threads++;
+ acb = thread_pool_submit_aio(pool, qcow2_compress_pool_func, &arg,
+ qcow2_compress_complete,
+ qemu_coroutine_self());
+
+ if (!acb) {
+ s->nb_compress_threads--;
+ return -EINVAL;
+ }
+ qemu_coroutine_yield();
+ s->nb_compress_threads--;
+ qemu_co_queue_next(&s->compress_wait_queue);
+
+ return arg.ret;
+}
+
/* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */
static coroutine_fn int
@@ -3659,8 +3764,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
BDRVQcow2State *s = bs->opaque;
QEMUIOVector hd_qiov;
struct iovec iov;
- z_stream strm;
- int ret, out_len;
+ int ret;
+ size_t out_len;
uint8_t *buf, *out_buf;
int64_t cluster_offset;
@@ -3694,32 +3799,11 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
out_buf = g_malloc(s->cluster_size);
- /* best compression, small window, no zlib header */
- memset(&strm, 0, sizeof(strm));
- ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
- Z_DEFLATED, -12,
- 9, Z_DEFAULT_STRATEGY);
- if (ret != 0) {
+ out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size);
+ if (out_len == -2) {
ret = -EINVAL;
goto fail;
- }
-
- strm.avail_in = s->cluster_size;
- strm.next_in = (uint8_t *)buf;
- strm.avail_out = s->cluster_size;
- strm.next_out = out_buf;
-
- ret = deflate(&strm, Z_FINISH);
- if (ret != Z_STREAM_END && ret != Z_OK) {
- deflateEnd(&strm);
- ret = -EINVAL;
- goto fail;
- }
- out_len = strm.next_out - out_buf;
-
- deflateEnd(&strm);
-
- if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+ } else if (out_len == -1) {
/* could not compress: write normal cluster */
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
if (ret < 0) {
diff --git a/block/qcow2.h b/block/qcow2.h
index 1c9c0d3631..d6aca687d6 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -326,6 +326,9 @@ typedef struct BDRVQcow2State {
* override) */
char *image_backing_file;
char *image_backing_format;
+
+ CoQueue compress_wait_queue;
+ int nb_compress_threads;
} BDRVQcow2State;
typedef struct Qcow2COWRegion {
diff --git a/configure b/configure
index b99ebdde24..2a7796ea80 100755
--- a/configure
+++ b/configure
@@ -6212,7 +6212,7 @@ qemu_version=$(head $source_path/VERSION)
echo "VERSION=$qemu_version" >>$config_host_mak
echo "PKGVERSION=$pkgversion" >>$config_host_mak
echo "SRC_PATH=$source_path" >> $config_host_mak
-echo "TARGET_LIST=$target_list" >> $config_host_mak
+echo "TARGET_DIRS=$target_list" >> $config_host_mak
if [ "$docs" = "yes" ] ; then
echo "BUILD_DOCS=yes" >> $config_host_mak
fi
diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak
index 20e670d99c..7937c69e22 100644
--- a/default-configs/riscv32-softmmu.mak
+++ b/default-configs/riscv32-softmmu.mak
@@ -3,3 +3,5 @@
CONFIG_SERIAL=y
CONFIG_VIRTIO_MMIO=y
include virtio.mak
+
+CONFIG_CADENCE=y
diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak
index 20e670d99c..7937c69e22 100644
--- a/default-configs/riscv64-softmmu.mak
+++ b/default-configs/riscv64-softmmu.mak
@@ -3,3 +3,5 @@
CONFIG_SERIAL=y
CONFIG_VIRTIO_MMIO=y
include virtio.mak
+
+CONFIG_CADENCE=y
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 94a7e8f4d0..c2e11465f0 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -624,62 +624,48 @@ its return value.
In rare cases, QAPI cannot express a type-safe representation of a
corresponding Client JSON Protocol command. You then have to suppress
generation of a marshalling function by including a key 'gen' with
-boolean value false, and instead write your own function. Please try
-to avoid adding new commands that rely on this, and instead use
-type-safe unions. For an example of this usage:
+boolean value false, and instead write your own function. For
+example:
{ 'command': 'netdev_add',
'data': {'type': 'str', 'id': 'str'},
'gen': false }
+Please try to avoid adding new commands that rely on this, and instead
+use type-safe unions.
+
Normally, the QAPI schema is used to describe synchronous exchanges,
where a response is expected. But in some cases, the action of a
command is expected to change state in a way that a successful
response is not possible (although the command will still return a
normal dictionary error on failure). When a successful reply is not
-possible, the command expression should include the optional key
+possible, the command expression includes 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:
+Key 'allow-oob' declares whether the command supports out-of-band
+(OOB) execution. It defaults to false. 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": { } }
+See qmp-spec.txt for out-of-band execution syntax and semantics.
-Without it, even the commands that support out-of-band execution will
-still be run in-band.
+Commands supporting out-of-band execution can still be executed
+in-band.
-Under normal QMP command execution, the following apply to each
-command:
+When a command is executed in-band, its handler runs in the main
+thread with the BQL held.
-- They are executed in order,
-- They run only in main thread of QEMU,
-- They run with the BQL held.
+When a command is executed out-of-band, its handler runs in a
+dedicated monitor I/O thread with the BQL *not* held.
-When a command is executed with OOB, the following changes occur:
+An OOB-capable command handler must satisfy the following conditions:
-- They can be completed before a pending in-band command,
-- They run in a dedicated monitor thread,
-- They run with the BQL not held.
-
-OOB command handlers must satisfy the following conditions:
-
-- It terminates quickly,
-- It does not invoke system calls that may block,
+- It terminates quickly.
+- 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,
+ enabled for postcopy live migration.
- It takes only "fast" locks, i.e. all critical sections protected by
any lock it takes also satisfy the conditions for OOB command
handler code.
@@ -688,17 +674,18 @@ The restrictions on locking limit access to shared state. Such access
requires synchronization, but OOB commands can't take the BQL or any
other "slow" lock.
-If in doubt, do not implement OOB execution support.
+When in doubt, do not implement OOB execution support.
-A command may use the optional 'allow-preconfig' key to permit its execution
-at early runtime configuration stage (preconfig runstate).
-If not specified then a command defaults to 'allow-preconfig': false.
+Key 'allow-preconfig' declares whether the command is available before
+the machine is built. It defaults to false. For example:
-An example of declaring a command that is enabled during preconfig:
{ 'command': 'qmp_capabilities',
'data': { '*enable': [ 'QMPCapability' ] },
'allow-preconfig': true }
+QMP is available before the machine is built only when QEMU was
+started with --preconfig.
+
=== Events ===
Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
@@ -744,6 +731,35 @@ Example: Red Hat, Inc. controls redhat.com, and may therefore add a
downstream command __com.redhat_drive-mirror.
+=== Configuring the schema ===
+
+The 'struct', 'enum', 'union', 'alternate', 'command' and 'event'
+top-level expressions can take an 'if' key. Its value must be a string
+or a list of strings. A string is shorthand for a list containing just
+that string. The code generated for the top-level expression will then
+be guarded by #if COND for each COND in the list.
+
+Example: a conditional struct
+
+ { 'struct': 'IfStruct', 'data': { 'foo': 'int' },
+ 'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] }
+
+gets its generated code guarded like this:
+
+ #if defined(CONFIG_FOO)
+ #if defined(HAVE_BAR)
+ ... generated code ...
+ #endif /* defined(HAVE_BAR) */
+ #endif /* defined(CONFIG_FOO) */
+
+Please note that you are responsible to ensure that the C code will
+compile with an arbitrary combination of conditions, since the
+generators are unable to check it at this point.
+
+The presence of 'if' keys in the schema is reflected through to the
+introspection output depending on the build configuration.
+
+
== Client JSON Protocol introspection ==
Clients of a Client JSON Protocol commonly need to figure out what
diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst
index f33e5a8423..5e19cd50da 100644
--- a/docs/devel/testing.rst
+++ b/docs/devel/testing.rst
@@ -158,12 +158,21 @@ rarely used. See "QEMU iotests" section below for more information.
GCC gcov support
----------------
-``gcov`` is a GCC tool to analyze the testing coverage by instrumenting the
-tested code. To use it, configure QEMU with ``--enable-gcov`` option and build.
-Then run ``make check`` as usual. There will be additional ``gcov`` output as
-the testing goes on, showing the test coverage percentage numbers per analyzed
-source file. More detailed reports can be obtained by running ``gcov`` command
-on the output files under ``$build_dir/tests/``, please read the ``gcov``
+``gcov`` is a GCC tool to analyze the testing coverage by
+instrumenting the tested code. To use it, configure QEMU with
+``--enable-gcov`` option and build. Then run ``make check`` as usual.
+
+If you want to gather coverage information on a single test the ``make
+clean-coverage`` target can be used to delete any existing coverage
+information before running a single test.
+
+You can generate a HTML coverage report by executing ``make
+coverage-report`` which will create
+./reports/coverage/coverage-report.html. If you want to create it
+elsewhere simply execute ``make /foo/bar/baz/coverage-report.html``.
+
+Further analysis can be conducted by running the ``gcov`` command
+directly on the various .gcda output files. Please read the ``gcov``
documentation for more information.
QEMU iotests
diff --git a/docs/interop/qmp-intro.txt b/docs/interop/qmp-intro.txt
index 900d69d612..9d54a718b8 100644
--- a/docs/interop/qmp-intro.txt
+++ b/docs/interop/qmp-intro.txt
@@ -52,13 +52,14 @@ Escape character is '^]'.
"QMP": {
"version": {
"qemu": {
- "micro": 50,
- "minor": 6,
- "major": 1
- },
- "package": ""
- },
+ "micro": 0,
+ "minor": 0,
+ "major": 3
+ },
+ "package": "v3.0.0"
+ },
"capabilities": [
+ "oob"
]
}
}
diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt
index 6fa193a80b..1566b8ae5e 100644
--- a/docs/interop/qmp-spec.txt
+++ b/docs/interop/qmp-spec.txt
@@ -77,52 +77,65 @@ The greeting message format is:
is the same of the query-version command)
- The "capabilities" member specify the availability of features beyond the
baseline specification; the order of elements in this array has no
- particular significance, so a client must search the entire array
- when looking for a particular capability
+ particular significance.
2.2.1 Capabilities
------------------
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.
+- "oob": the QMP server supports "out-of-band" (OOB) command
+ execution, as described in section "2.3.1 Out-of-band execution".
2.3 Issuing Commands
--------------------
The format for command execution is:
-{ "execute": json-string, "arguments": json-object, "id": json-value,
- "control": json-object }
+{ "execute": json-string, "arguments": json-object, "id": json-value }
+
+or
+
+{ "exec-oob": json-string, "arguments": json-object, "id": json-value }
Where,
-- The "execute" member identifies the command to be executed by the Server
+- The "execute" or "exec-oob" member identifies the command to be
+ executed by the server. The latter requests out-of-band execution.
- The "arguments" member is used to pass any arguments required for the
execution of the command, it is optional when no arguments are
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 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 } }
+ command execution, it is optional and will be part of the response
+ if provided. The "id" member can be any json-value. A json-number
+ incremented for each successive command works fine.
+
+2.3.1 Out-of-band execution
+---------------------------
+
+The server normally reads, executes and responds to one command after
+the other. The client therefore receives command responses in issue
+order.
+
+With out-of-band execution enabled via capability negotiation (section
+4.), the server reads and queues commands as they arrive. It executes
+commands from the queue one after the other. Commands executed
+out-of-band jump the queue: the command get executed right away,
+possibly overtaking prior in-band commands. The client may therefore
+receive such a command's response before responses from prior in-band
+commands.
+
+To be able to match responses back to their commands, the client needs
+to pass "id" with out-of-band commands. Passing it with all commands
+is recommended for clients that accept capability "oob".
+
+If the client sends in-band commands faster than the server can
+execute them, the server will eventually drop commands to limit the
+queue length. The sever sends event COMMAND_DROPPED then.
+
+Only a few commands support out-of-band execution. The ones that do
+have "allow-oob": true in output of query-qmp-schema.
2.4 Commands Responses
----------------------
@@ -223,12 +236,13 @@ This section provides some examples of real QMP usage, in all of them
3.1 Server greeting
-------------------
-S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
- "package": ""}, "capabilities": []}}
+S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3},
+ "package": "v3.0.0"}, "capabilities": ["oob"] } }
+
+3.2 Capabilities negotiation
+----------------------------
-3.2 Client QMP negotiation
---------------------------
-C: { "execute": "qmp_capabilities" }
+C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } }
S: { "return": {}}
3.3 Simple 'stop' execution
@@ -255,6 +269,15 @@ S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
"event": "POWERDOWN" }
+3.7 Out-of-band execution
+-------------------------
+
+C: { "exec-oob": "migrate-pause", "id": 42 }
+S: { "id": 42,
+ "error": { "class": "GenericError",
+ "desc": "migrate-pause is currently only supported during postcopy-active state" } }
+
+
4. Capabilities Negotiation
===========================
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index a482b6e56b..70639f656a 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -426,6 +426,7 @@ STEXI
Show which guest mouse is receiving events.
ETEXI
+#if defined(CONFIG_VNC)
{
.name = "vnc",
.args_type = "",
@@ -433,6 +434,7 @@ ETEXI
.help = "show the vnc server status",
.cmd = hmp_info_vnc,
},
+#endif
STEXI
@item info vnc
diff --git a/hmp.c b/hmp.c
index fe4477a8fb..4555b503ac 100644
--- a/hmp.c
+++ b/hmp.c
@@ -616,6 +616,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
qapi_free_BlockStatsList(stats_list);
}
+#ifdef CONFIG_VNC
/* Helper for hmp_info_vnc_clients, _servers */
static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
const char *name)
@@ -703,6 +704,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
qapi_free_VncInfo2List(info2l);
}
+#endif
#ifdef CONFIG_SPICE
void hmp_info_spice(Monitor *mon, const QDict *qdict)
@@ -1772,12 +1774,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &err);
}
+#ifdef CONFIG_VNC
static void hmp_change_read_arg(void *opaque, const char *password,
void *readline_opaque)
{
qmp_change_vnc_password(password, NULL);
monitor_read_command(opaque, 1);
}
+#endif
void hmp_change(Monitor *mon, const QDict *qdict)
{
@@ -1788,6 +1792,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
BlockdevChangeReadOnlyMode read_only_mode = 0;
Error *err = NULL;
+#ifdef CONFIG_VNC
if (strcmp(device, "vnc") == 0) {
if (read_only) {
monitor_printf(mon,
@@ -1802,7 +1807,9 @@ void hmp_change(Monitor *mon, const QDict *qdict)
}
}
qmp_change("vnc", target, !!arg, arg, &err);
- } else {
+ } else
+#endif
+ {
if (read_only) {
read_only_mode =
qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup,
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 31c66d4255..2b58c3505b 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -189,7 +189,7 @@ struct HDAAudioState {
static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
{
- return 2 * st->as.nchannels * st->as.freq;
+ return 2LL * st->as.nchannels * st->as.freq;
}
static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
@@ -203,6 +203,9 @@ static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
if (target_pos < -limit) {
corr = -HDA_TIMER_TICKS;
}
+ if (target_pos < -(2 * limit)) {
+ corr = -(4 * HDA_TIMER_TICKS);
+ }
if (corr == 0) {
return;
}
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index e4ecb7aa4b..8a8dbe1c00 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -102,18 +102,12 @@ static void riscv_sifive_e_init(MachineState *machine)
SiFiveEState *s = g_new0(SiFiveEState, 1);
MemoryRegion *sys_mem = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
- MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
- MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
int i;
- /* Initialize SOC */
- object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ /* Initialize SoC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_E_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
&error_abort);
- object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type",
- &error_abort);
- object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
- &error_abort);
object_property_set_bool(OBJECT(&s->soc), true, "realized",
&error_abort);
@@ -123,6 +117,49 @@ static void riscv_sifive_e_init(MachineState *machine)
memory_region_add_subregion(sys_mem,
memmap[SIFIVE_E_DTIM].base, main_mem);
+ /* Mask ROM reset vector */
+ uint32_t reset_vec[2] = {
+ 0x204002b7, /* 0x1000: lui t0,0x20400 */
+ 0x00028067, /* 0x1004: jr t0 */
+ };
+
+ /* copy in the reset vector in little_endian byte order */
+ for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
+ reset_vec[i] = cpu_to_le32(reset_vec[i]);
+ }
+ rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
+ memmap[SIFIVE_E_MROM].base, &address_space_memory);
+
+ if (machine->kernel_filename) {
+ load_kernel(machine->kernel_filename);
+ }
+}
+
+static void riscv_sifive_e_soc_init(Object *obj)
+{
+ SiFiveESoCState *s = RISCV_E_SOC(obj);
+
+ object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(obj, "cpus", OBJECT(&s->cpus),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts",
+ &error_abort);
+}
+
+static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
+{
+ const struct MemmapEntry *memmap = sifive_e_memmap;
+
+ SiFiveESoCState *s = RISCV_E_SOC(dev);
+ MemoryRegion *sys_mem = get_system_memory();
+ MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+
+ object_property_set_bool(OBJECT(&s->cpus), true, "realized",
+ &error_abort);
+
/* Mask ROM */
memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom",
memmap[SIFIVE_E_MROM].size, &error_fatal);
@@ -150,13 +187,14 @@ static void riscv_sifive_e_init(MachineState *machine)
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0",
memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size);
sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base,
- serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]);
+ serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ));
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0",
memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size);
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0",
memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size);
/* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base,
- serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */
+ serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic),
+ SIFIVE_E_UART1_IRQ)); */
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1",
memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size);
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1",
@@ -171,23 +209,6 @@ static void riscv_sifive_e_init(MachineState *machine)
memmap[SIFIVE_E_XIP].size, &error_fatal);
memory_region_set_readonly(xip_mem, true);
memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem);
-
- /* Mask ROM reset vector */
- uint32_t reset_vec[2] = {
- 0x204002b7, /* 0x1000: lui t0,0x20400 */
- 0x00028067, /* 0x1004: jr t0 */
- };
-
- /* copy in the reset vector in little_endian byte order */
- for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
- reset_vec[i] = cpu_to_le32(reset_vec[i]);
- }
- rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
- memmap[SIFIVE_E_MROM].base, &address_space_memory);
-
- if (machine->kernel_filename) {
- load_kernel(machine->kernel_filename);
- }
}
static void riscv_sifive_e_machine_init(MachineClass *mc)
@@ -198,3 +219,27 @@ static void riscv_sifive_e_machine_init(MachineClass *mc)
}
DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
+
+static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = riscv_sifive_e_soc_realize;
+ /* Reason: Uses serial_hds in realize function, thus can't be used twice */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo riscv_sifive_e_soc_type_info = {
+ .name = TYPE_RISCV_E_SOC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SiFiveESoCState),
+ .instance_init = riscv_sifive_e_soc_init,
+ .class_init = riscv_sifive_e_soc_class_init,
+};
+
+static void riscv_sifive_e_soc_register_types(void)
+{
+ type_register_static(&riscv_sifive_e_soc_type_info);
+}
+
+type_init(riscv_sifive_e_soc_register_types)
diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
index 874de2ebaf..a91aeb97ab 100644
--- a/hw/riscv/sifive_plic.c
+++ b/hw/riscv/sifive_plic.c
@@ -435,7 +435,6 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level)
static void sifive_plic_realize(DeviceState *dev, Error **errp)
{
SiFivePLICState *plic = SIFIVE_PLIC(dev);
- int i;
memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
TYPE_SIFIVE_PLIC, plic->aperture_size);
@@ -448,10 +447,7 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp)
plic->claimed = g_new0(uint32_t, plic->bitfield_words);
plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
- plic->irqs = g_new0(qemu_irq, plic->num_sources + 1);
- for (i = 0; i <= plic->num_sources; i++) {
- plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i);
- }
+ qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
}
static void sifive_plic_class_init(ObjectClass *klass, void *data)
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index c05dcbba95..3a6ffeb437 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -60,8 +60,11 @@ static const struct MemmapEntry {
[SIFIVE_U_UART0] = { 0x10013000, 0x1000 },
[SIFIVE_U_UART1] = { 0x10023000, 0x1000 },
[SIFIVE_U_DRAM] = { 0x80000000, 0x0 },
+ [SIFIVE_U_GEM] = { 0x100900FC, 0x2000 },
};
+#define GEM_REVISION 0x10070109
+
static uint64_t load_kernel(const char *kernel_filename)
{
uint64_t kernel_entry, kernel_high;
@@ -97,7 +100,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_add_subnode(fdt, "/soc");
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
- qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
+ qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
@@ -116,10 +119,10 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
- for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+ for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) {
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
- char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+ char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
SIFIVE_U_CLOCK_FREQ);
@@ -140,8 +143,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
g_free(nodename);
}
- cells = g_new0(uint32_t, s->soc.num_harts * 4);
- for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) {
nodename =
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
@@ -159,12 +162,12 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
0x0, memmap[SIFIVE_U_CLINT].base,
0x0, memmap[SIFIVE_U_CLINT].size);
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
- cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4);
g_free(cells);
g_free(nodename);
- cells = g_new0(uint32_t, s->soc.num_harts * 4);
- for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) {
nodename =
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
@@ -181,20 +184,41 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
- cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4);
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_PLIC].base,
0x0, memmap[SIFIVE_U_PLIC].size);
qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
- qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4);
+ qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35);
qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2);
qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2);
plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
g_free(cells);
g_free(nodename);
- nodename = g_strdup_printf("/uart@%lx",
+ nodename = g_strdup_printf("/soc/ethernet@%lx",
+ (long)memmap[SIFIVE_U_GEM].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "cdns,macb");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_GEM].base,
+ 0x0, memmap[SIFIVE_U_GEM].size);
+ qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
+ qemu_fdt_setprop_string(fdt, nodename, "phy-mode", "gmii");
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GEM_IRQ);
+ qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", 1);
+ qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0);
+ g_free(nodename);
+
+ nodename = g_strdup_printf("/soc/ethernet@%lx/ethernet-phy@0",
+ (long)memmap[SIFIVE_U_GEM].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0);
+ g_free(nodename);
+
+ nodename = g_strdup_printf("/soc/uart@%lx",
(long)memmap[SIFIVE_U_UART0].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0");
@@ -217,17 +241,12 @@ static void riscv_sifive_u_init(MachineState *machine)
SiFiveUState *s = g_new0(SiFiveUState, 1);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
- MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
int i;
- /* Initialize SOC */
- object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ /* Initialize SoC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_U_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
&error_abort);
- object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type",
- &error_abort);
- object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
- &error_abort);
object_property_set_bool(OBJECT(&s->soc), true, "realized",
&error_abort);
@@ -235,17 +254,11 @@ static void riscv_sifive_u_init(MachineState *machine)
memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram",
machine->ram_size, &error_fatal);
memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DRAM].base,
- main_mem);
+ main_mem);
/* create device tree */
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
- /* boot rom */
- memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom",
- memmap[SIFIVE_U_MROM].size, &error_fatal);
- memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base,
- mask_rom);
-
if (machine->kernel_filename) {
load_kernel(machine->kernel_filename);
}
@@ -284,6 +297,43 @@ static void riscv_sifive_u_init(MachineState *machine)
rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt),
memmap[SIFIVE_U_MROM].base + sizeof(reset_vec),
&address_space_memory);
+}
+
+static void riscv_sifive_u_soc_init(Object *obj)
+{
+ SiFiveUSoCState *s = RISCV_U_SOC(obj);
+
+ object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(obj, "cpus", OBJECT(&s->cpus),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts",
+ &error_abort);
+
+ object_initialize(&s->gem, sizeof(s->gem), TYPE_CADENCE_GEM);
+ qdev_set_parent_bus(DEVICE(&s->gem), sysbus_get_default());
+}
+
+static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
+{
+ SiFiveUSoCState *s = RISCV_U_SOC(dev);
+ const struct MemmapEntry *memmap = sifive_u_memmap;
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+ qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES];
+ int i;
+ Error *err = NULL;
+ NICInfo *nd = &nd_table[0];
+
+ object_property_set_bool(OBJECT(&s->cpus), true, "realized",
+ &error_abort);
+
+ /* boot rom */
+ memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom",
+ memmap[SIFIVE_U_MROM].size, &error_fatal);
+ memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base,
+ mask_rom);
/* MMIO */
s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
@@ -298,12 +348,32 @@ static void riscv_sifive_u_init(MachineState *machine)
SIFIVE_U_PLIC_CONTEXT_STRIDE,
memmap[SIFIVE_U_PLIC].size);
sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base,
- serial_hd(0), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]);
+ serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ));
/* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base,
- serial_hd(1), SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */
+ serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic),
+ SIFIVE_U_UART1_IRQ)); */
sifive_clint_create(memmap[SIFIVE_U_CLINT].base,
memmap[SIFIVE_U_CLINT].size, smp_cpus,
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+
+ for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) {
+ plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i);
+ }
+
+ if (nd->used) {
+ qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
+ qdev_set_nic_properties(DEVICE(&s->gem), nd);
+ }
+ object_property_set_int(OBJECT(&s->gem), GEM_REVISION, "revision",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->gem), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0,
+ plic_gpios[SIFIVE_U_GEM_IRQ]);
}
static void riscv_sifive_u_machine_init(MachineClass *mc)
@@ -314,3 +384,27 @@ static void riscv_sifive_u_machine_init(MachineClass *mc)
}
DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
+
+static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = riscv_sifive_u_soc_realize;
+ /* Reason: Uses serial_hds in realize function, thus can't be used twice */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo riscv_sifive_u_soc_type_info = {
+ .name = TYPE_RISCV_U_SOC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SiFiveUSoCState),
+ .instance_init = riscv_sifive_u_soc_init,
+ .class_init = riscv_sifive_u_soc_class_init,
+};
+
+static void riscv_sifive_u_soc_register_types(void)
+{
+ type_register_static(&riscv_sifive_u_soc_type_info);
+}
+
+type_init(riscv_sifive_u_soc_register_types)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 34d48993a2..aeada2498d 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -380,11 +380,11 @@ static void riscv_virt_board_init(MachineState *machine)
for (i = 0; i < VIRTIO_COUNT; i++) {
sysbus_create_simple("virtio-mmio",
memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
- SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
+ qdev_get_gpio_in(DEVICE(s->plic), VIRTIO_IRQ + i));
}
serial_mm_init(system_memory, memmap[VIRT_UART0].base,
- 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
+ 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193,
serial_hd(0), DEVICE_LITTLE_ENDIAN);
}
diff --git a/include/block/block.h b/include/block/block.h
index e5c7759a0c..bc76b1e59f 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -225,6 +225,13 @@ enum {
BLK_PERM_GRAPH_MOD = 0x10,
BLK_PERM_ALL = 0x1f,
+
+ DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ
+ | BLK_PERM_WRITE
+ | BLK_PERM_WRITE_UNCHANGED
+ | BLK_PERM_RESIZE,
+
+ DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH,
};
char *bdrv_perm_names(uint64_t perm);
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
index 288dc6adb6..259bd27c40 100644
--- a/include/block/dirty-bitmap.h
+++ b/include/block/dirty-bitmap.h
@@ -32,6 +32,7 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
Error **errp);
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
+void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap);
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs);
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs);
uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap);
diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h
index 12ad6d2ebb..7b6d8aed96 100644
--- a/include/hw/riscv/sifive_e.h
+++ b/include/hw/riscv/sifive_e.h
@@ -19,13 +19,25 @@
#ifndef HW_SIFIVE_E_H
#define HW_SIFIVE_E_H
-typedef struct SiFiveEState {
+#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc"
+#define RISCV_E_SOC(obj) \
+ OBJECT_CHECK(SiFiveESoCState, (obj), TYPE_RISCV_E_SOC)
+
+typedef struct SiFiveESoCState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
- RISCVHartArrayState soc;
+ RISCVHartArrayState cpus;
DeviceState *plic;
+} SiFiveESoCState;
+
+typedef struct SiFiveEState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ SiFiveESoCState soc;
} SiFiveEState;
enum {
diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h
index 11a5a98df1..2f2af7e686 100644
--- a/include/hw/riscv/sifive_plic.h
+++ b/include/hw/riscv/sifive_plic.h
@@ -56,7 +56,6 @@ typedef struct SiFivePLICState {
uint32_t *claimed;
uint32_t *enable;
QemuMutex lock;
- qemu_irq *irqs;
/* config */
char *hart_config;
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
index 94a390566e..e8b4d9ffa3 100644
--- a/include/hw/riscv/sifive_u.h
+++ b/include/hw/riscv/sifive_u.h
@@ -19,13 +19,28 @@
#ifndef HW_SIFIVE_U_H
#define HW_SIFIVE_U_H
-typedef struct SiFiveUState {
+#include "hw/net/cadence_gem.h"
+
+#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc"
+#define RISCV_U_SOC(obj) \
+ OBJECT_CHECK(SiFiveUSoCState, (obj), TYPE_RISCV_U_SOC)
+
+typedef struct SiFiveUSoCState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
- RISCVHartArrayState soc;
+ RISCVHartArrayState cpus;
DeviceState *plic;
+ CadenceGEMState gem;
+} SiFiveUSoCState;
+
+typedef struct SiFiveUState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ SiFiveUSoCState soc;
void *fdt;
int fdt_size;
} SiFiveUState;
@@ -37,12 +52,14 @@ enum {
SIFIVE_U_PLIC,
SIFIVE_U_UART0,
SIFIVE_U_UART1,
- SIFIVE_U_DRAM
+ SIFIVE_U_DRAM,
+ SIFIVE_U_GEM
};
enum {
SIFIVE_U_UART0_IRQ = 3,
- SIFIVE_U_UART1_IRQ = 4
+ SIFIVE_U_UART1_IRQ = 4,
+ SIFIVE_U_GEM_IRQ = 0x35
};
enum {
diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index b366bb48bd..4e2e749faf 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -41,15 +41,15 @@ void qmp_register_command(QmpCommandList *cmds, const char *name,
QmpCommandFunc *fn, QmpCommandOptions options);
void qmp_unregister_command(QmpCommandList *cmds, const char *name);
QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name);
-QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request);
void qmp_disable_command(QmpCommandList *cmds, const char *name);
void qmp_enable_command(QmpCommandList *cmds, const char *name);
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);
+QDict *qmp_error_response(Error *err);
+QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
+ bool allow_oob);
bool qmp_is_oob(QDict *dict);
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h
index b274ac3a86..43b2ce2f33 100644
--- a/include/qapi/qmp/qjson.h
+++ b/include/qapi/qmp/qjson.h
@@ -19,6 +19,8 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
GCC_FMT_ATTR(1, 0);
+QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2);
+
QString *qobject_to_json(const QObject *obj);
QString *qobject_to_json_pretty(const QObject *obj);
diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs
index 59a5c17354..b5dfb71f25 100644
--- a/linux-user/Makefile.objs
+++ b/linux-user/Makefile.objs
@@ -1,7 +1,7 @@
obj-y = main.o syscall.o strace.o mmap.o signal.o \
elfload.o linuxload.o uaccess.o uname.o \
safe-syscall.o $(TARGET_ABI_DIR)/signal.o \
- $(TARGET_ABI_DIR)/cpu_loop.o
+ $(TARGET_ABI_DIR)/cpu_loop.o exit.o
obj-$(TARGET_HAS_BFLT) += flatload.o
obj-$(TARGET_I386) += vm86.o
diff --git a/linux-user/exit.c b/linux-user/exit.c
new file mode 100644
index 0000000000..14e94e28fa
--- /dev/null
+++ b/linux-user/exit.c
@@ -0,0 +1,35 @@
+/*
+ * exit support for qemu
+ *
+ * Copyright (c) 2018 Alex Bennée <alex.bennee@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu.h"
+
+#ifdef CONFIG_GCOV
+extern void __gcov_dump(void);
+#endif
+
+void preexit_cleanup(CPUArchState *env, int code)
+{
+#ifdef TARGET_GPROF
+ _mcleanup();
+#endif
+#ifdef CONFIG_GCOV
+ __gcov_dump();
+#endif
+ gdb_exit(env, code);
+}
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 793cd4df04..bb85c81aa4 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -623,6 +623,14 @@ static inline int is_error(abi_long ret)
return (abi_ulong)ret >= (abi_ulong)(-4096);
}
+/**
+ * preexit_cleanup: housekeeping before the guest exits
+ *
+ * env: the CPU state
+ * code: the exit code
+ */
+void preexit_cleanup(CPUArchState *env, int code);
+
/* Include target-specific struct and function definitions;
* they may need access to the target-independent structures
* above, so include them last.
diff --git a/linux-user/strace.list b/linux-user/strace.list
index 2bc5ba04d4..ff8bb19f5f 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -59,10 +59,10 @@
{ TARGET_NR_cacheflush, "cacheflush" , NULL, NULL, NULL },
#endif
#ifdef TARGET_NR_capget
-{ TARGET_NR_capget, "capget" , NULL, NULL, NULL },
+{ TARGET_NR_capget, "capget" , "%s(%p,%p)", NULL, NULL },
#endif
#ifdef TARGET_NR_capset
-{ TARGET_NR_capset, "capset" , NULL, NULL, NULL },
+{ TARGET_NR_capset, "capset" , "%s(%p,%p)", NULL, NULL },
#endif
#ifdef TARGET_NR_chdir
{ TARGET_NR_chdir, "chdir" , NULL, print_chdir, NULL },
@@ -1107,7 +1107,7 @@
{ TARGET_NR_recvmmsg, "recvmmsg" , NULL, NULL, NULL },
#endif
#ifdef TARGET_NR_recvmsg
-{ TARGET_NR_recvmsg, "recvmsg" , NULL, NULL, NULL },
+{ TARGET_NR_recvmsg, "recvmsg" , "%s(%d,%p,%#x)", NULL, NULL },
#endif
#ifdef TARGET_NR_remap_file_pages
{ TARGET_NR_remap_file_pages, "remap_file_pages" , NULL, NULL, NULL },
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 2117fb13b4..5822e03e28 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2522,6 +2522,8 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr)
case QEMU_IFLA_NUM_VF:
case QEMU_IFLA_GSO_MAX_SEGS:
case QEMU_IFLA_GSO_MAX_SIZE:
+ case QEMU_IFLA_CARRIER_UP_COUNT:
+ case QEMU_IFLA_CARRIER_DOWN_COUNT:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
@@ -3017,6 +3019,8 @@ static abi_long do_setsockopt(int sockfd, int level, int optname,
case IPV6_V6ONLY:
case IPV6_RECVPKTINFO:
case IPV6_UNICAST_HOPS:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
case IPV6_RECVERR:
case IPV6_RECVHOPLIMIT:
case IPV6_2292HOPLIMIT:
@@ -8018,10 +8022,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
}
cpu_list_unlock();
-#ifdef TARGET_GPROF
- _mcleanup();
-#endif
- gdb_exit(cpu_env, arg1);
+ preexit_cleanup(cpu_env, arg1);
_exit(arg1);
ret = 0; /* avoid warning */
break;
@@ -10127,10 +10128,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#ifdef __NR_exit_group
/* new thread calls */
case TARGET_NR_exit_group:
-#ifdef TARGET_GPROF
- _mcleanup();
-#endif
- gdb_exit(cpu_env, arg1);
+ preexit_cleanup(cpu_env, arg1);
ret = get_errno(exit_group(arg1));
break;
#endif
diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c
index 3bafbbdc4c..477826330c 100644
--- a/migration/block-dirty-bitmap.c
+++ b/migration/block-dirty-bitmap.c
@@ -511,7 +511,7 @@ void dirty_bitmap_mig_before_vm_start(void)
DirtyBitmapLoadBitmapState *b = item->data;
if (b->migrated) {
- bdrv_enable_dirty_bitmap(b->bitmap);
+ bdrv_enable_dirty_bitmap_locked(b->bitmap);
} else {
bdrv_dirty_bitmap_enable_successor(b->bitmap);
}
@@ -547,7 +547,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s)
if (enabled_bitmaps == NULL) {
/* in postcopy */
bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort);
- bdrv_enable_dirty_bitmap(s->bitmap);
+ bdrv_enable_dirty_bitmap_locked(s->bitmap);
} else {
/* target not started, successor must be empty */
int64_t count = bdrv_get_dirty_count(s->bitmap);
diff --git a/monitor.c b/monitor.c
index 3a0ea0c602..3c9c97b73f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -169,14 +169,16 @@ typedef struct {
JSONMessageParser parser;
/*
* When a client connects, we're in capabilities negotiation mode.
- * When command qmp_capabilities succeeds, we go into command
- * mode.
+ * @commands is &qmp_cap_negotiation_commands then. When command
+ * qmp_capabilities succeeds, we go into command mode, and
+ * @command becomes &qmp_commands.
*/
QmpCommandList *commands;
- bool qmp_caps[QMP_CAPABILITY__MAX];
+ bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
+ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
/*
- * Protects qmp request/response queue. Please take monitor_lock
- * first when used together.
+ * Protects qmp request/response queue.
+ * Take monitor_lock first when you need both.
*/
QemuMutex qmp_queue_lock;
/* Input queue that holds all the parsed QMP requests */
@@ -207,11 +209,11 @@ struct Monitor {
int flags;
int suspend_cnt; /* Needs to be accessed atomically */
bool skip_flush;
- bool use_io_thr;
+ bool use_io_thread;
/*
* State used only in the thread "owning" the monitor.
- * If @use_io_thr, this is mon_global.mon_iothread.
+ * If @use_io_thread, this is @mon_iothread.
* Else, it's the main thread.
* These members can be safely accessed without locks.
*/
@@ -231,7 +233,7 @@ struct Monitor {
QemuMutex mon_lock;
/*
- * Fields that are protected by the per-monitor lock.
+ * Members that are protected by the per-monitor lock
*/
QLIST_HEAD(, mon_fd_t) fds;
QString *outbuf;
@@ -240,22 +242,26 @@ struct Monitor {
int mux_out;
};
-/* Let's add monitor global variables to this struct. */
-static struct {
- IOThread *mon_iothread;
- /* Bottom half to dispatch the requests received from IO thread */
- QEMUBH *qmp_dispatcher_bh;
- /* Bottom half to deliver the responses back to clients */
- QEMUBH *qmp_respond_bh;
-} mon_global;
+/* Shared monitor I/O thread */
+IOThread *mon_iothread;
+
+/* Bottom half to dispatch the requests received from I/O thread */
+QEMUBH *qmp_dispatcher_bh;
+
+/* Bottom half to deliver the responses back to clients */
+QEMUBH *qmp_respond_bh;
struct QMPRequest {
/* Owner of the request */
Monitor *mon;
/* "id" field of the request */
QObject *id;
- /* Request object to be handled */
+ /*
+ * Request object to be handled or Error to be reported
+ * (exactly one of them is non-null)
+ */
QObject *req;
+ Error *err;
/*
* Whether we need to resume the monitor afterward. This flag is
* used to emulate the old QMP server behavior that the current
@@ -298,9 +304,9 @@ static inline bool monitor_is_qmp(const Monitor *mon)
}
/**
- * Whether @mon is using readline? Note: not all HMP monitors use
- * readline, e.g., gdbserver has a non-interactive HMP monitor, so
- * readline is not used there.
+ * Is @mon is using readline?
+ * Note: not all HMP monitors use readline, e.g., gdbserver has a
+ * non-interactive HMP monitor, so readline is not used there.
*/
static inline bool monitor_uses_readline(const Monitor *mon)
{
@@ -314,14 +320,12 @@ static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
/*
* Return the clock to use for recording an event's time.
+ * It's QEMU_CLOCK_REALTIME, except for qtests it's
+ * QEMU_CLOCK_VIRTUAL, to support testing rate limits.
* Beware: result is invalid before configure_accelerator().
*/
static inline QEMUClockType monitor_get_event_clock(void)
{
- /*
- * This allows us to perform tests on the monitor queues to verify
- * that the rate limits are enforced.
- */
return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME;
}
@@ -360,10 +364,11 @@ static void qmp_request_free(QMPRequest *req)
{
qobject_unref(req->id);
qobject_unref(req->req);
+ error_free(req->err);
g_free(req);
}
-/* Must with the mon->qmp.qmp_queue_lock held */
+/* Caller must hold mon->qmp.qmp_queue_lock */
static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
{
while (!g_queue_is_empty(mon->qmp.qmp_requests)) {
@@ -371,11 +376,11 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
}
}
-/* Must with the mon->qmp.qmp_queue_lock held */
+/* Caller must hold the mon->qmp.qmp_queue_lock */
static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon)
{
while (!g_queue_is_empty(mon->qmp.qmp_responses)) {
- qobject_unref((QObject *)g_queue_pop_head(mon->qmp.qmp_responses));
+ qobject_unref((QDict *)g_queue_pop_head(mon->qmp.qmp_responses));
}
}
@@ -402,7 +407,7 @@ static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
return FALSE;
}
-/* Called with mon->mon_lock held. */
+/* Caller must hold mon->mon_lock */
static void monitor_flush_locked(Monitor *mon)
{
int rc;
@@ -499,9 +504,9 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...)
return 0;
}
-static void monitor_json_emitter_raw(Monitor *mon,
- QObject *data)
+static void qmp_send_response(Monitor *mon, QDict *rsp)
{
+ QObject *data = QOBJECT(rsp);
QString *json;
json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
@@ -514,37 +519,35 @@ static void monitor_json_emitter_raw(Monitor *mon,
qobject_unref(json);
}
-static void monitor_json_emitter(Monitor *mon, QObject *data)
+static void qmp_queue_response(Monitor *mon, QDict *rsp)
{
- if (mon->use_io_thr) {
+ if (mon->use_io_thread) {
/*
- * If using IO thread, we need to queue the item so that IO
- * thread will do the rest for us. Take refcount so that
- * caller won't free the data (which will be finally freed in
- * responder thread).
+ * Push a reference to the response queue. The I/O thread
+ * drains that queue and emits.
*/
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
- g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(data));
+ g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(rsp));
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
- qemu_bh_schedule(mon_global.qmp_respond_bh);
+ qemu_bh_schedule(qmp_respond_bh);
} else {
/*
- * If not using monitor IO thread, then we are in main thread.
- * Do the emission right away.
+ * Not using monitor I/O thread, i.e. we are in the main thread.
+ * Emit right away.
*/
- monitor_json_emitter_raw(mon, data);
+ qmp_send_response(mon, rsp);
}
}
struct QMPResponse {
Monitor *mon;
- QObject *data;
+ QDict *data;
};
typedef struct QMPResponse QMPResponse;
-static QObject *monitor_qmp_response_pop_one(Monitor *mon)
+static QDict *monitor_qmp_response_pop_one(Monitor *mon)
{
- QObject *data;
+ QDict *data;
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
data = g_queue_pop_head(mon->qmp.qmp_responses);
@@ -555,10 +558,10 @@ static QObject *monitor_qmp_response_pop_one(Monitor *mon)
static void monitor_qmp_response_flush(Monitor *mon)
{
- QObject *data;
+ QDict *data;
while ((data = monitor_qmp_response_pop_one(mon))) {
- monitor_json_emitter_raw(mon, data);
+ qmp_send_response(mon, data);
qobject_unref(data);
}
}
@@ -570,7 +573,7 @@ static void monitor_qmp_response_flush(Monitor *mon)
static bool monitor_qmp_response_pop_any(QMPResponse *response)
{
Monitor *mon;
- QObject *data = NULL;
+ QDict *data = NULL;
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH(mon, &mon_list, entry) {
@@ -590,7 +593,7 @@ static void monitor_qmp_bh_responder(void *opaque)
QMPResponse response;
while (monitor_qmp_response_pop_any(&response)) {
- monitor_json_emitter_raw(response.mon, response.data);
+ qmp_send_response(response.mon, response.data);
qobject_unref(response.data);
}
}
@@ -606,8 +609,9 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
};
/*
- * Emits the event to every monitor instance, @event is only used for trace
- * Called with monitor_lock held.
+ * Broadcast an event to all monitors.
+ * @qdict is the event object. Its member "event" must match @event.
+ * Caller must hold monitor_lock.
*/
static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
{
@@ -617,7 +621,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
QTAILQ_FOREACH(mon, &mon_list, entry) {
if (monitor_is_qmp(mon)
&& mon->qmp.commands != &qmp_cap_negotiation_commands) {
- monitor_json_emitter(mon, QOBJECT(qdict));
+ qmp_queue_response(mon, qdict);
}
}
}
@@ -762,7 +766,7 @@ static void monitor_qapi_event_init(void)
static void handle_hmp_command(Monitor *mon, const char *cmdline);
static void monitor_data_init(Monitor *mon, bool skip_flush,
- bool use_io_thr)
+ bool use_io_thread)
{
memset(mon, 0, sizeof(Monitor));
qemu_mutex_init(&mon->mon_lock);
@@ -771,7 +775,7 @@ static void monitor_data_init(Monitor *mon, bool skip_flush,
/* Use *mon_cmds by default. */
mon->cmd_table = mon_cmds;
mon->skip_flush = skip_flush;
- mon->use_io_thr = use_io_thr;
+ mon->use_io_thread = use_io_thread;
mon->qmp.qmp_requests = g_queue_new();
mon->qmp.qmp_responses = g_queue_new();
}
@@ -976,8 +980,7 @@ static int parse_cmdline(const char *cmdline,
}
/*
- * Returns true if the command can be executed in preconfig mode
- * i.e. it has the 'p' flag.
+ * Can command @cmd be executed in preconfig state?
*/
static bool cmd_can_preconfig(const mon_cmd_t *cmd)
{
@@ -1191,9 +1194,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
*/
static void qmp_unregister_commands_hack(void)
{
-#ifndef CONFIG_SPICE
- qmp_unregister_command(&qmp_commands, "query-spice");
-#endif
#ifndef CONFIG_REPLICATION
qmp_unregister_command(&qmp_commands, "xen-set-replication");
qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
@@ -1249,96 +1249,56 @@ static void monitor_init_qmp_commands(void)
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}
-static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap)
-{
- return mon->qmp.qmp_caps[cap];
-}
-
static bool qmp_oob_enabled(Monitor *mon)
{
- return qmp_cap_enabled(mon, QMP_CAPABILITY_OOB);
+ return mon->qmp.capab[QMP_CAPABILITY_OOB];
}
-static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list,
- Error **errp)
-{
- for (; list; list = list->next) {
- assert(list->value < QMP_CAPABILITY__MAX);
- switch (list->value) {
- case QMP_CAPABILITY_OOB:
- if (!mon->use_io_thr) {
- /*
- * Out-Of-Band only works with monitors that are
- * running on dedicated IOThread.
- */
- error_setg(errp, "This monitor does not support "
- "Out-Of-Band (OOB)");
- return;
- }
- break;
- default:
- break;
- }
- }
-}
-
-/* This function should only be called after capabilities are checked. */
-static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list)
+static void monitor_qmp_caps_reset(Monitor *mon)
{
- for (; list; list = list->next) {
- mon->qmp.qmp_caps[list->value] = true;
- }
+ memset(mon->qmp.capab_offered, 0, sizeof(mon->qmp.capab_offered));
+ memset(mon->qmp.capab, 0, sizeof(mon->qmp.capab));
+ mon->qmp.capab_offered[QMP_CAPABILITY_OOB] = mon->use_io_thread;
}
/*
- * Return true if check successful, or false otherwise. When false is
- * returned, detailed error will be in errp if provided.
+ * Accept QMP capabilities in @list for @mon.
+ * On success, set mon->qmp.capab[], and return true.
+ * On error, set @errp, and return false.
*/
-static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
+static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list,
+ Error **errp)
{
- const char *command;
- QmpCommand *cmd;
+ GString *unavailable = NULL;
+ bool capab[QMP_CAPABILITY__MAX];
- command = qdict_get_try_str(req, "execute");
- if (!command) {
- error_setg(errp, "Command field 'execute' missing");
- return false;
- }
+ memset(capab, 0, sizeof(capab));
- cmd = qmp_find_command(mon->qmp.commands, command);
- if (!cmd) {
- if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "Expecting capabilities negotiation "
- "with 'qmp_capabilities'");
- } else {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "The command %s has not been found", command);
+ for (; list; list = list->next) {
+ if (!mon->qmp.capab_offered[list->value]) {
+ if (!unavailable) {
+ unavailable = g_string_new(QMPCapability_str(list->value));
+ } else {
+ g_string_append_printf(unavailable, ", %s",
+ QMPCapability_str(list->value));
+ }
}
- return false;
+ capab[list->value] = true;
}
- if (qmp_is_oob(req)) {
- if (!qmp_oob_enabled(mon)) {
- error_setg(errp, "Please enable Out-Of-Band first "
- "for the session during capabilities negotiation");
- return false;
- }
- if (!(cmd->options & QCO_ALLOW_OOB)) {
- error_setg(errp, "The command %s does not support OOB",
- command);
- return false;
- }
+ if (unavailable) {
+ error_setg(errp, "Capability %s not available", unavailable->str);
+ g_string_free(unavailable, true);
+ return false;
}
+ memcpy(mon->qmp.capab, capab, sizeof(capab));
return true;
}
void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
Error **errp)
{
- Error *local_err = NULL;
-
if (cur_mon->qmp.commands == &qmp_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Capabilities negotiation is already complete, command "
@@ -1346,19 +1306,8 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
return;
}
- /* Enable QMP capabilities provided by the client if applicable. */
- if (has_enable) {
- qmp_caps_check(cur_mon, enable, &local_err);
- if (local_err) {
- /*
- * Failed check on any of the capabilities will fail the
- * entire command (and thus not apply any of the other
- * capabilities that were also requested).
- */
- error_propagate(errp, local_err);
- return;
- }
- qmp_caps_apply(cur_mon, enable);
+ if (!qmp_caps_accept(cur_mon, enable, errp)) {
+ return;
}
cur_mon->qmp.commands = &qmp_commands;
@@ -2267,7 +2216,7 @@ void qmp_getfd(const char *fdname, Error **errp)
tmp_fd = monfd->fd;
monfd->fd = fd;
qemu_mutex_unlock(&cur_mon->mon_lock);
- /* Make sure close() is out of critical section */
+ /* Make sure close() is outside critical section */
close(tmp_fd);
return;
}
@@ -2296,7 +2245,7 @@ void qmp_closefd(const char *fdname, Error **errp)
g_free(monfd->name);
g_free(monfd);
qemu_mutex_unlock(&cur_mon->mon_lock);
- /* Make sure close() is out of critical section */
+ /* Make sure close() is outside critical section */
close(tmp_fd);
return;
}
@@ -4140,78 +4089,53 @@ static int monitor_can_read(void *opaque)
}
/*
- * 1. This function takes ownership of rsp, err, and id.
- * 2. rsp, err, and id may be NULL.
- * 3. If err != NULL then rsp must be NULL.
+ * Emit QMP response @rsp with ID @id to @mon.
+ * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
+ * Nothing is emitted then.
*/
-static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
- Error *err, QObject *id)
+static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id)
{
- QDict *qdict = NULL;
-
- if (err) {
- assert(!rsp);
- qdict = qdict_new();
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
- rsp = QOBJECT(qdict);
- }
-
if (rsp) {
if (id) {
- qdict_put_obj(qobject_to(QDict, rsp), "id", qobject_ref(id));
+ qdict_put_obj(rsp, "id", qobject_ref(id));
}
- monitor_json_emitter(mon, rsp);
+ qmp_queue_response(mon, rsp);
}
-
- qobject_unref(id);
- qobject_unref(rsp);
}
-/*
- * Dispatch one single QMP request. The function will free the req_obj
- * and objects inside it before return.
- */
-static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
+static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id)
{
- Monitor *mon, *old_mon;
- QObject *req, *rsp = NULL, *id;
- bool need_resume;
-
- req = req_obj->req;
- mon = req_obj->mon;
- id = req_obj->id;
- need_resume = req_obj->need_resume;
-
- g_free(req_obj);
-
- if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
- QString *req_json = qobject_to_json(req);
- trace_handle_qmp_command(mon, qstring_get_str(req_json));
- qobject_unref(req_json);
- }
+ Monitor *old_mon;
+ QDict *rsp;
+ QDict *error;
old_mon = cur_mon;
cur_mon = mon;
- rsp = qmp_dispatch(mon->qmp.commands, req);
+ rsp = qmp_dispatch(mon->qmp.commands, req, qmp_oob_enabled(mon));
cur_mon = old_mon;
- /* Respond if necessary */
- monitor_qmp_respond(mon, rsp, NULL, id);
-
- /* This pairs with the monitor_suspend() in handle_qmp_command(). */
- if (need_resume) {
- monitor_resume(mon);
+ if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
+ error = qdict_get_qdict(rsp, "error");
+ if (error
+ && !g_strcmp0(qdict_get_try_str(error, "class"),
+ QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
+ /* Provide a more useful error message */
+ qdict_del(error, "desc");
+ qdict_put_str(error, "desc", "Expecting capabilities negotiation"
+ " with 'qmp_capabilities'");
+ }
}
- qobject_unref(req);
+ monitor_qmp_respond(mon, rsp, id);
+ qobject_unref(rsp);
}
/*
- * Pop one QMP request from monitor queues, return NULL if not found.
+ * Pop a QMP request from a monitor request queue.
+ * Return the request, or NULL all request queues are empty.
* We are using round-robin fashion to pop the request, to avoid
* processing commands only on a very busy monitor. To achieve that,
* when we process one request on a specific monitor, we put that
@@ -4250,13 +4174,30 @@ static QMPRequest *monitor_qmp_requests_pop_any(void)
static void monitor_qmp_bh_dispatcher(void *data)
{
QMPRequest *req_obj = monitor_qmp_requests_pop_any();
+ QDict *rsp;
- if (req_obj) {
+ if (!req_obj) {
+ return;
+ }
+
+ if (req_obj->req) {
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
- monitor_qmp_dispatch_one(req_obj);
- /* Reschedule instead of looping so the main loop stays responsive */
- qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
+ monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id);
+ } else {
+ assert(req_obj->err);
+ rsp = qmp_error_response(req_obj->err);
+ monitor_qmp_respond(req_obj->mon, rsp, NULL);
+ qobject_unref(rsp);
+ }
+
+ if (req_obj->need_resume) {
+ /* Pairs with the monitor_suspend() in handle_qmp_command() */
+ monitor_resume(req_obj->mon);
}
+ qmp_request_free(req_obj);
+
+ /* Reschedule instead of looping so the main loop stays responsive */
+ qemu_bh_schedule(qmp_dispatcher_bh);
}
#define QMP_REQ_QUEUE_LEN_MAX (8)
@@ -4264,7 +4205,7 @@ static void monitor_qmp_bh_dispatcher(void *data)
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
{
QObject *req, *id = NULL;
- QDict *qdict = NULL;
+ QDict *qdict;
MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser);
Monitor *mon = container_of(mon_qmp, Monitor, qmp);
Error *err = NULL;
@@ -4275,46 +4216,34 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
/* json_parser_parse_err() sucks: can fail without setting @err */
error_setg(&err, QERR_JSON_PARSING);
}
- if (err) {
- goto err;
- }
- /* Check against the request in general layout */
- qdict = qmp_dispatch_check_obj(req, &err);
- if (!qdict) {
- goto err;
- }
+ qdict = qobject_to(QDict, req);
+ if (qdict) {
+ id = qobject_ref(qdict_get(qdict, "id"));
+ qdict_del(qdict, "id");
+ } /* else will fail qmp_dispatch() */
- /* Check against OOB specific */
- if (!qmp_cmd_oob_check(mon, qdict, &err)) {
- goto err;
+ if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
+ QString *req_json = qobject_to_json(req);
+ trace_handle_qmp_command(mon, qstring_get_str(req_json));
+ qobject_unref(req_json);
}
- id = qdict_get(qdict, "id");
-
- /* When OOB is enabled, the "id" field is mandatory. */
- if (qmp_oob_enabled(mon) && !id) {
- error_setg(&err, "Out-Of-Band capability requires that "
- "every command contains an 'id' field");
- goto err;
+ if (qdict && qmp_is_oob(qdict)) {
+ /* OOB commands are executed immediately */
+ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id)
+ ?: "");
+ monitor_qmp_dispatch(mon, req, id);
+ return;
}
req_obj = g_new0(QMPRequest, 1);
req_obj->mon = mon;
- req_obj->id = qobject_ref(id);
+ req_obj->id = id;
req_obj->req = req;
+ req_obj->err = err;
req_obj->need_resume = false;
- qdict_del(qdict, "id");
-
- if (qmp_is_oob(qdict)) {
- /* Out-Of-Band (OOB) requests are executed directly in parser. */
- trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id)
- ?: "");
- monitor_qmp_dispatch_one(req_obj);
- return;
- }
-
/* Protect qmp_requests and fetching its length. */
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
@@ -4331,6 +4260,12 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
/* Drop the request if queue is full. */
if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) {
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ /*
+ * FIXME @id's scope is just @mon, and broadcasting it is
+ * wrong. If another monitor's client has a command with
+ * the same ID in flight, the event will incorrectly claim
+ * that command was dropped.
+ */
qapi_event_send_command_dropped(id,
COMMAND_DROP_REASON_QUEUE_FULL,
&error_abort);
@@ -4348,12 +4283,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
/* Kick the dispatcher routine */
- qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
- return;
-
-err:
- monitor_qmp_respond(mon, NULL, err, NULL);
- qobject_unref(req);
+ qemu_bh_schedule(qmp_dispatcher_bh);
}
static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
@@ -4403,10 +4333,10 @@ int monitor_suspend(Monitor *mon)
if (monitor_is_qmp(mon)) {
/*
- * Kick iothread to make sure this takes effect. It'll be
+ * Kick I/O thread to make sure this takes effect. It'll be
* evaluated again in prepare() of the watch object.
*/
- aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
+ aio_notify(iothread_get_aio_context(mon_iothread));
}
trace_monitor_suspend(mon, 1);
@@ -4422,11 +4352,11 @@ void monitor_resume(Monitor *mon)
if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
if (monitor_is_qmp(mon)) {
/*
- * For QMP monitors that are running in IOThread, let's
- * kick the thread in case it's sleeping.
+ * For QMP monitors that are running in the I/O thread,
+ * let's kick the thread in case it's sleeping.
*/
- if (mon->use_io_thr) {
- aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
+ if (mon->use_io_thread) {
+ aio_notify(iothread_get_aio_context(mon_iothread));
}
} else {
assert(mon->rs);
@@ -4436,7 +4366,7 @@ void monitor_resume(Monitor *mon)
trace_monitor_suspend(mon, -1);
}
-static QObject *get_qmp_greeting(Monitor *mon)
+static QDict *qmp_greeting(Monitor *mon)
{
QList *cap_list = qlist_new();
QObject *ver = NULL;
@@ -4445,33 +4375,27 @@ static QObject *get_qmp_greeting(Monitor *mon)
qmp_marshal_query_version(NULL, &ver, NULL);
for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
- if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) {
- /* Monitors that are not using IOThread won't support OOB */
- continue;
+ if (mon->qmp.capab_offered[cap]) {
+ qlist_append_str(cap_list, QMPCapability_str(cap));
}
- qlist_append_str(cap_list, QMPCapability_str(cap));
}
- return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
- ver, cap_list);
-}
-
-static void monitor_qmp_caps_reset(Monitor *mon)
-{
- memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps));
+ return qdict_from_jsonf_nofail(
+ "{'QMP': {'version': %p, 'capabilities': %p}}",
+ ver, cap_list);
}
static void monitor_qmp_event(void *opaque, int event)
{
- QObject *data;
+ QDict *data;
Monitor *mon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
mon->qmp.commands = &qmp_cap_negotiation_commands;
monitor_qmp_caps_reset(mon);
- data = get_qmp_greeting(mon);
- monitor_json_emitter(mon, data);
+ data = qmp_greeting(mon);
+ qmp_queue_response(mon, data);
qobject_unref(data);
mon_refcount++;
break;
@@ -4564,36 +4488,35 @@ static void sortcmdlist(void)
static GMainContext *monitor_get_io_context(void)
{
- return iothread_get_g_main_context(mon_global.mon_iothread);
+ return iothread_get_g_main_context(mon_iothread);
}
static AioContext *monitor_get_aio_context(void)
{
- return iothread_get_aio_context(mon_global.mon_iothread);
+ return iothread_get_aio_context(mon_iothread);
}
static void monitor_iothread_init(void)
{
- mon_global.mon_iothread = iothread_create("mon_iothread",
- &error_abort);
+ mon_iothread = iothread_create("mon_iothread", &error_abort);
/*
- * This MUST be on main loop thread since we have commands that
- * have assumption to be run on main loop thread. It would be
- * nice that one day we can remove this assumption in the future.
+ * The dispatcher BH must run in the main loop thread, since we
+ * have commands assuming that context. It would be nice to get
+ * rid of those assumptions.
*/
- mon_global.qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(),
- monitor_qmp_bh_dispatcher,
- NULL);
+ qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(),
+ monitor_qmp_bh_dispatcher,
+ NULL);
/*
- * Unlike the dispatcher BH, this must be run on the monitor IO
- * thread, so that monitors that are using IO thread will make
- * sure read/write operations are all done on the IO thread.
+ * The responder BH must be run in the monitor I/O thread, so that
+ * monitors that are using the I/O thread have their output
+ * written by the I/O thread.
*/
- mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(),
- monitor_qmp_bh_responder,
- NULL);
+ qmp_respond_bh = aio_bh_new(monitor_get_aio_context(),
+ monitor_qmp_bh_responder,
+ NULL);
}
void monitor_init_globals(void)
@@ -4658,16 +4581,12 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
Monitor *mon = opaque;
GMainContext *context;
- if (mon->use_io_thr) {
- /*
- * When use_io_thr is set, we use the global shared dedicated
- * IO thread for this monitor to handle input/output.
- */
+ if (mon->use_io_thread) {
+ /* Use @mon_iothread context */
context = monitor_get_io_context();
- /* We should have inited globals before reaching here. */
assert(context);
} else {
- /* The default main loop, which is the main thread */
+ /* Use default main loop context */
context = NULL;
}
@@ -4684,12 +4603,12 @@ void monitor_init(Chardev *chr, int flags)
if (use_oob) {
if (CHARDEV_IS_MUX(chr)) {
- error_report("Monitor Out-Of-Band is not supported with "
+ error_report("Monitor out-of-band is not supported with "
"MUX typed chardev backend");
exit(1);
}
if (use_readline) {
- error_report("Monitor Out-Of-band is only supported by QMP");
+ error_report("Monitor out-of-band is only supported by QMP");
exit(1);
}
}
@@ -4709,7 +4628,7 @@ void monitor_init(Chardev *chr, int flags)
if (monitor_is_qmp(mon)) {
qemu_chr_fe_set_echo(&mon->chr, true);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
- if (mon->use_io_thr) {
+ if (mon->use_io_thread) {
/*
* Make sure the old iowatch is gone. It's possible when
* e.g. the chardev is in client mode, with wait=on.
@@ -4717,15 +4636,12 @@ void monitor_init(Chardev *chr, int flags)
remove_fd_in_watch(chr);
/*
* We can't call qemu_chr_fe_set_handlers() directly here
- * since during the procedure the chardev will be active
- * and running in monitor iothread, while we'll still do
- * something before returning from it, which is a possible
- * race too. To avoid that, we just create a BH to setup
- * the handlers.
+ * since chardev might be running in the monitor I/O
+ * thread. Schedule a bottom half.
*/
aio_bh_schedule_oneshot(monitor_get_aio_context(),
monitor_qmp_setup_handlers_bh, mon);
- /* We'll add this to mon_list in the BH when setup done */
+ /* The bottom half will add @mon to @mon_list */
return;
} else {
qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
@@ -4745,22 +4661,20 @@ void monitor_cleanup(void)
Monitor *mon, *next;
/*
- * We need to explicitly stop the iothread (but not destroy it),
- * cleanup the monitor resources, then destroy the iothread since
+ * We need to explicitly stop the I/O thread (but not destroy it),
+ * clean up the monitor resources, then destroy the I/O thread since
* we need to unregister from chardev below in
* monitor_data_destroy(), and chardev is not thread-safe yet
*/
- iothread_stop(mon_global.mon_iothread);
+ iothread_stop(mon_iothread);
/*
- * After we have IOThread to send responses, it's possible that
- * when we stop the IOThread there are still replies queued in the
- * responder queue. Flush all of them. Note that even after this
- * flush it's still possible that out buffer is not flushed.
- * It'll be done in below monitor_flush() as the last resort.
+ * Flush all response queues. Note that even after this flush,
+ * data may remain in output buffers.
*/
monitor_qmp_bh_responder(NULL);
+ /* Flush output buffers and destroy monitors */
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) {
QTAILQ_REMOVE(&mon_list, mon, entry);
@@ -4770,14 +4684,14 @@ void monitor_cleanup(void)
}
qemu_mutex_unlock(&monitor_lock);
- /* QEMUBHs needs to be deleted before destroying the IOThread. */
- qemu_bh_delete(mon_global.qmp_dispatcher_bh);
- mon_global.qmp_dispatcher_bh = NULL;
- qemu_bh_delete(mon_global.qmp_respond_bh);
- mon_global.qmp_respond_bh = NULL;
+ /* QEMUBHs needs to be deleted before destroying the I/O thread */
+ qemu_bh_delete(qmp_dispatcher_bh);
+ qmp_dispatcher_bh = NULL;
+ qemu_bh_delete(qmp_respond_bh);
+ qmp_respond_bh = NULL;
- iothread_destroy(mon_global.mon_iothread);
- mon_global.mon_iothread = NULL;
+ iothread_destroy(mon_iothread);
+ mon_iothread = NULL;
}
QemuOptsList qemu_mon_opts = {
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 90e554ed0f..38b31250f9 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2533,16 +2533,17 @@
# @throttle: Since 2.11
# @nvme: Since 2.12
# @copy-on-read: Since 3.0
+# @blklogwrites: Since 3.0
#
# Since: 2.9
##
{ 'enum': 'BlockdevDriver',
- 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', 'copy-on-read',
- 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
- 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
- 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed',
- 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
- 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
+ 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop',
+ 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
+ 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
+ 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
+ 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
##
# @BlockdevOptionsFile:
@@ -3045,6 +3046,30 @@
'*set-state': ['BlkdebugSetStateOptions'] } }
##
+# @BlockdevOptionsBlklogwrites:
+#
+# Driver specific block device options for blklogwrites.
+#
+# @file: block device
+#
+# @log: block device used to log writes to @file
+#
+# @log-sector-size: sector size used in logging writes to @file, determines
+# granularity of offsets and sizes of writes (default: 512)
+#
+# @log-super-update-interval: interval of write requests after which the log
+# super block is updated to disk (default: 4096)
+#
+# Since: 3.0
+##
+{ 'struct': 'BlockdevOptionsBlklogwrites',
+ 'data': { 'file': 'BlockdevRef',
+ 'log': 'BlockdevRef',
+ '*log-sector-size': 'uint32',
+ '*log-append': 'bool',
+ '*log-super-update-interval': 'uint64' } }
+
+##
# @BlockdevOptionsBlkverify:
#
# Driver specific block device options for blkverify.
@@ -3563,6 +3588,7 @@
'discriminator': 'driver',
'data': {
'blkdebug': 'BlockdevOptionsBlkdebug',
+ 'blklogwrites':'BlockdevOptionsBlklogwrites',
'blkverify': 'BlockdevOptionsBlkverify',
'bochs': 'BlockdevOptionsGenericFormat',
'cloop': 'BlockdevOptionsGenericFormat',
diff --git a/qapi/char.json b/qapi/char.json
index 60f31d83fc..b7b2a05766 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -320,6 +320,7 @@
##
{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' },
'base': 'ChardevCommon' }
+# TODO: 'if': 'defined(CONFIG_SPICE)'
##
# @ChardevSpicePort:
@@ -332,6 +333,7 @@
##
{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' },
'base': 'ChardevCommon' }
+# TODO: 'if': 'defined(CONFIG_SPICE)'
##
# @ChardevVC:
@@ -385,8 +387,10 @@
'testdev': 'ChardevCommon',
'stdio' : 'ChardevStdio',
'console': 'ChardevCommon',
- 'spicevmc' : 'ChardevSpiceChannel',
- 'spiceport' : 'ChardevSpicePort',
+ 'spicevmc': 'ChardevSpiceChannel',
+# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
+ 'spiceport': 'ChardevSpicePort',
+# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
'vc' : 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
# next one is just for compatibility
diff --git a/qapi/misc.json b/qapi/misc.json
index 29da7856e3..f1860418e8 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -46,7 +46,7 @@
# Enumeration of capabilities to be advertised during initial client
# connection, used for agreeing on particular QMP extension behaviors.
#
-# @oob: QMP ability to support Out-Of-Band requests.
+# @oob: QMP ability to support out-of-band requests.
# (Please refer to qmp-spec.txt for more information on OOB)
#
# Since: 2.12
@@ -3454,6 +3454,9 @@
# only be dropped when the oob capability is enabled.
#
# @id: The dropped command's "id" field.
+# FIXME Broken by design. Events are broadcast to all monitors. If
+# another monitor's client has a command with the same ID in flight,
+# the event will incorrectly claim that command was dropped.
#
# @reason: The reason why the command is dropped.
#
@@ -3470,24 +3473,6 @@
'data': { 'id': 'any', 'reason': 'CommandDropReason' } }
##
-# @x-oob-test:
-#
-# Test OOB functionality. When sending this command with lock=true,
-# it'll try to hang the dispatcher. When sending it with lock=false,
-# it'll try to notify the locked thread to continue. Note: it should
-# only be used by QMP test program rather than anything else.
-#
-# Since: 2.12
-#
-# Example:
-#
-# { "execute": "x-oob-test",
-# "arguments": { "lock": true } }
-##
-{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' },
- 'allow-oob': true }
-
-##
# @set-numa-node:
#
# Runtime equivalent of '-numa' CLI option, available at
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 935f9e159c..6f2d466596 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -20,13 +20,14 @@
#include "qapi/qmp/qbool.h"
#include "sysemu/sysemu.h"
-QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
+static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
+ Error **errp)
{
+ const char *exec_key = NULL;
const QDictEntry *ent;
const char *arg_name;
const QObject *arg_obj;
- bool has_exec_key = false;
- QDict *dict = NULL;
+ QDict *dict;
dict = qobject_to(QDict, request);
if (!dict) {
@@ -39,25 +40,23 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
arg_name = qdict_entry_key(ent);
arg_obj = qdict_entry_value(ent);
- if (!strcmp(arg_name, "execute")) {
+ if (!strcmp(arg_name, "execute")
+ || (!strcmp(arg_name, "exec-oob") && allow_oob)) {
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
- error_setg(errp,
- "QMP input member 'execute' must be a string");
+ error_setg(errp, "QMP input member '%s' must be a string",
+ arg_name);
return NULL;
}
- has_exec_key = true;
- } else if (!strcmp(arg_name, "arguments")) {
- if (qobject_type(arg_obj) != QTYPE_QDICT) {
- error_setg(errp,
- "QMP input member 'arguments' must be an object");
+ if (exec_key) {
+ error_setg(errp, "QMP input member '%s' clashes with '%s'",
+ arg_name, exec_key);
return NULL;
}
- } else if (!strcmp(arg_name, "id")) {
- continue;
- } else if (!strcmp(arg_name, "control")) {
+ exec_key = arg_name;
+ } else if (!strcmp(arg_name, "arguments")) {
if (qobject_type(arg_obj) != QTYPE_QDICT) {
error_setg(errp,
- "QMP input member 'control' must be a dict");
+ "QMP input member 'arguments' must be an object");
return NULL;
}
} else {
@@ -67,7 +66,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
}
}
- if (!has_exec_key) {
+ if (!exec_key) {
error_setg(errp, "QMP input lacks member 'execute'");
return NULL;
}
@@ -76,20 +75,27 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
}
static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
- Error **errp)
+ bool allow_oob, Error **errp)
{
Error *local_err = NULL;
+ bool oob;
const char *command;
QDict *args, *dict;
QmpCommand *cmd;
QObject *ret = NULL;
- dict = qmp_dispatch_check_obj(request, errp);
+ dict = qmp_dispatch_check_obj(request, allow_oob, errp);
if (!dict) {
return NULL;
}
- command = qdict_get_str(dict, "execute");
+ command = qdict_get_try_str(dict, "execute");
+ oob = false;
+ if (!command) {
+ assert(allow_oob);
+ command = qdict_get_str(dict, "exec-oob");
+ oob = true;
+ }
cmd = qmp_find_command(cmds, command);
if (cmd == NULL) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
@@ -101,6 +107,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
command);
return NULL;
}
+ if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
+ error_setg(errp, "The command %s does not support OOB",
+ command);
+ return false;
+ }
if (runstate_check(RUN_STATE_PRECONFIG) &&
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
@@ -122,6 +133,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
} else if (cmd->options & QCO_NO_SUCCESS_RESP) {
g_assert(!ret);
} else if (!ret) {
+ /* TODO turn into assertion */
ret = QOBJECT(qdict_new());
}
@@ -130,53 +142,44 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
return ret;
}
-QObject *qmp_build_error_object(Error *err)
+QDict *qmp_error_response(Error *err)
{
- return qobject_from_jsonf("{ 'class': %s, 'desc': %s }",
- QapiErrorClass_str(error_get_class(err)),
- error_get_pretty(err));
+ QDict *rsp;
+
+ rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
+ QapiErrorClass_str(error_get_class(err)),
+ error_get_pretty(err));
+ error_free(err);
+ return rsp;
}
/*
- * Detect whether a request should be run out-of-band, by quickly
- * peeking at whether we have: { "control": { "run-oob": true } }. By
- * default commands are run in-band.
+ * Does @qdict look like a command to be run out-of-band?
*/
bool qmp_is_oob(QDict *dict)
{
- QBool *bool_obj;
-
- dict = qdict_get_qdict(dict, "control");
- if (!dict) {
- return false;
- }
-
- bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob"));
- if (!bool_obj) {
- return false;
- }
-
- return qbool_get_bool(bool_obj);
+ return qdict_haskey(dict, "exec-oob")
+ && !qdict_haskey(dict, "execute");
}
-QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
+QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
+ bool allow_oob)
{
Error *err = NULL;
QObject *ret;
QDict *rsp;
- ret = do_qmp_dispatch(cmds, request, &err);
+ ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
- rsp = qdict_new();
if (err) {
- qdict_put_obj(rsp, "error", qmp_build_error_object(err));
- error_free(err);
+ rsp = qmp_error_response(err);
} else if (ret) {
+ rsp = qdict_new();
qdict_put_obj(rsp, "return", ret);
} else {
- qobject_unref(rsp);
- return NULL;
+ /* Can only happen for commands with QCO_NO_SUCCESS_RESP */
+ rsp = NULL;
}
- return QOBJECT(rsp);
+ return rsp;
}
diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c
index 9d7e88e84a..5b8854043e 100644
--- a/qapi/qmp-event.c
+++ b/qapi/qmp-event.c
@@ -34,15 +34,15 @@ QMPEventFuncEmit qmp_event_get_func_emit(void)
static void timestamp_put(QDict *qdict)
{
int err;
- QObject *obj;
+ QDict *ts;
qemu_timeval tv;
err = qemu_gettimeofday(&tv);
/* Put -1 to indicate failure of getting host time */
- obj = qobject_from_jsonf("{ 'seconds': %lld, 'microseconds': %lld }",
- err < 0 ? -1LL : (long long)tv.tv_sec,
- err < 0 ? -1LL : (long long)tv.tv_usec);
- qdict_put_obj(qdict, "timestamp", obj);
+ ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }",
+ err < 0 ? -1LL : (long long)tv.tv_sec,
+ err < 0 ? -1LL : (long long)tv.tv_usec);
+ qdict_put(qdict, "timestamp", ts);
}
/*
diff --git a/qapi/ui.json b/qapi/ui.json
index f48d2a0afb..4ca91bb45a 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -118,7 +118,8 @@
{ 'struct': 'SpiceBasicInfo',
'data': { 'host': 'str',
'port': 'str',
- 'family': 'NetworkAddressFamily' } }
+ 'family': 'NetworkAddressFamily' },
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceServerInfo:
@@ -131,7 +132,8 @@
##
{ 'struct': 'SpiceServerInfo',
'base': 'SpiceBasicInfo',
- 'data': { '*auth': 'str' } }
+ 'data': { '*auth': 'str' },
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceChannel:
@@ -156,7 +158,8 @@
{ 'struct': 'SpiceChannel',
'base': 'SpiceBasicInfo',
'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
- 'tls': 'bool'} }
+ 'tls': 'bool'},
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceQueryMouseMode:
@@ -175,7 +178,8 @@
# Since: 1.1
##
{ 'enum': 'SpiceQueryMouseMode',
- 'data': [ 'client', 'server', 'unknown' ] }
+ 'data': [ 'client', 'server', 'unknown' ],
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceInfo:
@@ -212,7 +216,8 @@
{ 'struct': 'SpiceInfo',
'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
'*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
- 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
+ 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']},
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @query-spice:
@@ -257,7 +262,8 @@
# }
#
##
-{ 'command': 'query-spice', 'returns': 'SpiceInfo' }
+{ 'command': 'query-spice', 'returns': 'SpiceInfo',
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_CONNECTED:
@@ -282,7 +288,8 @@
##
{ 'event': 'SPICE_CONNECTED',
'data': { 'server': 'SpiceBasicInfo',
- 'client': 'SpiceBasicInfo' } }
+ 'client': 'SpiceBasicInfo' },
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_INITIALIZED:
@@ -310,7 +317,8 @@
##
{ 'event': 'SPICE_INITIALIZED',
'data': { 'server': 'SpiceServerInfo',
- 'client': 'SpiceChannel' } }
+ 'client': 'SpiceChannel' },
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_DISCONNECTED:
@@ -335,7 +343,8 @@
##
{ 'event': 'SPICE_DISCONNECTED',
'data': { 'server': 'SpiceBasicInfo',
- 'client': 'SpiceBasicInfo' } }
+ 'client': 'SpiceBasicInfo' },
+ 'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_MIGRATE_COMPLETED:
@@ -350,7 +359,8 @@
# "event": "SPICE_MIGRATE_COMPLETED" }
#
##
-{ 'event': 'SPICE_MIGRATE_COMPLETED' }
+{ 'event': 'SPICE_MIGRATE_COMPLETED',
+ 'if': 'defined(CONFIG_SPICE)' }
##
# == VNC
@@ -377,7 +387,8 @@
'data': { 'host': 'str',
'service': 'str',
'family': 'NetworkAddressFamily',
- 'websocket': 'bool' } }
+ 'websocket': 'bool' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncServerInfo:
@@ -391,7 +402,8 @@
##
{ 'struct': 'VncServerInfo',
'base': 'VncBasicInfo',
- 'data': { '*auth': 'str' } }
+ 'data': { '*auth': 'str' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncClientInfo:
@@ -408,7 +420,8 @@
##
{ 'struct': 'VncClientInfo',
'base': 'VncBasicInfo',
- 'data': { '*x509_dname': 'str', '*sasl_username': 'str' } }
+ 'data': { '*x509_dname': 'str', '*sasl_username': 'str' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncInfo:
@@ -449,7 +462,8 @@
{ 'struct': 'VncInfo',
'data': {'enabled': 'bool', '*host': 'str',
'*family': 'NetworkAddressFamily',
- '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
+ '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']},
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncPrimaryAuth:
@@ -460,7 +474,8 @@
##
{ 'enum': 'VncPrimaryAuth',
'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra',
- 'tls', 'vencrypt', 'sasl' ] }
+ 'tls', 'vencrypt', 'sasl' ],
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncVencryptSubAuth:
@@ -474,8 +489,8 @@
'tls-none', 'x509-none',
'tls-vnc', 'x509-vnc',
'tls-plain', 'x509-plain',
- 'tls-sasl', 'x509-sasl' ] }
-
+ 'tls-sasl', 'x509-sasl' ],
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncServerInfo2:
@@ -492,8 +507,8 @@
{ 'struct': 'VncServerInfo2',
'base': 'VncBasicInfo',
'data': { 'auth' : 'VncPrimaryAuth',
- '*vencrypt' : 'VncVencryptSubAuth' } }
-
+ '*vencrypt' : 'VncVencryptSubAuth' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VncInfo2:
@@ -525,7 +540,8 @@
'clients' : ['VncClientInfo'],
'auth' : 'VncPrimaryAuth',
'*vencrypt' : 'VncVencryptSubAuth',
- '*display' : 'str' } }
+ '*display' : 'str' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @query-vnc:
@@ -556,8 +572,8 @@
# }
#
##
-{ 'command': 'query-vnc', 'returns': 'VncInfo' }
-
+{ 'command': 'query-vnc', 'returns': 'VncInfo',
+ 'if': 'defined(CONFIG_VNC)' }
##
# @query-vnc-servers:
#
@@ -567,7 +583,8 @@
#
# Since: 2.3
##
-{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] }
+{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'],
+ 'if': 'defined(CONFIG_VNC)' }
##
# @change-vnc-password:
@@ -581,7 +598,8 @@
# Notes: An empty password in this command will set the password to the empty
# string. Existing clients are unaffected by executing this command.
##
-{ 'command': 'change-vnc-password', 'data': {'password': 'str'} }
+{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VNC_CONNECTED:
@@ -610,7 +628,8 @@
##
{ 'event': 'VNC_CONNECTED',
'data': { 'server': 'VncServerInfo',
- 'client': 'VncBasicInfo' } }
+ 'client': 'VncBasicInfo' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VNC_INITIALIZED:
@@ -637,7 +656,8 @@
##
{ 'event': 'VNC_INITIALIZED',
'data': { 'server': 'VncServerInfo',
- 'client': 'VncClientInfo' } }
+ 'client': 'VncClientInfo' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# @VNC_DISCONNECTED:
@@ -663,7 +683,8 @@
##
{ 'event': 'VNC_DISCONNECTED',
'data': { 'server': 'VncServerInfo',
- 'client': 'VncClientInfo' } }
+ 'client': 'VncClientInfo' },
+ 'if': 'defined(CONFIG_VNC)' }
##
# = Input
diff --git a/qemu-img.c b/qemu-img.c
index e1a506f7f6..7651d8172c 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -2141,11 +2141,6 @@ static int img_convert(int argc, char **argv)
goto fail_getopt;
}
- if (!s.wr_in_order && s.compressed) {
- error_report("Out of order write and compress are mutually exclusive");
- goto fail_getopt;
- }
-
if (tgt_image_opts && !skip_create) {
error_report("--target-image-opts requires use of -n flag");
goto fail_getopt;
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index eae817191b..233f78a406 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -46,6 +46,7 @@ extern char **environ;
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <sys/statvfs.h>
#ifdef FIFREEZE
#define CONFIG_FSFREEZE
@@ -458,7 +459,7 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
+ } else if (count < 0 || count >= UINT32_MAX) {
error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
count);
return NULL;
@@ -875,13 +876,28 @@ static void build_guest_fsinfo_for_real_device(char const *syspath,
p = strstr(syspath, "/devices/pci");
if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
- g_debug("only pci device is supported: sysfs path \"%s\"", syspath);
+ g_debug("only pci device is supported: sysfs path '%s'", syspath);
return;
}
- driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp);
- if (!driver) {
- goto cleanup;
+ p += 12 + pcilen;
+ while (true) {
+ driver = get_pci_driver(syspath, p - syspath, errp);
+ if (driver && (g_str_equal(driver, "ata_piix") ||
+ g_str_equal(driver, "sym53c8xx") ||
+ g_str_equal(driver, "virtio-pci") ||
+ g_str_equal(driver, "ahci"))) {
+ break;
+ }
+
+ if (sscanf(p, "/%x:%x:%x.%x%n",
+ pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
+ p += pcilen;
+ continue;
+ }
+
+ g_debug("unsupported driver or sysfs path '%s'", syspath);
+ return;
}
p = strstr(syspath, "/target");
@@ -1072,6 +1088,8 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
Error **errp)
{
GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
+ struct statvfs buf;
+ unsigned long used, nonroot_total, fr_size;
char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
mount->devmajor, mount->devminor);
@@ -1079,7 +1097,19 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
fs->type = g_strdup(mount->devtype);
build_guest_fsinfo_for_device(devpath, fs, errp);
+ if (statvfs(fs->mountpoint, &buf) == 0) {
+ fr_size = buf.f_frsize;
+ used = buf.f_blocks - buf.f_bfree;
+ nonroot_total = used + buf.f_bavail;
+ fs->used_bytes = used * fr_size;
+ fs->total_bytes = nonroot_total * fr_size;
+
+ fs->has_total_bytes = true;
+ fs->has_used_bytes = true;
+ }
+
g_free(devpath);
+
return fs;
}
@@ -1274,6 +1304,12 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
}
free_fs_mount_list(&mounts);
+ /* We may not issue any FIFREEZE here.
+ * Just unset ga_state here and ready for the next call.
+ */
+ if (i == 0) {
+ ga_unset_frozen(ga_state);
+ }
return i;
error:
@@ -1439,102 +1475,208 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
#define SUSPEND_SUPPORTED 0
#define SUSPEND_NOT_SUPPORTED 1
-static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
- const char *sysfile_str, Error **errp)
+typedef enum {
+ SUSPEND_MODE_DISK = 0,
+ SUSPEND_MODE_RAM = 1,
+ SUSPEND_MODE_HYBRID = 2,
+} SuspendMode;
+
+/*
+ * Executes a command in a child process using g_spawn_sync,
+ * returning an int >= 0 representing the exit status of the
+ * process.
+ *
+ * If the program wasn't found in path, returns -1.
+ *
+ * If a problem happened when creating the child process,
+ * returns -1 and errp is set.
+ */
+static int run_process_child(const char *command[], Error **errp)
+{
+ int exit_status, spawn_flag;
+ GError *g_err = NULL;
+ bool success;
+
+ spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
+ G_SPAWN_STDERR_TO_DEV_NULL;
+
+ success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag,
+ NULL, NULL, NULL, NULL,
+ &exit_status, &g_err);
+
+ if (success) {
+ return WEXITSTATUS(exit_status);
+ }
+
+ if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
+ error_setg(errp, "failed to create child process, error '%s'",
+ g_err->message);
+ }
+
+ g_error_free(g_err);
+ return -1;
+}
+
+static bool systemd_supports_mode(SuspendMode mode, Error **errp)
{
Error *local_err = NULL;
- char *pmutils_path;
- pid_t pid;
+ const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
+ "systemd-hybrid-sleep"};
+ const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
int status;
- pmutils_path = g_find_program_in_path(pmutils_bin);
+ status = run_process_child(cmd, &local_err);
- pid = fork();
- if (!pid) {
- char buf[32]; /* hopefully big enough */
- ssize_t ret;
- int fd;
+ /*
+ * systemctl status uses LSB return codes so we can expect
+ * status > 0 and be ok. To assert if the guest has support
+ * for the selected suspend mode, status should be < 4. 4 is
+ * the code for unknown service status, the return value when
+ * the service does not exist. A common value is status = 3
+ * (program is not running).
+ */
+ if (status > 0 && status < 4) {
+ return true;
+ }
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
- }
+ return false;
+}
- /*
- * If we get here either pm-utils is not installed or execle() has
- * failed. Let's try the manual method if the caller wants it.
- */
+static void systemd_suspend(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
+ const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
+ int status;
- if (!sysfile_str) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
+ status = run_process_child(cmd, &local_err);
- fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
- if (fd < 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
+ if (status == 0) {
+ return;
+ }
- ret = read(fd, buf, sizeof(buf)-1);
- if (ret <= 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
- buf[ret] = '\0';
+ if ((status == -1) && !local_err) {
+ error_setg(errp, "the helper program 'systemctl %s' was not found",
+ systemctl_args[mode]);
+ return;
+ }
- if (strstr(buf, sysfile_str)) {
- _exit(SUSPEND_SUPPORTED);
- }
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else {
+ error_setg(errp, "the helper program 'systemctl %s' returned an "
+ "unexpected exit status code (%d)",
+ systemctl_args[mode], status);
+ }
+}
- _exit(SUSPEND_NOT_SUPPORTED);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- goto out;
+static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *pmutils_args[3] = {"--hibernate", "--suspend",
+ "--suspend-hybrid"};
+ const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, &local_err);
+
+ if (status == SUSPEND_SUPPORTED) {
+ return true;
+ }
+
+ if ((status == -1) && !local_err) {
+ return false;
}
- ga_wait_child(pid, &status, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- goto out;
+ } else {
+ error_setg(errp,
+ "the helper program '%s' returned an unexpected exit"
+ " status code (%d)", "pm-is-supported", status);
}
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
+ return false;
+}
+
+static void pmutils_suspend(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
+ "pm-suspend-hybrid"};
+ const char *cmd[2] = {pmutils_binaries[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, &local_err);
+
+ if (status == 0) {
+ return;
}
- switch (WEXITSTATUS(status)) {
- case SUSPEND_SUPPORTED:
- goto out;
- case SUSPEND_NOT_SUPPORTED:
- error_setg(errp,
- "the requested suspend mode is not supported by the guest");
- goto out;
- default:
+ if ((status == -1) && !local_err) {
+ error_setg(errp, "the helper program '%s' was not found",
+ pmutils_binaries[mode]);
+ return;
+ }
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else {
error_setg(errp,
- "the helper program '%s' returned an unexpected exit status"
- " code (%d)", pmutils_path, WEXITSTATUS(status));
- goto out;
+ "the helper program '%s' returned an unexpected exit"
+ " status code (%d)", pmutils_binaries[mode], status);
}
+}
-out:
- g_free(pmutils_path);
+static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
+{
+ const char *sysfile_strs[3] = {"disk", "mem", NULL};
+ const char *sysfile_str = sysfile_strs[mode];
+ char buf[32]; /* hopefully big enough */
+ int fd;
+ ssize_t ret;
+
+ if (!sysfile_str) {
+ error_setg(errp, "unknown guest suspend mode");
+ return false;
+ }
+
+ fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
+ if (fd < 0) {
+ return false;
+ }
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret <= 0) {
+ return false;
+ }
+ buf[ret] = '\0';
+
+ if (strstr(buf, sysfile_str)) {
+ return true;
+ }
+ return false;
}
-static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
- Error **errp)
+static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
{
Error *local_err = NULL;
- char *pmutils_path;
+ const char *sysfile_strs[3] = {"disk", "mem", NULL};
+ const char *sysfile_str = sysfile_strs[mode];
pid_t pid;
int status;
- pmutils_path = g_find_program_in_path(pmutils_bin);
+ if (!sysfile_str) {
+ error_setg(errp, "unknown guest suspend mode");
+ return;
+ }
pid = fork();
- if (pid == 0) {
+ if (!pid) {
/* child */
int fd;
@@ -1543,19 +1685,6 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
reopen_fd_to_null(1);
reopen_fd_to_null(2);
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, NULL, environ);
- }
-
- /*
- * If we get here either pm-utils is not installed or execle() has
- * failed. Let's try the manual method if the caller wants it.
- */
-
- if (!sysfile_str) {
- _exit(EXIT_FAILURE);
- }
-
fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
if (fd < 0) {
_exit(EXIT_FAILURE);
@@ -1568,67 +1697,74 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
_exit(EXIT_SUCCESS);
} else if (pid < 0) {
error_setg_errno(errp, errno, "failed to create child process");
- goto out;
+ return;
}
ga_wait_child(pid, &status, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- goto out;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
+ return;
}
if (WEXITSTATUS(status)) {
error_setg(errp, "child process has failed to suspend");
- goto out;
}
-out:
- g_free(pmutils_path);
}
-void qmp_guest_suspend_disk(Error **errp)
+static void guest_suspend(SuspendMode mode, Error **errp)
{
Error *local_err = NULL;
+ bool mode_supported = false;
- bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (systemd_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ systemd_suspend(mode, &local_err);
+ }
+
+ if (!local_err) {
return;
}
- guest_suspend("pm-hibernate", "disk", errp);
-}
+ error_free(local_err);
-void qmp_guest_suspend_ram(Error **errp)
-{
- Error *local_err = NULL;
+ if (pmutils_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ pmutils_suspend(mode, &local_err);
+ }
- bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!local_err) {
return;
}
- guest_suspend("pm-suspend", "mem", errp);
-}
+ error_free(local_err);
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- Error *local_err = NULL;
+ if (linux_sys_state_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ linux_sys_state_suspend(mode, &local_err);
+ }
- bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL,
- &local_err);
- if (local_err) {
+ if (!mode_supported) {
+ error_setg(errp,
+ "the requested suspend mode is not supported by the guest");
+ } else if (local_err) {
error_propagate(errp, local_err);
- return;
}
+}
- guest_suspend("pm-suspend-hybrid", NULL, errp);
+void qmp_guest_suspend_disk(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_DISK, errp);
+}
+
+void qmp_guest_suspend_ram(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_RAM, errp);
+}
+
+void qmp_guest_suspend_hybrid(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_HYBRID, errp);
}
static GuestNetworkInterfaceList *
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 70ee5379f6..318d760a74 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -318,7 +318,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
}
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
+ } else if (count < 0 || count >= UINT32_MAX) {
error_setg(errp, "value '%" PRId64
"' is invalid for argument count", count);
return NULL;
@@ -670,6 +670,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
char fs_name[32];
char vol_info[MAX_PATH+1];
size_t len;
+ uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes;
GuestFilesystemInfo *fs = NULL;
GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
@@ -699,10 +700,21 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
fs_name[sizeof(fs_name) - 1] = 0;
fs = g_malloc(sizeof(*fs));
fs->name = g_strdup(guid);
+ fs->has_total_bytes = false;
+ fs->has_used_bytes = false;
if (len == 0) {
fs->mountpoint = g_strdup("System Reserved");
} else {
fs->mountpoint = g_strndup(mnt_point, len);
+ if (GetDiskFreeSpaceEx(fs->mountpoint,
+ (PULARGE_INTEGER) & i64FreeBytesToCaller,
+ (PULARGE_INTEGER) & i64TotalBytes,
+ (PULARGE_INTEGER) & i64FreeBytes)) {
+ fs->used_bytes = i64TotalBytes - i64FreeBytes;
+ fs->total_bytes = i64TotalBytes;
+ fs->has_total_bytes = true;
+ fs->has_used_bytes = true;
+ }
}
fs->type = g_strdup(fs_name);
fs->disk = build_guest_disk_info(guid, errp);
diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs
index 5af11627f8..f751a7e9f7 100644
--- a/qga/installer/qemu-ga.wxs
+++ b/qga/installer/qemu-ga.wxs
@@ -41,7 +41,7 @@
<Product
Name="QEMU guest agent"
- Id="{DF9974AD-E41A-4304-81AD-69AA8F299766}"
+ Id="*"
UpgradeCode="{EB6B8302-C06E-4BEC-ADAC-932C68A3A98D}"
Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
Version="$(env.QEMU_GA_VERSION)"
diff --git a/qga/main.c b/qga/main.c
index ea7540edcc..537cc0e162 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -545,7 +545,7 @@ fail:
#endif
}
-static int send_response(GAState *s, QObject *payload)
+static int send_response(GAState *s, QDict *payload)
{
const char *buf;
QString *payload_qstr, *response_qstr;
@@ -553,7 +553,7 @@ static int send_response(GAState *s, QObject *payload)
g_assert(payload && s->channel);
- payload_qstr = qobject_to_json(payload);
+ payload_qstr = qobject_to_json(QOBJECT(payload));
if (!payload_qstr) {
return -EINVAL;
}
@@ -581,12 +581,12 @@ static int send_response(GAState *s, QObject *payload)
static void process_command(GAState *s, QDict *req)
{
- QObject *rsp = NULL;
+ QDict *rsp;
int ret;
g_assert(req);
g_debug("processing command");
- rsp = qmp_dispatch(&ga_commands, QOBJECT(req));
+ rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false);
if (rsp) {
ret = send_response(s, rsp);
if (ret < 0) {
@@ -610,15 +610,13 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
if (err || !qdict) {
qobject_unref(qdict);
- qdict = qdict_new();
if (!err) {
g_warning("failed to parse event: unknown error");
error_setg(&err, QERR_JSON_PARSING);
} else {
g_warning("failed to parse event: %s", error_get_pretty(err));
}
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
+ qdict = qmp_error_response(err);
}
/* handle host->guest commands */
@@ -627,13 +625,11 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
} else {
if (!qdict_haskey(qdict, "error")) {
qobject_unref(qdict);
- qdict = qdict_new();
g_warning("unrecognized payload format");
error_setg(&err, QERR_UNSUPPORTED);
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
+ qdict = qmp_error_response(err);
}
- ret = send_response(s, QOBJECT(qdict));
+ ret = send_response(s, qdict);
if (ret < 0) {
g_warning("error sending error response: %s", strerror(-ret));
}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 17884c7c70..dfbc4a5e32 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -435,7 +435,9 @@
# for up to 10 seconds by VSS.
#
# Returns: Number of file systems currently frozen. On error, all filesystems
-# will be thawed.
+# will be thawed. If no filesystems are frozen as a result of this call,
+# then @guest-fsfreeze-status will remain "thawed" and calling
+# @guest-fsfreeze-thaw is not necessary.
#
# Since: 0.15.0
##
@@ -846,6 +848,8 @@
# @name: disk name
# @mountpoint: mount point path
# @type: file system type string
+# @used-bytes: file system used bytes (since 3.0)
+# @total-bytes: non-root file system total bytes (since 3.0)
# @disk: an array of disk hardware information that the volume lies on,
# which may be empty if the disk type is not supported
#
@@ -853,6 +857,7 @@
##
{ 'struct': 'GuestFilesystemInfo',
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
+ '*used-bytes': 'uint64', '*total-bytes': 'uint64',
'disk': ['GuestDiskAddress']} }
##
@@ -1168,10 +1173,10 @@
#
# @kernel-release:
# * POSIX: release field returned by uname(2)
-# * Windows: version number of the OS
+# * Windows: build number of the OS
# @kernel-version:
# * POSIX: version field returned by uname(2)
-# * Windows: build number of the OS
+# * Windows: version number of the OS
# @machine:
# * POSIX: machine field returned by uname(2)
# * Windows: one of x86, x86_64, arm, ia64
diff --git a/qmp.c b/qmp.c
index 73e46d795f..5170403e5d 100644
--- a/qmp.c
+++ b/qmp.c
@@ -129,38 +129,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
}
}
-#ifndef CONFIG_VNC
-/* If VNC support is enabled, the "true" query-vnc command is
- defined in the VNC subsystem */
-VncInfo *qmp_query_vnc(Error **errp)
-{
- error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
- return NULL;
-};
-
-VncInfo2List *qmp_query_vnc_servers(Error **errp)
-{
- error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
- return NULL;
-};
-#endif
-
-#ifndef CONFIG_SPICE
-/*
- * qmp_unregister_commands_hack() ensures that QMP command query-spice
- * exists only #ifdef CONFIG_SPICE. Necessary for an accurate
- * query-commands result. However, the QAPI schema is blissfully
- * unaware of that, and the QAPI code generator happily generates a
- * dead qmp_marshal_query_spice() that calls qmp_query_spice().
- * Provide it one, or else linking fails. FIXME Educate the QAPI
- * schema on CONFIG_SPICE.
- */
-SpiceInfo *qmp_query_spice(Error **errp)
-{
- abort();
-};
-#endif
-
void qmp_exit_preconfig(Error **errp)
{
if (!runstate_check(RUN_STATE_PRECONFIG)) {
@@ -412,23 +380,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
qmp_change_vnc_listen(target, errp);
}
}
-#else
-void qmp_change_vnc_password(const char *password, Error **errp)
-{
- error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
-}
-static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
- Error **errp)
-{
- error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
-}
#endif /* !CONFIG_VNC */
void qmp_change(const char *device, const char *target,
bool has_arg, const char *arg, Error **errp)
{
if (strcmp(device, "vnc") == 0) {
+#ifdef CONFIG_VNC
qmp_change_vnc(target, has_arg, arg, errp);
+#else
+ error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
+#endif
} else {
qmp_blockdev_change_medium(true, device, false, NULL, target,
has_arg, arg, false, 0, errp);
@@ -775,19 +737,3 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp)
return mem_info;
}
-
-static QemuSemaphore x_oob_test_sem;
-
-static void __attribute__((constructor)) x_oob_test_init(void)
-{
- qemu_sem_init(&x_oob_test_sem, 0);
-}
-
-void qmp_x_oob_test(bool lock, Error **errp)
-{
- if (lock) {
- qemu_sem_wait(&x_oob_test_sem);
- } else {
- qemu_sem_post(&x_oob_test_sem);
- }
-}
diff --git a/qobject/qjson.c b/qobject/qjson.c
index 9816a65c7d..2f6a590e44 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -59,10 +59,6 @@ QObject *qobject_from_json(const char *string, Error **errp)
return qobject_from_jsonv(string, NULL, errp);
}
-/*
- * IMPORTANT: This function aborts on error, thus it must not
- * be used with untrusted arguments.
- */
QObject *qobject_from_jsonf(const char *string, ...)
{
QObject *obj;
@@ -72,7 +68,24 @@ QObject *qobject_from_jsonf(const char *string, ...)
obj = qobject_from_jsonv(string, &ap, &error_abort);
va_end(ap);
- assert(obj != NULL);
+ return obj;
+}
+
+/*
+ * Parse @string as JSON object with %-escapes interpolated.
+ * Abort on error. Do not use with untrusted @string.
+ * Return the resulting QDict. It is never null.
+ */
+QDict *qdict_from_jsonf_nofail(const char *string, ...)
+{
+ QDict *obj;
+ va_list ap;
+
+ va_start(ap, string);
+ obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort));
+ va_end(ap);
+
+ assert(obj);
return obj;
}
diff --git a/scripts/coverity-model.c b/scripts/coverity-model.c
index 48b112393b..2c0346ff25 100644
--- a/scripts/coverity-model.c
+++ b/scripts/coverity-model.c
@@ -106,12 +106,8 @@ static int get_keysym(const name2keysym_t *table,
/* Replay data is considered trusted. */
uint8_t replay_get_byte(void)
{
- uint8_t byte = 0;
- if (replay_file) {
- uint8_t c;
- byte = c;
- }
- return byte;
+ uint8_t byte;
+ return byte;
}
diff --git a/scripts/create_config b/scripts/create_config
index 58948a67a4..d727e5e36e 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -107,7 +107,7 @@ case $line in
target_name=${line#*=}
echo "#define TARGET_NAME \"$target_name\""
;;
- TARGET_LIST=*)
+ TARGET_DIRS=*)
# do nothing
;;
TARGET_*=y) # configuration
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 3b0867c14f..0f3c991918 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -239,7 +239,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
QAPISchemaModularCVisitor.__init__(
self, prefix, 'qapi-commands',
' * Schema-defined QAPI/QMP commands', __doc__)
- self._regy = ''
+ self._regy = QAPIGenCCode()
self._visited_ret_types = {}
def _begin_module(self, name):
@@ -275,20 +275,28 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
''',
c_prefix=c_name(self._prefix, protect=False)))
- genc.add(gen_registry(self._regy, self._prefix))
+ genc.add(gen_registry(self._regy.get_content(), self._prefix))
- def visit_command(self, name, info, arg_type, ret_type, gen,
+ def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
if not gen:
return
- self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
+ # FIXME: If T is a user-defined type, the user is responsible
+ # for making this work, i.e. to make T's condition the
+ # conjunction of the T-returning commands' conditions. If T
+ # is a built-in type, this isn't possible: the
+ # qmp_marshal_output_T() will be generated unconditionally.
if ret_type and ret_type not in self._visited_ret_types[self._genc]:
self._visited_ret_types[self._genc].add(ret_type)
- self._genc.add(gen_marshal_output(ret_type))
- self._genh.add(gen_marshal_decl(name))
- self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
- self._regy += gen_register_command(name, success_response, allow_oob,
- allow_preconfig)
+ with ifcontext(ret_type.ifcond,
+ self._genh, self._genc, self._regy):
+ self._genc.add(gen_marshal_output(ret_type))
+ with ifcontext(ifcond, self._genh, self._genc, self._regy):
+ self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
+ self._genh.add(gen_marshal_decl(name))
+ self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
+ self._regy.add(gen_register_command(name, success_response,
+ allow_oob, allow_preconfig))
def gen_commands(schema, output_dir, prefix):
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 8b6708dbf1..9230a2a3e8 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -12,6 +12,7 @@
# See the COPYING file in the top-level directory.
from __future__ import print_function
+from contextlib import contextmanager
import errno
import os
import re
@@ -638,6 +639,27 @@ def add_name(name, info, meta, implicit=False):
all_names[name] = meta
+def check_if(expr, info):
+
+ def check_if_str(ifcond, info):
+ if not isinstance(ifcond, str):
+ raise QAPISemError(
+ info, "'if' condition must be a string or a list of strings")
+ if ifcond == '':
+ raise QAPISemError(info, "'if' condition '' makes no sense")
+
+ ifcond = expr.get('if')
+ if ifcond is None:
+ return
+ if isinstance(ifcond, list):
+ if ifcond == []:
+ raise QAPISemError(info, "'if' condition [] is useless")
+ for elt in ifcond:
+ check_if_str(elt, info)
+ else:
+ check_if_str(ifcond, info)
+
+
def check_type(info, source, value, allow_array=False,
allow_dict=False, allow_optional=False,
allow_metas=[]):
@@ -871,6 +893,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
+ if key == 'if':
+ check_if(expr, info)
for key in required:
if key not in expr:
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
@@ -899,28 +923,28 @@ def check_exprs(exprs):
if 'enum' in expr:
meta = 'enum'
- check_keys(expr_elem, 'enum', ['data'], ['prefix'])
+ check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
enum_types[expr[meta]] = expr
elif 'union' in expr:
meta = 'union'
check_keys(expr_elem, 'union', ['data'],
- ['base', 'discriminator'])
+ ['base', 'discriminator', 'if'])
union_types[expr[meta]] = expr
elif 'alternate' in expr:
meta = 'alternate'
- check_keys(expr_elem, 'alternate', ['data'])
+ check_keys(expr_elem, 'alternate', ['data'], ['if'])
elif 'struct' in expr:
meta = 'struct'
- check_keys(expr_elem, 'struct', ['data'], ['base'])
+ check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
struct_types[expr[meta]] = expr
elif 'command' in expr:
meta = 'command'
check_keys(expr_elem, 'command', [],
['data', 'returns', 'gen', 'success-response',
- 'boxed', 'allow-oob', 'allow-preconfig'])
+ 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
elif 'event' in expr:
meta = 'event'
- check_keys(expr_elem, 'event', [], ['data', 'boxed'])
+ check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
else:
raise QAPISemError(expr_elem['info'],
"Expression is missing metatype")
@@ -978,8 +1002,16 @@ def check_exprs(exprs):
# Schema compiler frontend
#
+def listify_cond(ifcond):
+ if not ifcond:
+ return []
+ if not isinstance(ifcond, list):
+ return [ifcond]
+ return ifcond
+
+
class QAPISchemaEntity(object):
- def __init__(self, name, info, doc):
+ def __init__(self, name, info, doc, ifcond=None):
assert name is None or isinstance(name, str)
self.name = name
self.module = None
@@ -990,12 +1022,19 @@ class QAPISchemaEntity(object):
# such place).
self.info = info
self.doc = doc
+ self._ifcond = ifcond # self.ifcond is set only after .check()
def c_name(self):
return c_name(self.name)
def check(self, schema):
- pass
+ if isinstance(self._ifcond, QAPISchemaType):
+ # inherit the condition from a type
+ typ = self._ifcond
+ typ.check(schema)
+ self.ifcond = typ.ifcond
+ else:
+ self.ifcond = listify_cond(self._ifcond)
def is_implicit(self):
return not self.info
@@ -1024,26 +1063,26 @@ class QAPISchemaVisitor(object):
def visit_builtin_type(self, name, info, json_type):
pass
- def visit_enum_type(self, name, info, values, prefix):
+ def visit_enum_type(self, name, info, ifcond, values, prefix):
pass
- def visit_array_type(self, name, info, element_type):
+ def visit_array_type(self, name, info, ifcond, element_type):
pass
- def visit_object_type(self, name, info, base, members, variants):
+ def visit_object_type(self, name, info, ifcond, base, members, variants):
pass
- def visit_object_type_flat(self, name, info, members, variants):
+ def visit_object_type_flat(self, name, info, ifcond, members, variants):
pass
- def visit_alternate_type(self, name, info, variants):
+ def visit_alternate_type(self, name, info, ifcond, variants):
pass
- def visit_command(self, name, info, arg_type, ret_type, gen,
+ def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
pass
- def visit_event(self, name, info, arg_type, boxed):
+ def visit_event(self, name, info, ifcond, arg_type, boxed):
pass
@@ -1122,8 +1161,8 @@ class QAPISchemaBuiltinType(QAPISchemaType):
class QAPISchemaEnumType(QAPISchemaType):
- def __init__(self, name, info, doc, values, prefix):
- QAPISchemaType.__init__(self, name, info, doc)
+ def __init__(self, name, info, doc, ifcond, values, prefix):
+ QAPISchemaType.__init__(self, name, info, doc, ifcond)
for v in values:
assert isinstance(v, QAPISchemaMember)
v.set_owner(name)
@@ -1132,6 +1171,7 @@ class QAPISchemaEnumType(QAPISchemaType):
self.prefix = prefix
def check(self, schema):
+ QAPISchemaType.check(self, schema)
seen = {}
for v in self.values:
v.check_clash(self.info, seen)
@@ -1152,20 +1192,23 @@ class QAPISchemaEnumType(QAPISchemaType):
return 'string'
def visit(self, visitor):
- visitor.visit_enum_type(self.name, self.info,
+ visitor.visit_enum_type(self.name, self.info, self.ifcond,
self.member_names(), self.prefix)
class QAPISchemaArrayType(QAPISchemaType):
def __init__(self, name, info, element_type):
- QAPISchemaType.__init__(self, name, info, None)
+ QAPISchemaType.__init__(self, name, info, None, None)
assert isinstance(element_type, str)
self._element_type_name = element_type
self.element_type = None
def check(self, schema):
+ QAPISchemaType.check(self, schema)
self.element_type = schema.lookup_type(self._element_type_name)
assert self.element_type
+ self.element_type.check(schema)
+ self.ifcond = self.element_type.ifcond
def is_implicit(self):
return True
@@ -1183,15 +1226,17 @@ class QAPISchemaArrayType(QAPISchemaType):
return 'array of ' + elt_doc_type
def visit(self, visitor):
- visitor.visit_array_type(self.name, self.info, self.element_type)
+ visitor.visit_array_type(self.name, self.info, self.ifcond,
+ self.element_type)
class QAPISchemaObjectType(QAPISchemaType):
- def __init__(self, name, info, doc, base, local_members, variants):
+ def __init__(self, name, info, doc, ifcond,
+ base, local_members, variants):
# struct has local_members, optional base, and no variants
# flat union has base, variants, and no local_members
# simple union has local_members, variants, and no base
- QAPISchemaType.__init__(self, name, info, doc)
+ QAPISchemaType.__init__(self, name, info, doc, ifcond)
assert base is None or isinstance(base, str)
for m in local_members:
assert isinstance(m, QAPISchemaObjectTypeMember)
@@ -1206,6 +1251,7 @@ class QAPISchemaObjectType(QAPISchemaType):
self.members = None
def check(self, schema):
+ QAPISchemaType.check(self, schema)
if self.members is False: # check for cycles
raise QAPISemError(self.info,
"Object %s contains itself" % self.name)
@@ -1263,9 +1309,9 @@ class QAPISchemaObjectType(QAPISchemaType):
return 'object'
def visit(self, visitor):
- visitor.visit_object_type(self.name, self.info,
+ visitor.visit_object_type(self.name, self.info, self.ifcond,
self.base, self.local_members, self.variants)
- visitor.visit_object_type_flat(self.name, self.info,
+ visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
self.members, self.variants)
@@ -1387,8 +1433,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
class QAPISchemaAlternateType(QAPISchemaType):
- def __init__(self, name, info, doc, variants):
- QAPISchemaType.__init__(self, name, info, doc)
+ def __init__(self, name, info, doc, ifcond, variants):
+ QAPISchemaType.__init__(self, name, info, doc, ifcond)
assert isinstance(variants, QAPISchemaObjectTypeVariants)
assert variants.tag_member
variants.set_owner(name)
@@ -1396,6 +1442,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
self.variants = variants
def check(self, schema):
+ QAPISchemaType.check(self, schema)
self.variants.tag_member.check(schema)
# Not calling self.variants.check_clash(), because there's nothing
# to clash with
@@ -1417,16 +1464,17 @@ class QAPISchemaAlternateType(QAPISchemaType):
return 'value'
def visit(self, visitor):
- visitor.visit_alternate_type(self.name, self.info, self.variants)
+ visitor.visit_alternate_type(self.name, self.info, self.ifcond,
+ self.variants)
def is_empty(self):
return False
class QAPISchemaCommand(QAPISchemaEntity):
- def __init__(self, name, info, doc, arg_type, ret_type,
+ def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
gen, success_response, boxed, allow_oob, allow_preconfig):
- QAPISchemaEntity.__init__(self, name, info, doc)
+ QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
self._arg_type_name = arg_type
@@ -1440,6 +1488,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.allow_preconfig = allow_preconfig
def check(self, schema):
+ QAPISchemaEntity.check(self, schema)
if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name)
assert (isinstance(self.arg_type, QAPISchemaObjectType) or
@@ -1459,7 +1508,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
assert isinstance(self.ret_type, QAPISchemaType)
def visit(self, visitor):
- visitor.visit_command(self.name, self.info,
+ visitor.visit_command(self.name, self.info, self.ifcond,
self.arg_type, self.ret_type,
self.gen, self.success_response,
self.boxed, self.allow_oob,
@@ -1467,14 +1516,15 @@ class QAPISchemaCommand(QAPISchemaEntity):
class QAPISchemaEvent(QAPISchemaEntity):
- def __init__(self, name, info, doc, arg_type, boxed):
- QAPISchemaEntity.__init__(self, name, info, doc)
+ def __init__(self, name, info, doc, ifcond, arg_type, boxed):
+ QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str)
self._arg_type_name = arg_type
self.arg_type = None
self.boxed = boxed
def check(self, schema):
+ QAPISchemaEntity.check(self, schema)
if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name)
assert (isinstance(self.arg_type, QAPISchemaObjectType) or
@@ -1491,7 +1541,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
def visit(self, visitor):
- visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
+ visitor.visit_event(self.name, self.info, self.ifcond,
+ self.arg_type, self.boxed)
class QAPISchema(object):
@@ -1567,22 +1618,22 @@ class QAPISchema(object):
('null', 'null', 'QNull' + pointer_suffix)]:
self._def_builtin_type(*t)
self.the_empty_object_type = QAPISchemaObjectType(
- 'q_empty', None, None, None, [], None)
+ 'q_empty', None, None, None, None, [], None)
self._def_entity(self.the_empty_object_type)
qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
'qstring', 'qdict', 'qlist',
'qbool'])
- self._def_entity(QAPISchemaEnumType('QType', None, None,
+ self._def_entity(QAPISchemaEnumType('QType', None, None, None,
qtype_values, 'QTYPE'))
def _make_enum_members(self, values):
return [QAPISchemaMember(v) for v in values]
- def _make_implicit_enum_type(self, name, info, values):
+ def _make_implicit_enum_type(self, name, info, ifcond, values):
# See also QAPISchemaObjectTypeMember._pretty_owner()
name = name + 'Kind' # Use namespace reserved by add_name()
self._def_entity(QAPISchemaEnumType(
- name, info, None, self._make_enum_members(values), None))
+ name, info, None, ifcond, self._make_enum_members(values), None))
return name
def _make_array_type(self, element_type, info):
@@ -1591,22 +1642,37 @@ class QAPISchema(object):
self._def_entity(QAPISchemaArrayType(name, info, element_type))
return name
- def _make_implicit_object_type(self, name, info, doc, role, members):
+ def _make_implicit_object_type(self, name, info, doc, ifcond,
+ role, members):
if not members:
return None
# See also QAPISchemaObjectTypeMember._pretty_owner()
name = 'q_obj_%s-%s' % (name, role)
- if not self.lookup_entity(name, QAPISchemaObjectType):
- self._def_entity(QAPISchemaObjectType(name, info, doc, None,
- members, None))
+ typ = self.lookup_entity(name, QAPISchemaObjectType)
+ if typ:
+ # The implicit object type has multiple users. This can
+ # happen only for simple unions' implicit wrapper types.
+ # Its ifcond should be the disjunction of its user's
+ # ifconds. Not implemented. Instead, we always pass the
+ # wrapped type's ifcond, which is trivially the same for all
+ # users. It's also necessary for the wrapper to compile.
+ # But it's not tight: the disjunction need not imply it. We
+ # may end up compiling useless wrapper types.
+ # TODO kill simple unions or implement the disjunction
+ assert ifcond == typ._ifcond # pylint: disable=protected-access
+ else:
+ self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
+ None, members, None))
return name
def _def_enum_type(self, expr, info, doc):
name = expr['enum']
data = expr['data']
prefix = expr.get('prefix')
+ ifcond = expr.get('if')
self._def_entity(QAPISchemaEnumType(
- name, info, doc, self._make_enum_members(data), prefix))
+ name, info, doc, ifcond,
+ self._make_enum_members(data), prefix))
def _make_member(self, name, typ, info):
optional = False
@@ -1626,7 +1692,8 @@ class QAPISchema(object):
name = expr['struct']
base = expr.get('base')
data = expr['data']
- self._def_entity(QAPISchemaObjectType(name, info, doc, base,
+ ifcond = expr.get('if')
+ self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
self._make_members(data, info),
None))
@@ -1638,18 +1705,21 @@ class QAPISchema(object):
assert len(typ) == 1
typ = self._make_array_type(typ[0], info)
typ = self._make_implicit_object_type(
- typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
+ typ, info, None, self.lookup_type(typ),
+ 'wrapper', [self._make_member('data', typ, info)])
return QAPISchemaObjectTypeVariant(case, typ)
def _def_union_type(self, expr, info, doc):
name = expr['union']
data = expr['data']
base = expr.get('base')
+ ifcond = expr.get('if')
tag_name = expr.get('discriminator')
tag_member = None
if isinstance(base, dict):
- base = (self._make_implicit_object_type(
- name, info, doc, 'base', self._make_members(base, info)))
+ base = self._make_implicit_object_type(
+ name, info, doc, ifcond,
+ 'base', self._make_members(base, info))
if tag_name:
variants = [self._make_variant(key, value)
for (key, value) in data.items()]
@@ -1657,12 +1727,12 @@ class QAPISchema(object):
else:
variants = [self._make_simple_variant(key, value, info)
for (key, value) in data.items()]
- typ = self._make_implicit_enum_type(name, info,
+ typ = self._make_implicit_enum_type(name, info, ifcond,
[v.name for v in variants])
tag_member = QAPISchemaObjectTypeMember('type', typ, False)
members = [tag_member]
self._def_entity(
- QAPISchemaObjectType(name, info, doc, base, members,
+ QAPISchemaObjectType(name, info, doc, ifcond, base, members,
QAPISchemaObjectTypeVariants(tag_name,
tag_member,
variants)))
@@ -1670,11 +1740,12 @@ class QAPISchema(object):
def _def_alternate_type(self, expr, info, doc):
name = expr['alternate']
data = expr['data']
+ ifcond = expr.get('if')
variants = [self._make_variant(key, value)
for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
self._def_entity(
- QAPISchemaAlternateType(name, info, doc,
+ QAPISchemaAlternateType(name, info, doc, ifcond,
QAPISchemaObjectTypeVariants(None,
tag_member,
variants)))
@@ -1688,13 +1759,14 @@ class QAPISchema(object):
boxed = expr.get('boxed', False)
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
+ ifcond = expr.get('if')
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
- name, info, doc, 'arg', self._make_members(data, info))
+ name, info, doc, ifcond, 'arg', self._make_members(data, info))
if isinstance(rets, list):
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
- self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
+ self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
gen, success_response,
boxed, allow_oob, allow_preconfig))
@@ -1702,10 +1774,11 @@ class QAPISchema(object):
name = expr['event']
data = expr.get('data')
boxed = expr.get('boxed', False)
+ ifcond = expr.get('if')
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
- name, info, doc, 'arg', self._make_members(data, info))
- self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
+ name, info, doc, ifcond, 'arg', self._make_members(data, info))
+ self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
def _def_exprs(self, exprs):
for expr_elem in exprs:
@@ -1869,8 +1942,8 @@ def cgen(code, **kwds):
if indent_level:
indent = genindent(indent_level)
# re.subn() lacks flags support before Python 2.7, use re.compile()
- raw = re.subn(re.compile(r'^.', re.MULTILINE),
- indent + r'\g<0>', raw)
+ raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
+ indent, raw)
raw = raw[0]
return re.sub(re.escape(eatspace) + r' *', '', raw)
@@ -1902,6 +1975,40 @@ def guardend(name):
name=guardname(name))
+def gen_if(ifcond):
+ ret = ''
+ for ifc in ifcond:
+ ret += mcgen('''
+#if %(cond)s
+''', cond=ifc)
+ return ret
+
+
+def gen_endif(ifcond):
+ ret = ''
+ for ifc in reversed(ifcond):
+ ret += mcgen('''
+#endif /* %(cond)s */
+''', cond=ifc)
+ return ret
+
+
+def _wrap_ifcond(ifcond, before, after):
+ if before == after:
+ return after # suppress empty #if ... #endif
+
+ assert after.startswith(before)
+ out = before
+ added = after[len(before):]
+ if added[0] == '\n':
+ out += '\n'
+ added = added[1:]
+ out += gen_if(ifcond)
+ out += added
+ out += gen_endif(ifcond)
+ return out
+
+
def gen_enum_lookup(name, values, prefix=None):
ret = mcgen('''
@@ -1999,6 +2106,10 @@ class QAPIGen(object):
def add(self, text):
self._body += text
+ def get_content(self, fname=None):
+ return (self._top(fname) + self._preamble + self._body
+ + self._bottom(fname))
+
def _top(self, fname):
return ''
@@ -2019,8 +2130,7 @@ class QAPIGen(object):
f = open(fd, 'r+', encoding='utf-8')
else:
f = os.fdopen(fd, 'r+')
- text = (self._top(fname) + self._preamble + self._body
- + self._bottom(fname))
+ text = self.get_content(fname)
oldtext = f.read(len(text) + 1)
if text != oldtext:
f.seek(0)
@@ -2029,10 +2139,62 @@ class QAPIGen(object):
f.close()
-class QAPIGenC(QAPIGen):
+@contextmanager
+def ifcontext(ifcond, *args):
+ """A 'with' statement context manager to wrap with start_if()/end_if()
- def __init__(self, blurb, pydoc):
+ *args: any number of QAPIGenCCode
+
+ Example::
+
+ with ifcontext(ifcond, self._genh, self._genc):
+ modify self._genh and self._genc ...
+
+ Is equivalent to calling::
+
+ self._genh.start_if(ifcond)
+ self._genc.start_if(ifcond)
+ modify self._genh and self._genc ...
+ self._genh.end_if()
+ self._genc.end_if()
+ """
+ for arg in args:
+ arg.start_if(ifcond)
+ yield
+ for arg in args:
+ arg.end_if()
+
+
+class QAPIGenCCode(QAPIGen):
+
+ def __init__(self):
QAPIGen.__init__(self)
+ self._start_if = None
+
+ def start_if(self, ifcond):
+ assert self._start_if is None
+ self._start_if = (ifcond, self._body, self._preamble)
+
+ def end_if(self):
+ assert self._start_if
+ self._wrap_ifcond()
+ self._start_if = None
+
+ def _wrap_ifcond(self):
+ self._body = _wrap_ifcond(self._start_if[0],
+ self._start_if[1], self._body)
+ self._preamble = _wrap_ifcond(self._start_if[0],
+ self._start_if[2], self._preamble)
+
+ def get_content(self, fname=None):
+ assert self._start_if is None
+ return QAPIGen.get_content(self, fname)
+
+
+class QAPIGenC(QAPIGenCCode):
+
+ def __init__(self, blurb, pydoc):
+ QAPIGenCCode.__init__(self)
self._blurb = blurb
self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
re.MULTILINE))
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index b5630844f9..987fd3c943 100644..100755
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -174,7 +174,7 @@ def texi_members(doc, what, base, variants, member_func):
return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items)
-def texi_sections(doc):
+def texi_sections(doc, ifcond):
"""Format additional sections following arguments"""
body = ''
for section in doc.sections:
@@ -185,14 +185,16 @@ def texi_sections(doc):
body += texi_example(section.text)
else:
body += texi_format(section.text)
+ if ifcond:
+ body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
return body
-def texi_entity(doc, what, base=None, variants=None,
+def texi_entity(doc, what, ifcond, base=None, variants=None,
member_func=texi_member):
return (texi_body(doc)
+ texi_members(doc, what, base, variants, member_func)
- + texi_sections(doc))
+ + texi_sections(doc, ifcond))
class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
@@ -204,47 +206,47 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
def write(self, output_dir):
self._gen.write(output_dir, self._prefix + 'qapi-doc.texi')
- def visit_enum_type(self, name, info, values, prefix):
+ def visit_enum_type(self, name, info, ifcond, values, prefix):
doc = self.cur_doc
self._gen.add(TYPE_FMT(type='Enum',
name=doc.symbol,
- body=texi_entity(doc, 'Values',
+ body=texi_entity(doc, 'Values', ifcond,
member_func=texi_enum_value)))
- def visit_object_type(self, name, info, base, members, variants):
+ def visit_object_type(self, name, info, ifcond, base, members, variants):
doc = self.cur_doc
if base and base.is_implicit():
base = None
self._gen.add(TYPE_FMT(type='Object',
name=doc.symbol,
- body=texi_entity(doc, 'Members',
+ body=texi_entity(doc, 'Members', ifcond,
base, variants)))
- def visit_alternate_type(self, name, info, variants):
+ def visit_alternate_type(self, name, info, ifcond, variants):
doc = self.cur_doc
self._gen.add(TYPE_FMT(type='Alternate',
name=doc.symbol,
- body=texi_entity(doc, 'Members')))
+ body=texi_entity(doc, 'Members', ifcond)))
- def visit_command(self, name, info, arg_type, ret_type, gen,
+ def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
doc = self.cur_doc
if boxed:
body = texi_body(doc)
body += ('\n@b{Arguments:} the members of @code{%s}\n'
% arg_type.name)
- body += texi_sections(doc)
+ body += texi_sections(doc, ifcond)
else:
- body = texi_entity(doc, 'Arguments')
+ body = texi_entity(doc, 'Arguments', ifcond)
self._gen.add(MSG_FMT(type='Command',
name=doc.symbol,
body=body))
- def visit_event(self, name, info, arg_type, boxed):
+ def visit_event(self, name, info, ifcond, arg_type, boxed):
doc = self.cur_doc
self._gen.add(MSG_FMT(type='Event',
name=doc.symbol,
- body=texi_entity(doc, 'Arguments')))
+ body=texi_entity(doc, 'Arguments', ifcond)))
def symbol(self, doc, entity):
if self._gen._body:
@@ -257,7 +259,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
assert not doc.args
if self._gen._body:
self._gen.add('\n')
- self._gen.add(texi_body(doc) + texi_sections(doc))
+ self._gen.add(texi_body(doc) + texi_sections(doc, None))
def gen_doc(schema, output_dir, prefix):
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index 5657524688..764ef177ab 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -184,9 +184,11 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
genh.add(gen_enum(self._enum_name, self._event_names))
genc.add(gen_enum_lookup(self._enum_name, self._event_names))
- def visit_event(self, name, info, arg_type, boxed):
- self._genh.add(gen_event_send_decl(name, arg_type, boxed))
- self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name))
+ def visit_event(self, name, info, ifcond, arg_type, boxed):
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._genh.add(gen_event_send_decl(name, arg_type, boxed))
+ self._genc.add(gen_event_send(name, arg_type, boxed,
+ self._enum_name))
self._event_names.append(name)
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 6ad198ae5b..71d4a779ce 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -18,6 +18,15 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
def indent(level):
return level * 4 * ' '
+ if isinstance(obj, tuple):
+ ifobj, ifcond = obj
+ ret = gen_if(ifcond)
+ ret += to_qlit(ifobj, level)
+ endif = gen_endif(ifcond)
+ if endif:
+ ret += '\n' + endif
+ return ret
+
ret = ''
if not suppress_first_indent:
ret += indent(level)
@@ -26,11 +35,11 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
elif isinstance(obj, str):
ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'
elif isinstance(obj, list):
- elts = [to_qlit(elt, level + 1)
+ elts = [to_qlit(elt, level + 1).strip('\n')
for elt in obj]
elts.append(indent(level + 1) + "{}")
ret += 'QLIT_QLIST(((QLitObject[]) {\n'
- ret += ',\n'.join(elts) + '\n'
+ ret += '\n'.join(elts) + '\n'
ret += indent(level) + '}))'
elif isinstance(obj, dict):
elts = []
@@ -45,6 +54,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false')
else:
assert False # not implemented
+ if level > 0:
+ ret += ','
return ret
@@ -126,12 +137,12 @@ const QLitObject %(c_name)s = %(c_string)s;
return '[' + self._use_type(typ.element_type) + ']'
return self._name(typ.name)
- def _gen_qlit(self, name, mtype, obj):
+ def _gen_qlit(self, name, mtype, obj, ifcond):
if mtype not in ('command', 'event', 'builtin', 'array'):
name = self._name(name)
obj['name'] = name
obj['meta-type'] = mtype
- self._qlits.append(obj)
+ self._qlits.append((obj, ifcond))
def _gen_member(self, member):
ret = {'name': member.name, 'type': self._use_type(member.type)}
@@ -147,28 +158,29 @@ const QLitObject %(c_name)s = %(c_string)s;
return {'case': variant.name, 'type': self._use_type(variant.type)}
def visit_builtin_type(self, name, info, json_type):
- self._gen_qlit(name, 'builtin', {'json-type': json_type})
+ self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
- def visit_enum_type(self, name, info, values, prefix):
- self._gen_qlit(name, 'enum', {'values': values})
+ def visit_enum_type(self, name, info, ifcond, values, prefix):
+ self._gen_qlit(name, 'enum', {'values': values}, ifcond)
- def visit_array_type(self, name, info, element_type):
+ def visit_array_type(self, name, info, ifcond, element_type):
element = self._use_type(element_type)
- self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
+ self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
+ ifcond)
- def visit_object_type_flat(self, name, info, members, variants):
+ def visit_object_type_flat(self, name, info, ifcond, members, variants):
obj = {'members': [self._gen_member(m) for m in members]}
if variants:
obj.update(self._gen_variants(variants.tag_member.name,
variants.variants))
- self._gen_qlit(name, 'object', obj)
+ self._gen_qlit(name, 'object', obj, ifcond)
- def visit_alternate_type(self, name, info, variants):
+ def visit_alternate_type(self, name, info, ifcond, variants):
self._gen_qlit(name, 'alternate',
{'members': [{'type': self._use_type(m.type)}
- for m in variants.variants]})
+ for m in variants.variants]}, ifcond)
- def visit_command(self, name, info, arg_type, ret_type, gen,
+ def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
@@ -176,11 +188,12 @@ const QLitObject %(c_name)s = %(c_string)s;
{'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type),
'allow-oob': allow_oob,
- 'allow-preconfig': allow_preconfig})
+ 'allow-preconfig': allow_preconfig}, ifcond)
- def visit_event(self, name, info, arg_type, boxed):
+ def visit_event(self, name, info, ifcond, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
- self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
+ self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)},
+ ifcond)
def gen_introspect(schema, output_dir, prefix, opt_unmask):
diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index a599352e59..fd7808103c 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -55,7 +55,7 @@ def gen_struct_members(members):
return ret
-def gen_object(name, base, members, variants):
+def gen_object(name, ifcond, base, members, variants):
if name in objects_seen:
return ''
objects_seen.add(name)
@@ -64,11 +64,14 @@ def gen_object(name, base, members, variants):
if variants:
for v in variants.variants:
if isinstance(v.type, QAPISchemaObjectType):
- ret += gen_object(v.type.name, v.type.base,
+ ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
v.type.local_members, v.type.variants)
ret += mcgen('''
+''')
+ ret += gen_if(ifcond)
+ ret += mcgen('''
struct %(c_name)s {
''',
c_name=c_name(name))
@@ -101,6 +104,7 @@ struct %(c_name)s {
ret += mcgen('''
};
''')
+ ret += gen_endif(ifcond)
return ret
@@ -208,34 +212,40 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
self._genh.add(gen_type_cleanup_decl(name))
self._genc.add(gen_type_cleanup(name))
- def visit_enum_type(self, name, info, values, prefix):
- self._genh.preamble_add(gen_enum(name, values, prefix))
- self._genc.add(gen_enum_lookup(name, values, prefix))
+ def visit_enum_type(self, name, info, ifcond, values, prefix):
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._genh.preamble_add(gen_enum(name, values, prefix))
+ self._genc.add(gen_enum_lookup(name, values, prefix))
- def visit_array_type(self, name, info, element_type):
- self._genh.preamble_add(gen_fwd_object_or_array(name))
- self._genh.add(gen_array(name, element_type))
- self._gen_type_cleanup(name)
+ def visit_array_type(self, name, info, ifcond, element_type):
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._genh.preamble_add(gen_fwd_object_or_array(name))
+ self._genh.add(gen_array(name, element_type))
+ self._gen_type_cleanup(name)
- def visit_object_type(self, name, info, base, members, variants):
+ def visit_object_type(self, name, info, ifcond, base, members, variants):
# Nothing to do for the special empty builtin
if name == 'q_empty':
return
- self._genh.preamble_add(gen_fwd_object_or_array(name))
- self._genh.add(gen_object(name, base, members, variants))
- if base and not base.is_implicit():
- self._genh.add(gen_upcast(name, base))
- # TODO Worth changing the visitor signature, so we could
- # directly use rather than repeat type.is_implicit()?
- if not name.startswith('q_'):
- # implicit types won't be directly allocated/freed
- self._gen_type_cleanup(name)
-
- def visit_alternate_type(self, name, info, variants):
- self._genh.preamble_add(gen_fwd_object_or_array(name))
- self._genh.add(gen_object(name, None,
+ with ifcontext(ifcond, self._genh):
+ self._genh.preamble_add(gen_fwd_object_or_array(name))
+ self._genh.add(gen_object(name, ifcond, base, members, variants))
+ with ifcontext(ifcond, self._genh, self._genc):
+ if base and not base.is_implicit():
+ self._genh.add(gen_upcast(name, base))
+ # TODO Worth changing the visitor signature, so we could
+ # directly use rather than repeat type.is_implicit()?
+ if not name.startswith('q_'):
+ # implicit types won't be directly allocated/freed
+ self._gen_type_cleanup(name)
+
+ def visit_alternate_type(self, name, info, ifcond, variants):
+ with ifcontext(ifcond, self._genh):
+ self._genh.preamble_add(gen_fwd_object_or_array(name))
+ self._genh.add(gen_object(name, ifcond, None,
[variants.tag_member], variants))
- self._gen_type_cleanup(name)
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._gen_type_cleanup(name)
def gen_types(schema, output_dir, prefix, opt_builtins):
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index bdcafb64ee..dd5034a66a 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -310,30 +310,35 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
''',
types=types))
- def visit_enum_type(self, name, info, values, prefix):
- self._genh.add(gen_visit_decl(name, scalar=True))
- self._genc.add(gen_visit_enum(name))
+ def visit_enum_type(self, name, info, ifcond, values, prefix):
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._genh.add(gen_visit_decl(name, scalar=True))
+ self._genc.add(gen_visit_enum(name))
- def visit_array_type(self, name, info, element_type):
- self._genh.add(gen_visit_decl(name))
- self._genc.add(gen_visit_list(name, element_type))
+ def visit_array_type(self, name, info, ifcond, element_type):
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._genh.add(gen_visit_decl(name))
+ self._genc.add(gen_visit_list(name, element_type))
- def visit_object_type(self, name, info, base, members, variants):
+ def visit_object_type(self, name, info, ifcond, base, members, variants):
# Nothing to do for the special empty builtin
if name == 'q_empty':
return
- self._genh.add(gen_visit_members_decl(name))
- self._genc.add(gen_visit_object_members(name, base, members, variants))
- # TODO Worth changing the visitor signature, so we could
- # directly use rather than repeat type.is_implicit()?
- if not name.startswith('q_'):
- # only explicit types need an allocating visit
+ with ifcontext(ifcond, self._genh, self._genc):
+ self._genh.add(gen_visit_members_decl(name))
+ self._genc.add(gen_visit_object_members(name, base,
+ members, variants))
+ # TODO Worth changing the visitor signature, so we could
+ # directly use rather than repeat type.is_implicit()?
+ if not name.startswith('q_'):
+ # only explicit types need an allocating visit
+ self._genh.add(gen_visit_decl(name))
+ self._genc.add(gen_visit_object(name, base, members, variants))
+
+ def visit_alternate_type(self, name, info, ifcond, variants):
+ with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name))
- self._genc.add(gen_visit_object(name, base, members, variants))
-
- def visit_alternate_type(self, name, info, variants):
- self._genh.add(gen_visit_decl(name))
- self._genc.add(gen_visit_alternate(name, variants))
+ self._genc.add(gen_visit_alternate(name, variants))
def gen_visit(schema, output_dir, prefix, opt_builtins):
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index a5cb96d79a..b0dc8a714a 100755
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -165,21 +165,26 @@ usage() {
cat <<EOF
Usage: qemu-binfmt-conf.sh [--qemu-path PATH][--debian][--systemd CPU]
[--help][--credential yes|no][--exportdir PATH]
+ [--persistent yes|no][--qemu-suffix SUFFIX]
Configure binfmt_misc to use qemu interpreter
- --help: display this usage
- --qemu-path: set path to qemu interpreter ($QEMU_PATH)
- --debian: don't write into /proc,
- instead generate update-binfmts templates
- --systemd: don't write into /proc,
- instead generate file for systemd-binfmt.service
- for the given CPU. If CPU is "ALL", generate a
- file for all known cpus
- --exportdir: define where to write configuration files
- (default: $SYSTEMDDIR or $DEBIANDIR)
- --credential: if yes, credential and security tokens are
- calculated according to the binary to interpret
+ --help: display this usage
+ --qemu-path: set path to qemu interpreter ($QEMU_PATH)
+ --qemu-suffix: add a suffix to the default interpreter name
+ --debian: don't write into /proc,
+ instead generate update-binfmts templates
+ --systemd: don't write into /proc,
+ instead generate file for systemd-binfmt.service
+ for the given CPU. If CPU is "ALL", generate a
+ file for all known cpus
+ --exportdir: define where to write configuration files
+ (default: $SYSTEMDDIR or $DEBIANDIR)
+ --credential: if yes, credential and security tokens are
+ calculated according to the binary to interpret
+ --persistent: if yes, the interpreter is loaded when binfmt is
+ configured and remains in memory. All future uses
+ are cloned from the open file.
To import templates with update-binfmts, use :
@@ -245,7 +250,15 @@ qemu_check_systemd() {
}
qemu_generate_register() {
- echo ":qemu-$cpu:M::$magic:$mask:$qemu:$FLAGS"
+ flags=""
+ if [ "$CREDENTIAL" = "yes" ] ; then
+ flags="OC"
+ fi
+ if [ "$PERSISTENT" = "yes" ] ; then
+ flags="${flags}F"
+ fi
+
+ echo ":qemu-$cpu:M::$magic:$mask:$qemu:$flags"
}
qemu_register_interpreter() {
@@ -264,10 +277,8 @@ package qemu-$cpu
interpreter $qemu
magic $magic
mask $mask
+credential $CREDENTIAL
EOF
- if [ "$FLAGS" = "OC" ] ; then
- echo "credentials yes" >> "$EXPORTDIR/qemu-$cpu"
- fi
}
qemu_set_binfmts() {
@@ -291,6 +302,7 @@ qemu_set_binfmts() {
qemu="$QEMU_PATH/qemu-i386"
fi
+ qemu="$qemu$QEMU_SUFFIX"
if [ "$host_family" != "$family" ] ; then
$BINFMT_SET
fi
@@ -304,9 +316,11 @@ SYSTEMDDIR="/etc/binfmt.d"
DEBIANDIR="/usr/share/binfmts"
QEMU_PATH=/usr/local/bin
-FLAGS=""
+CREDENTIAL=no
+PERSISTENT=no
+QEMU_SUFFIX=""
-options=$(getopt -o ds:Q:e:hc: -l debian,systemd:,qemu-path:,exportdir:,help,credential: -- "$@")
+options=$(getopt -o ds:Q:S:e:hc:p: -l debian,systemd:,qemu-path:,qemu-suffix:,exportdir:,help,credential:,persistent: -- "$@")
eval set -- "$options"
while true ; do
@@ -342,6 +356,10 @@ while true ; do
shift
QEMU_PATH="$1"
;;
+ -F|--qemu-suffix)
+ shift
+ QEMU_SUFFIX="$1"
+ ;;
-e|--exportdir)
shift
EXPORTDIR="$1"
@@ -352,11 +370,11 @@ while true ; do
;;
-c|--credential)
shift
- if [ "$1" = "yes" ] ; then
- FLAGS="OC"
- else
- FLAGS=""
- fi
+ CREDENTIAL="$1"
+ ;;
+ -p|--persistent)
+ shift
+ PERSISTENT="$1"
;;
*)
break
diff --git a/scripts/travis/coverage-summary.sh b/scripts/travis/coverage-summary.sh
new file mode 100755
index 0000000000..d7086cf9ca
--- /dev/null
+++ b/scripts/travis/coverage-summary.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Author: Alex Bennée <alex.bennee@linaro.org>
+#
+# Summerise the state of code coverage with gcovr and tweak the output
+# to be more sane on Travis hosts. As we expect to be executed on a
+# throw away CI instance we do spam temp files all over the shop. You
+# most likely don't want to execute this script but just call gcovr
+# directly. See also "make coverage-report"
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+
+# first generate the coverage report
+gcovr -p -o raw-report.txt
+
+# strip the full-path and line markers
+sed s@$PWD\/@@ raw-report.txt | sed s/[0-9]\*[,-]//g > simplified.txt
+
+# reflow lines that got split
+awk '/.[ch]$/ { printf("%s", $0); next } 1' simplified.txt > rejoined.txt
+
+# columnify
+column -t rejoined.txt > final.txt
+
+# and dump, stripping out 0% coverage
+grep -v "0%" final.txt
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 8859e88ffb..a49282704e 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -442,6 +442,10 @@ qapi-schema += args-unknown.json
qapi-schema += bad-base.json
qapi-schema += bad-data.json
qapi-schema += bad-ident.json
+qapi-schema += bad-if.json
+qapi-schema += bad-if-empty.json
+qapi-schema += bad-if-empty-list.json
+qapi-schema += bad-if-list.json
qapi-schema += bad-type-bool.json
qapi-schema += bad-type-dict.json
qapi-schema += bad-type-int.json
@@ -870,7 +874,7 @@ endif
# QTest rules
-TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_LIST)))
+TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
ifeq ($(CONFIG_POSIX),y)
QTEST_TARGETS = $(TARGETS)
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
@@ -893,26 +897,16 @@ GCOV_OPTIONS = -n $(if $(V),-f,)
.PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS))
$(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: subdir-%-softmmu $(check-qtest-y)
- $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,)
$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
QTEST_QEMU_IMG=qemu-img$(EXESUF) \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \
gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER","$@")
- $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \
- echo Gcov report for $$f:;\
- $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
- done,)
.PHONY: $(patsubst %, check-%, $(check-unit-y) $(check-speed-y))
$(patsubst %, check-%, $(check-unit-y) $(check-speed-y)): check-%: %
- $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,)
$(call quiet-command, \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \
gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER","$*")
- $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \
- echo Gcov report for $$f:;\
- $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
- done,)
# gtester tests with XML output
@@ -934,14 +928,14 @@ check-report.html: check-report.xml
# Per guest TCG tests
-LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_LIST))
+LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_DIRS))
BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(LINUX_USER_TARGETS))
CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(LINUX_USER_TARGETS))
RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(LINUX_USER_TARGETS))
ifeq ($(HAVE_USER_DOCKER),y)
# Probe for the Docker Builds needed for each build
-$(foreach PROBE_TARGET,$(TARGET_LIST), \
+$(foreach PROBE_TARGET,$(TARGET_DIRS), \
$(eval -include $(SRC_PATH)/tests/tcg/Makefile.probe) \
$(if $(DOCKER_PREREQ), \
$(eval build-tcg-tests-$(PROBE_TARGET): $(DOCKER_PREREQ))))
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include
index 91d9665517..b2a7e761cc 100644
--- a/tests/docker/Makefile.include
+++ b/tests/docker/Makefile.include
@@ -5,6 +5,8 @@
DOCKER_SUFFIX := .docker
DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles
DOCKER_DEPRECATED_IMAGES := debian
+# we don't run tests on intermediate images (used as base by another image)
+DOCKER_INTERMEDIATE_IMAGES := debian8 debian9 debian8-mxe debian-ports debian-sid
DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker)))))
DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES))
# Use a global constant ccache directory to speed up repetitive builds
@@ -44,9 +46,6 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker
"CHECK", "$*")
else
docker-image-%: $(DOCKER_FILES_DIR)/%.docker
- @if test "$@" = docker-image-debian-bootstrap -a -z "$(EXECUTABLE)"; then \
- echo WARNING: EXECUTABLE is not set, debootstrap may fail. 2>&1 ; \
- fi
$(call quiet-command,\
$(DOCKER_SCRIPT) build qemu:$* $< \
$(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \
@@ -54,6 +53,31 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker
$(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\
$(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\
"BUILD","$*")
+
+# Special rule for debootstraped binfmt linux-user images
+docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker
+ $(if $(EXECUTABLE),,\
+ $(error EXECUTABLE not set, debootstrap of debian-$* would fail))
+ $(if $(wildcard $(EXECUTABLE)),,\
+ $(error Please build $(EXECUTABLE) first))
+ $(if $(DEB_ARCH),,\
+ $(error DEB_ARCH not set, debootstrap of debian-$* would fail))
+ $(if $(DEB_TYPE),,\
+ $(error DEB_TYPE not set, debootstrap of debian-$* would fail))
+ $(if $(filter $(QEMU_TARGET),$(TARGET_DIRS)), \
+ $(call quiet-command, \
+ DEB_ARCH=$(DEB_ARCH) \
+ DEB_TYPE=$(DEB_TYPE) \
+ $(DOCKER_SCRIPT) build qemu:debian-$* $< \
+ $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \
+ $(if $(NOUSER),,--add-current-user) \
+ $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES)) \
+ $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \
+ "BUILD","binfmt debian-$* (debootstrapped)"), \
+ $(call quiet-command, \
+ $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $<, \
+ "CHECK", "debian-$* exists"))
+
endif
# Enforce dependencies for composite images
@@ -97,8 +121,23 @@ docker-image-travis: NOUSER=1
# Specialist build images, sometimes very limited tools
docker-image-tricore-cross: docker-image-debian9
+# Rules for building linux-user powered images
+#
+# These are slower than using native cross compiler setups but can
+# work around issues with poorly working multi-arch systems and broken
+# packages.
+
+# Jessie is the last supported release for powerpc, but multi-arch is
+# broken so we need a qemu-linux-user for this target
+docker-binfmt-image-debian-powerpc-user: DEB_ARCH = powerpc
+docker-binfmt-image-debian-powerpc-user: DEB_TYPE = jessie
+docker-binfmt-image-debian-powerpc-user: QEMU_TARGET = ppc-linux-user
+docker-binfmt-image-debian-powerpc-user: EXECUTABLE = ${BUILD_DIR}/ppc-linux-user/qemu-ppc
+docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user
+DOCKER_USER_IMAGES += debian-powerpc-user
+
# Expand all the pre-requistes for each docker image and test combination
-$(foreach i,$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES), \
+$(foreach i,$(filter-out $(DOCKER_INTERMEDIATE_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \
$(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \
$(eval .PHONY: docker-$t@$i) \
$(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \
@@ -126,6 +165,11 @@ docker:
@echo
@echo 'Available container images:'
@echo ' $(DOCKER_IMAGES)'
+ifneq ($(DOCKER_USER_IMAGES),)
+ @echo
+ @echo 'Available linux-user images (docker-binfmt-image-debian-%):'
+ @echo ' $(DOCKER_USER_IMAGES)'
+endif
@echo
@echo 'Available tests:'
@echo ' $(DOCKER_TESTS)'
diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index b279836154..69e7130db7 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -113,6 +113,10 @@ def _copy_binary_with_libs(src, dest_dir):
_copy_with_mkdir(l , dest_dir, so_path)
def _read_qemu_dockerfile(img_name):
+ # special case for Debian linux-user images
+ if img_name.startswith("debian") and img_name.endswith("user"):
+ img_name = "debian-bootstrap"
+
df = os.path.join(os.path.dirname(__file__), "dockerfiles",
img_name + ".docker")
return open(df, "r").read()
diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker
index 3a9125e497..14212b9cf4 100644
--- a/tests/docker/dockerfiles/debian-bootstrap.docker
+++ b/tests/docker/dockerfiles/debian-bootstrap.docker
@@ -17,5 +17,3 @@ RUN /debootstrap/debootstrap --second-stage
# At this point we can install additional packages if we want
# Duplicate deb line as deb-src
RUN cat /etc/apt/sources.list | sed "s/deb/deb-src/" >> /etc/apt/sources.list
-RUN apt-get update
-RUN apt-get -y build-dep qemu
diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre
index 7c76dce663..56e1aa7a21 100755
--- a/tests/docker/dockerfiles/debian-bootstrap.pre
+++ b/tests/docker/dockerfiles/debian-bootstrap.pre
@@ -32,6 +32,15 @@ if [ -z "${DEB_TYPE}" ]; then
fi
+# The following allow finer grain control over the defaults
+if [ -z "${DEB_VARIANT}" ]; then
+ DEB_VARIANT=buildd
+fi
+
+if [ -z "${DEB_URL}" ]; then
+ DEB_URL="http://httpredir.debian.org/debian"
+fi
+
# We check in order for
#
# - DEBOOTSTRAP_DIR pointing at a development checkout
@@ -107,5 +116,5 @@ fi
echo "Building a rootfs using ${FAKEROOT} and ${DEBOOTSTRAP} ${DEB_ARCH}/${DEB_TYPE}"
-${FAKEROOT} ${DEBOOTSTRAP} --variant=buildd --foreign --arch=$DEB_ARCH $DEB_TYPE . http://httpredir.debian.org/debian || exit 1
+${FAKEROOT} ${DEBOOTSTRAP} --variant=$DEB_VARIANT --foreign --arch=$DEB_ARCH $DEB_TYPE . $DEB_URL || exit 1
exit 0
diff --git a/tests/docker/dockerfiles/debian-powerpc-user-cross.docker b/tests/docker/dockerfiles/debian-powerpc-user-cross.docker
new file mode 100644
index 0000000000..6938a845ee
--- /dev/null
+++ b/tests/docker/dockerfiles/debian-powerpc-user-cross.docker
@@ -0,0 +1,15 @@
+#
+# Docker powerpc cross-compiler target for QEMU
+#
+# We can't use current Debian stable cross-compilers to build powerpc
+# as it has been dropped as a release architecture. Using Debian Sid
+# is just far too sketchy a build environment. This leaves us the
+# final option of using linux-user. This image is based of the
+# debootstrapped qemu:debian-powerpc-user but doesn't need any extra
+# magic once it is setup.
+#
+FROM qemu:debian-powerpc-user
+
+RUN echo man-db man-db/auto-update boolean false | debconf-set-selections
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get build-dep -yy qemu
diff --git a/tests/docker/dockerfiles/debian8-mxe.docker b/tests/docker/dockerfiles/debian8-mxe.docker
index 9b8e577b03..2df4cc8c5c 100644
--- a/tests/docker/dockerfiles/debian8-mxe.docker
+++ b/tests/docker/dockerfiles/debian8-mxe.docker
@@ -14,6 +14,6 @@ RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
apt-get install -y --no-install-recommends \
libpython2.7-stdlib \
- $(apt-get -s install -y --no-install-recommends gw32.shared-mingw-w64 gw32.shared-mingw-w64 | egrep "^Inst mxe-x86-64-unknown-" | cut -d\ -f2)
+ $(apt-get -s install -y --no-install-recommends gw32.shared-mingw-w64 | egrep "^Inst mxe-x86-64-unknown-" | cut -d\ -f2)
ENV PATH $PATH:/usr/lib/mxe/usr/bin/
diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker
index c5ad39b533..03ebfb0ef2 100644
--- a/tests/docker/dockerfiles/travis.docker
+++ b/tests/docker/dockerfiles/travis.docker
@@ -5,7 +5,7 @@ ENV LC_ALL en_US.UTF-8
RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y build-dep qemu
-RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools
+RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools gcovr
# Travis tools require PhantomJS / Neo4j / Maven accessible
# in their PATH (QEMU build won't access them).
ENV PATH /usr/local/phantomjs/bin:/usr/local/phantomjs:/usr/local/neo4j-3.2.7/bin:/usr/local/maven-3.5.2/bin:/usr/local/cmake-3.9.2/bin:/usr/local/clang-5.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker
index dabbf2a8a4..7d724e7f53 100644
--- a/tests/docker/dockerfiles/ubuntu.docker
+++ b/tests/docker/dockerfiles/ubuntu.docker
@@ -1,18 +1,18 @@
FROM ubuntu:16.04
RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty universe multiverse" >> \
/etc/apt/sources.list
-RUN apt-get update
ENV PACKAGES flex bison \
libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \
libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev \
libspice-protocol-dev libnss3-dev libfdt-dev \
- libgtk-3-dev libvte-2.91-dev libsdl1.2-dev libpng12-dev libpixman-1-dev \
+ libgtk-3-dev libvte-2.91-dev libsdl2-dev libpng12-dev libpixman-1-dev \
libvdeplug-dev liblzo2-dev libsnappy-dev libbz2-dev libxen-dev librdmacm-dev libibverbs-dev \
libsasl2-dev libjpeg-turbo8-dev xfslibs-dev libcap-ng-dev libbrlapi-dev libcurl4-gnutls-dev \
libbluetooth-dev librbd-dev libaio-dev glusterfs-common libnuma-dev libepoxy-dev libdrm-dev libgbm-dev \
libjemalloc-dev libcacard-dev libusbredirhost-dev libnfs-dev libcap-dev libattr1-dev \
texinfo \
gettext git make ccache python-yaml gcc clang sparse
-RUN apt-get -y install $PACKAGES
+RUN apt-get update && \
+ apt-get -y install $PACKAGES
RUN dpkg -l $PACKAGES | sort > /packages.txt
-ENV FEATURES clang pyyaml
+ENV FEATURES clang pyyaml sdl2
diff --git a/tests/migration-test.c b/tests/migration-test.c
index 3a85446f95..331efb0fe5 100644
--- a/tests/migration-test.c
+++ b/tests/migration-test.c
@@ -360,7 +360,7 @@ static void migrate_start_postcopy(QTestState *who)
qobject_unref(rsp);
}
-static void test_migrate_start(QTestState **from, QTestState **to,
+static int test_migrate_start(QTestState **from, QTestState **to,
const char *uri, bool hide_stderr)
{
gchar *cmd_src, *cmd_dst;
@@ -385,9 +385,13 @@ static void test_migrate_start(QTestState **from, QTestState **to,
accel, tmpfs, bootpath, uri);
} else if (strcmp(arch, "ppc64") == 0) {
- /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */
+ /* On ppc64, the test only works with kvm-hv, but not with kvm-pr
+ * and TCG is touchy due to race conditions on dirty bits
+ * (especially on PPC for some reason)
+ */
if (access("/sys/module/kvm_hv", F_OK)) {
- accel = "tcg";
+ g_print("Skipping test: kvm_hv not available ");
+ return -1;
}
cmd_src = g_strdup_printf("-machine accel=%s -m 256M"
" -name source,debug-threads=on"
@@ -424,6 +428,7 @@ static void test_migrate_start(QTestState **from, QTestState **to,
*to = qtest_init(cmd_dst);
g_free(cmd_dst);
+ return 0;
}
static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
@@ -510,7 +515,9 @@ static void test_postcopy(void)
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
- test_migrate_start(&from, &to, uri, false);
+ if (test_migrate_start(&from, &to, uri, false)) {
+ return;
+ }
migrate_set_capability(from, "postcopy-ram", "true");
migrate_set_capability(to, "postcopy-ram", "true");
@@ -556,7 +563,9 @@ static void test_baddest(void)
const char *status;
bool failed;
- test_migrate_start(&from, &to, "tcp:0:0", true);
+ if (test_migrate_start(&from, &to, "tcp:0:0", true)) {
+ return;
+ }
migrate(from, "tcp:0:0");
do {
rsp = wait_command(from, "{ 'execute': 'query-migrate' }");
@@ -585,7 +594,9 @@ static void test_precopy_unix(void)
char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
- test_migrate_start(&from, &to, uri, false);
+ if (test_migrate_start(&from, &to, uri, false)) {
+ return;
+ }
/* We want to pick a speed slow enough that the test completes
* quickly, but that it doesn't complete precopy even on a slow
diff --git a/tests/qapi-schema/bad-if-empty-list.err b/tests/qapi-schema/bad-if-empty-list.err
new file mode 100644
index 0000000000..75fe6497bc
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty-list.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless
diff --git a/tests/qapi-schema/bad-if-empty-list.exit b/tests/qapi-schema/bad-if-empty-list.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty-list.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-if-empty-list.json b/tests/qapi-schema/bad-if-empty-list.json
new file mode 100644
index 0000000000..94f2eb8670
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty-list.json
@@ -0,0 +1,3 @@
+# check empty 'if' list
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+ 'if': [] }
diff --git a/tests/qapi-schema/bad-if-empty-list.out b/tests/qapi-schema/bad-if-empty-list.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty-list.out
diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err
new file mode 100644
index 0000000000..358bdc3e51
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense
diff --git a/tests/qapi-schema/bad-if-empty.exit b/tests/qapi-schema/bad-if-empty.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-if-empty.json b/tests/qapi-schema/bad-if-empty.json
new file mode 100644
index 0000000000..fe1dd4eca6
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty.json
@@ -0,0 +1,3 @@
+# check empty 'if'
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+ 'if': '' }
diff --git a/tests/qapi-schema/bad-if-empty.out b/tests/qapi-schema/bad-if-empty.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-if-empty.out
diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err
new file mode 100644
index 0000000000..0af6316f78
--- /dev/null
+++ b/tests/qapi-schema/bad-if-list.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense
diff --git a/tests/qapi-schema/bad-if-list.exit b/tests/qapi-schema/bad-if-list.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-if-list.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-if-list.json b/tests/qapi-schema/bad-if-list.json
new file mode 100644
index 0000000000..49ced9b9ca
--- /dev/null
+++ b/tests/qapi-schema/bad-if-list.json
@@ -0,0 +1,3 @@
+# check invalid 'if' content
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+ 'if': ['foo', ''] }
diff --git a/tests/qapi-schema/bad-if-list.out b/tests/qapi-schema/bad-if-list.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-if-list.out
diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err
new file mode 100644
index 0000000000..c2e3f5f44c
--- /dev/null
+++ b/tests/qapi-schema/bad-if.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings
diff --git a/tests/qapi-schema/bad-if.exit b/tests/qapi-schema/bad-if.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-if.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json
new file mode 100644
index 0000000000..3edd1a0bf2
--- /dev/null
+++ b/tests/qapi-schema/bad-if.json
@@ -0,0 +1,3 @@
+# check invalid 'if' type
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+ 'if': { 'value': 'defined(TEST_IF_STRUCT)' } }
diff --git a/tests/qapi-schema/bad-if.out b/tests/qapi-schema/bad-if.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/bad-if.out
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 97ab4625ff..984cd8ed06 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -55,7 +55,7 @@
#
# @two is undocumented
##
-{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
##
# @Base:
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 9c8a4838e1..35f3f1164c 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -3,6 +3,7 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
prefix QTYPE
module doc-good.json
enum Enum ['one', 'two']
+ if ['defined(IFCOND)']
object Base
member base1: Enum optional=False
object Variant1
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index 0aed2300a5..e42eace474 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -89,6 +89,8 @@ Not documented
@end table
@code{two} is undocumented
+
+@b{If:} @code{defined(IFCOND)}
@end deftp
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 7b59817f04..11aa4c8f8d 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -56,6 +56,9 @@
'data': { 'string0': 'str',
'dict1': 'UserDefTwoDict' } }
+{ 'struct': 'UserDefThree',
+ 'data': { 'string0': 'str' } }
+
# dummy struct to force generation of array types not otherwise mentioned
{ 'struct': 'ForceArrays',
'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
@@ -141,7 +144,7 @@
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
-# Smoke test on Out-Of-Band and allow-preconfig-test
+# Smoke test on out-of-band and allow-preconfig-test
{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true }
# For testing integer range flattening in opts-visitor. The following schema
@@ -193,3 +196,26 @@
'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
'returns': '__org.qemu_x-Union1' }
+
+# test 'if' condition handling
+
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+ 'if': 'defined(TEST_IF_STRUCT)' }
+
+{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
+ 'if': 'defined(TEST_IF_ENUM)' }
+
+{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
+ 'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
+
+{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
+ 'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
+
+{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
+ 'returns': 'UserDefThree',
+ 'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
+
+{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' }
+
+{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
+ 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 0dbcdafa3c..0da92455da 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -36,6 +36,8 @@ object UserDefTwoDict
object UserDefTwo
member string0: str optional=False
member dict1: UserDefTwoDict optional=False
+object UserDefThree
+ member string0: str optional=False
object ForceArrays
member unused1: UserDefOneList optional=False
member unused2: UserDefTwoList optional=False
@@ -233,3 +235,36 @@ object q_obj___org.qemu_x-command-arg
member d: __org.qemu_x-Alt optional=False
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
gen=True success_response=True boxed=False oob=False preconfig=False
+object TestIfStruct
+ member foo: int optional=False
+ if ['defined(TEST_IF_STRUCT)']
+enum TestIfEnum ['foo', 'bar']
+ if ['defined(TEST_IF_ENUM)']
+object q_obj_TestStruct-wrapper
+ member data: TestStruct optional=False
+enum TestIfUnionKind ['foo']
+ if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
+object TestIfUnion
+ member type: TestIfUnionKind optional=False
+ tag type
+ case foo: q_obj_TestStruct-wrapper
+ if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
+alternate TestIfAlternate
+ tag type
+ case foo: int
+ case bar: TestStruct
+ if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
+object q_obj_TestIfCmd-arg
+ member foo: TestIfStruct optional=False
+ if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
+command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
+ gen=True success_response=True boxed=False oob=False preconfig=False
+ if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
+command TestCmdReturnDefThree None -> UserDefThree
+ gen=True success_response=True boxed=False oob=False preconfig=False
+object q_obj_TestIfEvent-arg
+ member foo: TestIfStruct optional=False
+ if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
+event TestIfEvent q_obj_TestIfEvent-arg
+ boxed=False
+ if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 4512a41504..f514fe71e4 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -23,12 +23,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
def visit_include(self, name, info):
print('include %s' % name)
- def visit_enum_type(self, name, info, values, prefix):
+ def visit_enum_type(self, name, info, ifcond, values, prefix):
print('enum %s %s' % (name, values))
if prefix:
print(' prefix %s' % prefix)
+ self._print_if(ifcond)
- def visit_object_type(self, name, info, base, members, variants):
+ def visit_object_type(self, name, info, ifcond, base, members, variants):
print('object %s' % name)
if base:
print(' base %s' % base.name)
@@ -36,21 +37,25 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
print(' member %s: %s optional=%s' % \
(m.name, m.type.name, m.optional))
self._print_variants(variants)
+ self._print_if(ifcond)
- def visit_alternate_type(self, name, info, variants):
+ def visit_alternate_type(self, name, info, ifcond, variants):
print('alternate %s' % name)
self._print_variants(variants)
+ self._print_if(ifcond)
- def visit_command(self, name, info, arg_type, ret_type, gen,
+ def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \
(gen, success_response, boxed, allow_oob, allow_preconfig))
+ self._print_if(ifcond)
- def visit_event(self, name, info, arg_type, boxed):
+ def visit_event(self, name, info, ifcond, arg_type, boxed):
print('event %s %s' % (name, arg_type and arg_type.name))
print(' boxed=%s' % boxed)
+ self._print_if(ifcond)
@staticmethod
def _print_variants(variants):
@@ -59,6 +64,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
for v in variants.variants:
print(' case %s: %s' % (v.name, v.type.name))
+ @staticmethod
+ def _print_if(ifcond, indent=4):
+ if ifcond:
+ print('%sif %s' % (' ' * indent, ifcond))
+
try:
schema = QAPISchema(sys.argv[1])
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index f617e25e24..ee9c820d0f 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -100,6 +100,21 @@ run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2
run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2
echo
+echo === Node names ===
+echo
+
+# Maximum length: 31 characters
+run_qemu -drive file="$TEST_IMG",node-name=x123456789012345678901234567890
+run_qemu -drive file="$TEST_IMG",node-name=x1234567890123456789012345678901
+
+# First character must be alphabetic
+# Following characters alphanumeric or -._
+run_qemu -drive file="$TEST_IMG",node-name=All-Types.of_all0wed_chars
+run_qemu -drive file="$TEST_IMG",node-name=123foo
+run_qemu -drive file="$TEST_IMG",node-name=_foo
+run_qemu -drive file="$TEST_IMG",node-name=foo#12
+
+echo
echo === Device without drive ===
echo
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index dd9846d1ce..b7273505c7 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format'
+=== Node names ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) quit
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) quit
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name
+
+
=== Device without drive ===
Testing: -device VIRTIO_SCSI -device scsi-hd
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index b01f9a90d7..e9257fe318 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format'
+=== Node names ===
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) quit
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars
+QEMU X.Y.Z monitor - type 'help' for more information
+(qemu) quit
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name
+
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name
+
+
=== Device without drive ===
Testing: -device VIRTIO_SCSI -device scsi-hd
diff --git a/tests/qmp-test.c b/tests/qmp-test.c
index a49cbc6fde..ceaf4a6789 100644
--- a/tests/qmp-test.c
+++ b/tests/qmp-test.c
@@ -135,16 +135,65 @@ static void test_qmp_protocol(void)
qtest_quit(qts);
}
-/* Tests for Out-Of-Band support. */
+/* Out-of-band tests */
+
+char tmpdir[] = "/tmp/qmp-test-XXXXXX";
+char *fifo_name;
+
+static void setup_blocking_cmd(void)
+{
+ if (!mkdtemp(tmpdir)) {
+ g_error("mkdtemp: %s", strerror(errno));
+ }
+ fifo_name = g_strdup_printf("%s/fifo", tmpdir);
+ if (mkfifo(fifo_name, 0666)) {
+ g_error("mkfifo: %s", strerror(errno));
+ }
+}
+
+static void cleanup_blocking_cmd(void)
+{
+ unlink(fifo_name);
+ rmdir(tmpdir);
+}
+
+static void send_cmd_that_blocks(QTestState *s, const char *id)
+{
+ qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s,"
+ " 'arguments': {"
+ " 'driver': 'blkdebug', 'node-name': %s,"
+ " 'config': %s,"
+ " 'image': { 'driver': 'null-co' } } }",
+ id, id, fifo_name);
+}
+
+static void unblock_blocked_cmd(void)
+{
+ int fd = open(fifo_name, O_WRONLY);
+ g_assert(fd >= 0);
+ close(fd);
+}
+
+static void send_oob_cmd_that_fails(QTestState *s, const char *id)
+{
+ qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id);
+}
+
+static void recv_cmd_id(QTestState *s, const char *id)
+{
+ QDict *resp = qtest_qmp_receive(s);
+
+ g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
+ qobject_unref(resp);
+}
+
static void test_qmp_oob(void)
{
QTestState *qts;
QDict *resp, *q;
- int acks = 0;
const QListEntry *entry;
QList *capabilities;
QString *qstr;
- const char *cmd_id;
qts = qtest_init_without_qmp_handshake(true, common_args);
@@ -179,43 +228,33 @@ static void test_qmp_oob(void)
* Try any command that does not support OOB but with OOB flag. We
* should get failure.
*/
- resp = qtest_qmp(qts,
- "{ 'execute': 'query-cpus',"
- " 'control': { 'run-oob': true } }");
+ resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }");
g_assert(qdict_haskey(resp, "error"));
qobject_unref(resp);
- /*
- * First send the "x-oob-test" command with lock=true and
- * oob=false, it should hang the dispatcher and main thread;
- * later, we send another lock=false with oob=true to continue
- * that thread processing. Finally we should receive replies from
- * both commands.
- */
- qtest_async_qmp(qts,
- "{ 'execute': 'x-oob-test',"
- " 'arguments': { 'lock': true }, "
- " 'id': 'lock-cmd'}");
- qtest_async_qmp(qts,
- "{ 'execute': 'x-oob-test', "
- " 'arguments': { 'lock': false }, "
- " 'control': { 'run-oob': true }, "
- " 'id': 'unlock-cmd' }");
-
- /* Ignore all events. Wait for 2 acks */
- while (acks < 2) {
- resp = qtest_qmp_receive(qts);
- cmd_id = qdict_get_str(resp, "id");
- if (!g_strcmp0(cmd_id, "lock-cmd") ||
- !g_strcmp0(cmd_id, "unlock-cmd")) {
- acks++;
- }
- qobject_unref(resp);
- }
+ /* OOB command overtakes slow in-band command */
+ setup_blocking_cmd();
+ send_cmd_that_blocks(qts, "ib-blocks-1");
+ qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }");
+ send_oob_cmd_that_fails(qts, "oob-1");
+ recv_cmd_id(qts, "oob-1");
+ unblock_blocked_cmd();
+ recv_cmd_id(qts, "ib-blocks-1");
+ recv_cmd_id(qts, "ib-quick-1");
+
+ /* Even malformed in-band command fails in-band */
+ send_cmd_that_blocks(qts, "blocks-2");
+ qtest_async_qmp(qts, "{ 'id': 'err-2' }");
+ unblock_blocked_cmd();
+ recv_cmd_id(qts, "blocks-2");
+ recv_cmd_id(qts, "err-2");
+ cleanup_blocking_cmd();
qtest_quit(qts);
}
+/* Query smoke tests */
+
static int query_error_class(const char *cmd)
{
static struct {
@@ -392,6 +431,8 @@ static void add_query_tests(QmpSchema *schema)
}
}
+/* Preconfig tests */
+
static void test_qmp_preconfig(void)
{
QDict *rsp, *ret;
diff --git a/tests/test-qga.c b/tests/test-qga.c
index 30c9643257..d638b1571a 100644
--- a/tests/test-qga.c
+++ b/tests/test-qga.c
@@ -227,6 +227,38 @@ static void test_qga_ping(gconstpointer fix)
qobject_unref(ret);
}
+static void test_qga_invalid_id(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ QDict *ret, *error;
+ const char *class;
+
+ ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
+ g_assert_nonnull(ret);
+
+ error = qdict_get_qdict(ret, "error");
+ class = qdict_get_try_str(error, "class");
+ g_assert_cmpstr(class, ==, "GenericError");
+
+ qobject_unref(ret);
+}
+
+static void test_qga_invalid_oob(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ QDict *ret, *error;
+ const char *class;
+
+ ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}");
+ g_assert_nonnull(ret);
+
+ error = qdict_get_qdict(ret, "error");
+ class = qdict_get_try_str(error, "class");
+ g_assert_cmpstr(class, ==, "GenericError");
+
+ qobject_unref(ret);
+}
+
static void test_qga_invalid_args(gconstpointer fix)
{
const TestFixture *fixture = fix;
@@ -854,6 +886,54 @@ static void test_qga_guest_exec_invalid(gconstpointer fix)
qobject_unref(ret);
}
+static void test_qga_guest_get_host_name(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ QDict *ret, *val;
+
+ ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}");
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+
+ val = qdict_get_qdict(ret, "return");
+ g_assert(qdict_haskey(val, "host-name"));
+
+ qobject_unref(ret);
+}
+
+static void test_qga_guest_get_timezone(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ QDict *ret, *val;
+
+ ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}");
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+
+ /* Make sure there's at least offset */
+ val = qdict_get_qdict(ret, "return");
+ g_assert(qdict_haskey(val, "offset"));
+
+ qobject_unref(ret);
+}
+
+static void test_qga_guest_get_users(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ QDict *ret;
+ QList *val;
+
+ ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}");
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+
+ /* There is not much to test here */
+ val = qdict_get_qlist(ret, "return");
+ g_assert_nonnull(val);
+
+ qobject_unref(ret);
+}
+
static void test_qga_guest_get_osinfo(gconstpointer data)
{
TestFixture fixture;
@@ -934,6 +1014,8 @@ int main(int argc, char **argv)
g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
+ g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id);
+ g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
g_test_add_data_func("/qga/fsfreeze-status", &fix,
@@ -946,6 +1028,12 @@ int main(int argc, char **argv)
test_qga_guest_exec_invalid);
g_test_add_data_func("/qga/guest-get-osinfo", &fix,
test_qga_guest_get_osinfo);
+ g_test_add_data_func("/qga/guest-get-host-name", &fix,
+ test_qga_guest_get_host_name);
+ g_test_add_data_func("/qga/guest-get-timezone", &fix,
+ test_qga_guest_get_timezone);
+ g_test_add_data_func("/qga/guest-get-users", &fix,
+ test_qga_guest_get_users);
ret = g_test_run();
diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c
index 491b0c4a44..ba41a6161e 100644
--- a/tests/test-qmp-cmds.c
+++ b/tests/test-qmp-cmds.c
@@ -12,6 +12,18 @@
static QmpCommandList qmp_commands;
+#if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD)
+UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp)
+{
+ return NULL;
+}
+#endif
+
+UserDefThree *qmp_TestCmdReturnDefThree(Error **errp)
+{
+ return NULL;
+}
+
void qmp_user_def_cmd(Error **errp)
{
}
@@ -98,13 +110,13 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
static void test_dispatch_cmd(void)
{
QDict *req = qdict_new();
- QObject *resp;
+ QDict *resp;
qdict_put_str(req, "execute", "user_def_cmd");
- resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
+ resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp != NULL);
- assert(!qdict_haskey(qobject_to(QDict, resp), "error"));
+ assert(!qdict_haskey(resp, "error"));
qobject_unref(resp);
qobject_unref(req);
@@ -115,13 +127,13 @@ static void test_dispatch_cmd_failure(void)
{
QDict *req = qdict_new();
QDict *args = qdict_new();
- QObject *resp;
+ QDict *resp;
qdict_put_str(req, "execute", "user_def_cmd2");
- resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
+ resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp != NULL);
- assert(qdict_haskey(qobject_to(QDict, resp), "error"));
+ assert(qdict_haskey(resp, "error"));
qobject_unref(resp);
qobject_unref(req);
@@ -133,9 +145,9 @@ static void test_dispatch_cmd_failure(void)
qdict_put_str(req, "execute", "user_def_cmd");
- resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
+ resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp != NULL);
- assert(qdict_haskey(qobject_to(QDict, resp), "error"));
+ assert(qdict_haskey(resp, "error"));
qobject_unref(resp);
qobject_unref(req);
@@ -143,18 +155,15 @@ static void test_dispatch_cmd_failure(void)
static QObject *test_qmp_dispatch(QDict *req)
{
- QObject *resp_obj;
QDict *resp;
QObject *ret;
- resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req));
- assert(resp_obj);
- resp = qobject_to(QDict, resp_obj);
+ resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp && !qdict_haskey(resp, "error"));
ret = qdict_get(resp, "return");
assert(ret);
qobject_ref(ret);
- qobject_unref(resp_obj);
+ qobject_unref(resp);
return ret;
}
diff --git a/ui/vnc.h b/ui/vnc.h
index 762632929b..a86e0610e8 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -297,7 +297,9 @@ struct VncState
bool encode_ws;
bool websocket;
+#ifdef CONFIG_VNC
VncClientInfo *info;
+#endif
/* Job thread bottom half has put data for a forced update
* into the output buffer. This offset points to the end of