summaryrefslogtreecommitdiffstats
path: root/debian/patches/v7.2.9.diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/patches/v7.2.9.diff3566
1 files changed, 3566 insertions, 0 deletions
diff --git a/debian/patches/v7.2.9.diff b/debian/patches/v7.2.9.diff
new file mode 100644
index 00000000..57ed18b6
--- /dev/null
+++ b/debian/patches/v7.2.9.diff
@@ -0,0 +1,3566 @@
+Subject: v7.2.9
+Date: Mon Jan 29 12:49:21 2024 +0300
+Tagger: Michael Tokarev <mjt@tls.msk.ru>
+Forwarded: not-needed
+
+This is a difference between upstream qemu v7.2.9
+and upstream qemu v7.2.9.
+
+ .gitlab-ci.d/buildtest.yml | 5 +-
+ .readthedocs.yml | 19 +-
+ VERSION | 2 +-
+ accel/tcg/cpu-exec.c | 4 +-
+ accel/tcg/tb-maint.c | 6 +-
+ accel/tcg/translate-all.c | 2 -
+ block/blklogwrites.c | 35 ++-
+ block/io.c | 10 +
+ block/snapshot.c | 4 +-
+ chardev/char.c | 2 +-
+ docs/requirements.txt | 2 +
+ hw/block/pflash_cfi01.c | 171 ++++++------
+ hw/block/pflash_cfi02.c | 2 +-
+ hw/block/trace-events | 7 +-
+ hw/intc/arm_gicv3_cpuif.c | 17 +-
+ hw/net/virtio-net.c | 13 +-
+ hw/scsi/esp-pci.c | 61 ++--
+ include/exec/exec-all.h | 6 -
+ include/hw/elf_ops.h | 2 +-
+ monitor/qmp.c | 17 --
+ qapi/qmp-dispatch.c | 24 +-
+ softmmu/vl.c | 4 +
+ target/i386/cpu.h | 9 +-
+ target/i386/tcg/tcg-cpu.c | 25 +-
+ target/i386/tcg/translate.c | 22 +-
+ target/riscv/csr.c | 14 +-
+ target/s390x/tcg/translate.c | 3 +-
+ target/xtensa/mmu_helper.c | 47 +++-
+ tests/qemu-iotests/060.out | 4 +-
+ tests/qemu-iotests/071.out | 4 +-
+ tests/qemu-iotests/081.out | 16 +-
+ tests/qemu-iotests/087.out | 12 +-
+ tests/qemu-iotests/108.out | 2 +-
+ tests/qemu-iotests/109 | 4 +-
+ tests/qemu-iotests/109.out | 78 +++---
+ tests/qemu-iotests/117.out | 2 +-
+ tests/qemu-iotests/120.out | 2 +-
+ tests/qemu-iotests/127.out | 2 +-
+ tests/qemu-iotests/140.out | 2 +-
+ tests/qemu-iotests/141 | 307 +++++++++------------
+ tests/qemu-iotests/141.out | 200 +++-----------
+ tests/qemu-iotests/143.out | 2 +-
+ tests/qemu-iotests/156.out | 2 +-
+ tests/qemu-iotests/176.out | 16 +-
+ tests/qemu-iotests/182.out | 2 +-
+ tests/qemu-iotests/183.out | 4 +-
+ tests/qemu-iotests/184.out | 32 +--
+ tests/qemu-iotests/185 | 6 +-
+ tests/qemu-iotests/185.out | 45 ++-
+ tests/qemu-iotests/191.out | 16 +-
+ tests/qemu-iotests/195.out | 16 +-
+ tests/qemu-iotests/223.out | 12 +-
+ tests/qemu-iotests/227.out | 32 +--
+ tests/qemu-iotests/247.out | 2 +-
+ tests/qemu-iotests/273.out | 8 +-
+ tests/qemu-iotests/308 | 4 +-
+ tests/qemu-iotests/308.out | 2 +-
+ tests/qemu-iotests/iotests.py | 7 +
+ tests/qemu-iotests/tests/qcow2-internal-snapshots | 170 ++++++++++++
+ .../tests/qcow2-internal-snapshots.out | 107 +++++++
+ tests/qemu-iotests/tests/qsd-jobs.out | 4 +-
+ tests/qtest/meson.build | 1 +
+ 62 files changed, 979 insertions(+), 681 deletions(-)
+
+diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
+index 10886bb414..7243b8079b 100644
+--- a/.gitlab-ci.d/buildtest.yml
++++ b/.gitlab-ci.d/buildtest.yml
+@@ -637,7 +637,10 @@ pages:
+ - mkdir -p public
+ # HTML-ised source tree
+ - make gtags
+- - htags -anT --tree-view=filetree -m qemu_init
++ # We unset variables to work around a bug in some htags versions
++ # which causes it to fail when the environment is large
++ - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags
++ -anT --tree-view=filetree -m qemu_init
+ -t "Welcome to the QEMU sourcecode"
+ - mv HTML public/src
+ # Project documentation
+diff --git a/.readthedocs.yml b/.readthedocs.yml
+index 7fb7b8dd61..0b262469ce 100644
+--- a/.readthedocs.yml
++++ b/.readthedocs.yml
+@@ -5,16 +5,21 @@
+ # Required
+ version: 2
+
++# Set the version of Python and other tools you might need
++build:
++ os: ubuntu-22.04
++ tools:
++ python: "3.11"
++
+ # Build documentation in the docs/ directory with Sphinx
+ sphinx:
+ configuration: docs/conf.py
+
++# We recommend specifying your dependencies to enable reproducible builds:
++# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
++python:
++ install:
++ - requirements: docs/requirements.txt
++
+ # We want all the document formats
+ formats: all
+-
+-# For consistency, we require that QEMU's Sphinx extensions
+-# run with at least the same minimum version of Python that
+-# we require for other Python in our codebase (our conf.py
+-# enforces this, and some code needs it.)
+-python:
+- version: 3.6
+diff --git a/VERSION b/VERSION
+index 31554632ab..672f66a613 100644
+--- a/VERSION
++++ b/VERSION
+@@ -1 +1 @@
+-7.2.8
++7.2.9
+diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
+index 356fe348de..68fef3e01f 100644
+--- a/accel/tcg/cpu-exec.c
++++ b/accel/tcg/cpu-exec.c
+@@ -186,7 +186,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
+ const TranslationBlock *tb = p;
+ const struct tb_desc *desc = d;
+
+- if ((TARGET_TB_PCREL || tb_pc(tb) == desc->pc) &&
++ if (tb_pc(tb) == desc->pc &&
+ tb_page_addr0(tb) == desc->page_addr0 &&
+ tb->cs_base == desc->cs_base &&
+ tb->flags == desc->flags &&
+@@ -238,7 +238,7 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
+ return NULL;
+ }
+ desc.page_addr0 = phys_pc;
+- h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : pc),
++ h = tb_hash_func(phys_pc, pc,
+ flags, cflags, *cpu->trace_dstate);
+ return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
+ }
+diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
+index 0cdb35548c..9d9f651c78 100644
+--- a/accel/tcg/tb-maint.c
++++ b/accel/tcg/tb-maint.c
+@@ -34,7 +34,7 @@ static bool tb_cmp(const void *ap, const void *bp)
+ const TranslationBlock *a = ap;
+ const TranslationBlock *b = bp;
+
+- return ((TARGET_TB_PCREL || tb_pc(a) == tb_pc(b)) &&
++ return (tb_pc(a) == tb_pc(b) &&
+ a->cs_base == b->cs_base &&
+ a->flags == b->flags &&
+ (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
+@@ -269,7 +269,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
+
+ /* remove the TB from the hash list */
+ phys_pc = tb_page_addr0(tb);
+- h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
++ h = tb_hash_func(phys_pc, tb_pc(tb),
+ tb->flags, orig_cflags, tb->trace_vcpu_dstate);
+ if (!qht_remove(&tb_ctx.htable, tb, h)) {
+ return;
+@@ -459,7 +459,7 @@ TranslationBlock *tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
+ }
+
+ /* add in the hash table */
+- h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
++ h = tb_hash_func(phys_pc, tb_pc(tb),
+ tb->flags, tb->cflags, tb->trace_vcpu_dstate);
+ qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
+
+diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
+index ac3ee3740c..ed8ddee6e8 100644
+--- a/accel/tcg/translate-all.c
++++ b/accel/tcg/translate-all.c
+@@ -818,9 +818,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
+
+ gen_code_buf = tcg_ctx->code_gen_ptr;
+ tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
+-#if !TARGET_TB_PCREL
+ tb->pc = pc;
+-#endif
+ tb->cs_base = cs_base;
+ tb->flags = flags;
+ tb->cflags = cflags;
+diff --git a/block/blklogwrites.c b/block/blklogwrites.c
+index cef9efe55d..ad589c4a2e 100644
+--- a/block/blklogwrites.c
++++ b/block/blklogwrites.c
+@@ -321,22 +321,39 @@ typedef struct {
+ 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;
++ /*
++ * Determine the offsets and sizes of different parts of the entry, and
++ * update the state of the driver.
++ *
++ * This needs to be done in one go, before any actual I/O is done, as the
++ * log entry may have to be written in two parts, and the state of the
++ * driver may be modified by other driver operations while waiting for the
++ * I/O to complete.
++ */
++ const uint64_t entry_start_sector = s->cur_log_sector;
++ const uint64_t entry_offset = entry_start_sector << s->sectorbits;
++ const uint64_t qiov_aligned_size = ROUND_UP(lr->qiov->size, s->sectorsize);
++ const uint64_t entry_aligned_size = qiov_aligned_size +
++ ROUND_UP(lr->zero_size, s->sectorsize);
++ const uint64_t entry_nr_sectors = entry_aligned_size >> s->sectorbits;
+
+- lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
++ s->nr_entries++;
++ s->cur_log_sector += entry_nr_sectors;
++
++ /*
++ * Write the log entry. Note that if this is a "write zeroes" operation,
++ * only the entry header is written here, with the zeroing being done
++ * separately below.
++ */
++ lr->log_ret = bdrv_co_pwritev(s->log_file, entry_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;
++ const uint64_t zeroes_offset = entry_offset + qiov_aligned_size;
+
+- lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
++ lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, zeroes_offset,
+ lr->zero_size, 0);
+ }
+
+diff --git a/block/io.c b/block/io.c
+index bbaa0d1b2d..4589a58917 100644
+--- a/block/io.c
++++ b/block/io.c
+@@ -2595,6 +2595,16 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
+ ret |= (ret2 & BDRV_BLOCK_ZERO);
+ }
+ }
++
++ /*
++ * Now that the recursive search was done, clear the flag. Otherwise,
++ * with more complicated block graphs like snapshot-access ->
++ * copy-before-write -> qcow2, where the return value will be propagated
++ * further up to a parent bdrv_co_do_block_status() call, both the
++ * BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is
++ * not allowed.
++ */
++ ret &= ~BDRV_BLOCK_RECURSE;
+ }
+
+ out:
+diff --git a/block/snapshot.c b/block/snapshot.c
+index e22ac3eac6..86e29ca59f 100644
+--- a/block/snapshot.c
++++ b/block/snapshot.c
+@@ -190,8 +190,10 @@ static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs)
+ int bdrv_can_snapshot(BlockDriverState *bs)
+ {
+ BlockDriver *drv = bs->drv;
++
+ GLOBAL_STATE_CODE();
+- if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
++
++ if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) {
+ return 0;
+ }
+
+diff --git a/chardev/char.c b/chardev/char.c
+index b005df3ccf..193bbac054 100644
+--- a/chardev/char.c
++++ b/chardev/char.c
+@@ -519,7 +519,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp)
+
+ if (object_class_is_abstract(oc)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+- "an abstract device type");
++ "a non-abstract device type");
+ return NULL;
+ }
+
+diff --git a/docs/requirements.txt b/docs/requirements.txt
+new file mode 100644
+index 0000000000..691e5218ec
+--- /dev/null
++++ b/docs/requirements.txt
+@@ -0,0 +1,2 @@
++sphinx==5.3.0
++sphinx_rtd_theme==1.1.1
+diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
+index 0cbc2fb4cb..6bc00254cc 100644
+--- a/hw/block/pflash_cfi01.c
++++ b/hw/block/pflash_cfi01.c
+@@ -81,16 +81,39 @@ struct PFlashCFI01 {
+ uint16_t ident3;
+ uint8_t cfi_table[0x52];
+ uint64_t counter;
+- unsigned int writeblock_size;
++ uint32_t writeblock_size;
+ MemoryRegion mem;
+ char *name;
+ void *storage;
+ VMChangeStateEntry *vmstate;
+ bool old_multiple_chip_handling;
++
++ /* block update buffer */
++ unsigned char *blk_bytes;
++ uint32_t blk_offset;
+ };
+
+ static int pflash_post_load(void *opaque, int version_id);
+
++static bool pflash_blk_write_state_needed(void *opaque)
++{
++ PFlashCFI01 *pfl = opaque;
++
++ return (pfl->blk_offset != -1);
++}
++
++static const VMStateDescription vmstate_pflash_blk_write = {
++ .name = "pflash_cfi01_blk_write",
++ .version_id = 1,
++ .minimum_version_id = 1,
++ .needed = pflash_blk_write_state_needed,
++ .fields = (const VMStateField[]) {
++ VMSTATE_VBUFFER_UINT32(blk_bytes, PFlashCFI01, 0, NULL, writeblock_size),
++ VMSTATE_UINT32(blk_offset, PFlashCFI01),
++ VMSTATE_END_OF_LIST()
++ }
++};
++
+ static const VMStateDescription vmstate_pflash = {
+ .name = "pflash_cfi01",
+ .version_id = 1,
+@@ -102,6 +125,10 @@ static const VMStateDescription vmstate_pflash = {
+ VMSTATE_UINT8(status, PFlashCFI01),
+ VMSTATE_UINT64(counter, PFlashCFI01),
+ VMSTATE_END_OF_LIST()
++ },
++ .subsections = (const VMStateDescription * []) {
++ &vmstate_pflash_blk_write,
++ NULL
+ }
+ };
+
+@@ -226,34 +253,10 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
+ uint32_t ret;
+
+ p = pfl->storage;
+- switch (width) {
+- case 1:
+- ret = p[offset];
+- break;
+- case 2:
+- if (be) {
+- ret = p[offset] << 8;
+- ret |= p[offset + 1];
+- } else {
+- ret = p[offset];
+- ret |= p[offset + 1] << 8;
+- }
+- break;
+- case 4:
+- if (be) {
+- ret = p[offset] << 24;
+- ret |= p[offset + 1] << 16;
+- ret |= p[offset + 2] << 8;
+- ret |= p[offset + 3];
+- } else {
+- ret = p[offset];
+- ret |= p[offset + 1] << 8;
+- ret |= p[offset + 2] << 16;
+- ret |= p[offset + 3] << 24;
+- }
+- break;
+- default:
+- abort();
++ if (be) {
++ ret = ldn_be_p(p + offset, width);
++ } else {
++ ret = ldn_le_p(p + offset, width);
+ }
+ trace_pflash_data_read(pfl->name, offset, width, ret);
+ return ret;
+@@ -401,40 +404,61 @@ static void pflash_update(PFlashCFI01 *pfl, int offset,
+ }
+ }
+
++/* copy current flash content to block update buffer */
++static void pflash_blk_write_start(PFlashCFI01 *pfl, hwaddr offset)
++{
++ hwaddr mask = ~(pfl->writeblock_size - 1);
++
++ trace_pflash_write_block_start(pfl->name, pfl->counter);
++ pfl->blk_offset = offset & mask;
++ memcpy(pfl->blk_bytes, pfl->storage + pfl->blk_offset,
++ pfl->writeblock_size);
++}
++
++/* commit block update buffer changes */
++static void pflash_blk_write_flush(PFlashCFI01 *pfl)
++{
++ g_assert(pfl->blk_offset != -1);
++ trace_pflash_write_block_flush(pfl->name);
++ memcpy(pfl->storage + pfl->blk_offset, pfl->blk_bytes,
++ pfl->writeblock_size);
++ pflash_update(pfl, pfl->blk_offset, pfl->writeblock_size);
++ pfl->blk_offset = -1;
++}
++
++/* discard block update buffer changes */
++static void pflash_blk_write_abort(PFlashCFI01 *pfl)
++{
++ trace_pflash_write_block_abort(pfl->name);
++ pfl->blk_offset = -1;
++}
++
+ static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset,
+ uint32_t value, int width, int be)
+ {
+- uint8_t *p = pfl->storage;
++ uint8_t *p;
+
+- trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter);
+- switch (width) {
+- case 1:
+- p[offset] = value;
+- break;
+- case 2:
+- if (be) {
+- p[offset] = value >> 8;
+- p[offset + 1] = value;
+- } else {
+- p[offset] = value;
+- p[offset + 1] = value >> 8;
+- }
+- break;
+- case 4:
+- if (be) {
+- p[offset] = value >> 24;
+- p[offset + 1] = value >> 16;
+- p[offset + 2] = value >> 8;
+- p[offset + 3] = value;
+- } else {
+- p[offset] = value;
+- p[offset + 1] = value >> 8;
+- p[offset + 2] = value >> 16;
+- p[offset + 3] = value >> 24;
++ if (pfl->blk_offset != -1) {
++ /* block write: redirect writes to block update buffer */
++ if ((offset < pfl->blk_offset) ||
++ (offset + width > pfl->blk_offset + pfl->writeblock_size)) {
++ pfl->status |= 0x10; /* Programming error */
++ return;
+ }
+- break;
++ trace_pflash_data_write_block(pfl->name, offset, width, value,
++ pfl->counter);
++ p = pfl->blk_bytes + (offset - pfl->blk_offset);
++ } else {
++ /* write directly to storage */
++ trace_pflash_data_write(pfl->name, offset, width, value);
++ p = pfl->storage + offset;
+ }
+
++ if (be) {
++ stn_be_p(p, width, value);
++ } else {
++ stn_le_p(p, width, value);
++ }
+ }
+
+ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+@@ -549,9 +573,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+ } else {
+ value = extract32(value, 0, pfl->bank_width * 8);
+ }
+- trace_pflash_write_block(pfl->name, value);
+ pfl->counter = value;
+ pfl->wcycle++;
++ pflash_blk_write_start(pfl, offset);
+ break;
+ case 0x60:
+ if (cmd == 0xd0) {
+@@ -582,12 +606,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+ switch (pfl->cmd) {
+ case 0xe8: /* Block write */
+ /* FIXME check @offset, @width */
+- if (!pfl->ro) {
+- /*
+- * FIXME writing straight to memory is *wrong*. We
+- * should write to a buffer, and flush it to memory
+- * only on confirm command (see below).
+- */
++ if (!pfl->ro && (pfl->blk_offset != -1)) {
+ pflash_data_write(pfl, offset, value, width, be);
+ } else {
+ pfl->status |= 0x10; /* Programming error */
+@@ -596,18 +615,8 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+ pfl->status |= 0x80;
+
+ if (!pfl->counter) {
+- hwaddr mask = pfl->writeblock_size - 1;
+- mask = ~mask;
+-
+ trace_pflash_write(pfl->name, "block write finished");
+ pfl->wcycle++;
+- if (!pfl->ro) {
+- /* Flush the entire write buffer onto backing storage. */
+- /* FIXME premature! */
+- pflash_update(pfl, offset & mask, pfl->writeblock_size);
+- } else {
+- pfl->status |= 0x10; /* Programming error */
+- }
+ }
+
+ pfl->counter--;
+@@ -619,20 +628,17 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+ case 3: /* Confirm mode */
+ switch (pfl->cmd) {
+ case 0xe8: /* Block write */
+- if (cmd == 0xd0) {
+- /* FIXME this is where we should write out the buffer */
++ if ((cmd == 0xd0) && !(pfl->status & 0x10)) {
++ pflash_blk_write_flush(pfl);
+ pfl->wcycle = 0;
+ pfl->status |= 0x80;
+ } else {
+- qemu_log_mask(LOG_UNIMP,
+- "%s: Aborting write to buffer not implemented,"
+- " the data is already written to storage!\n"
+- "Flash device reset into READ mode.\n",
+- __func__);
++ pflash_blk_write_abort(pfl);
+ goto mode_read_array;
+ }
+ break;
+ default:
++ pflash_blk_write_abort(pfl);
+ goto error_flash;
+ }
+ break;
+@@ -866,6 +872,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
+ pfl->cmd = 0x00;
+ pfl->status = 0x80; /* WSM ready */
+ pflash_cfi01_fill_cfi_table(pfl);
++
++ pfl->blk_bytes = g_malloc(pfl->writeblock_size);
++ pfl->blk_offset = -1;
+ }
+
+ static void pflash_cfi01_system_reset(DeviceState *dev)
+@@ -885,6 +894,8 @@ static void pflash_cfi01_system_reset(DeviceState *dev)
+ * This model deliberately ignores this delay.
+ */
+ pfl->status = 0x80;
++
++ pfl->blk_offset = -1;
+ }
+
+ static Property pflash_cfi01_properties[] = {
+diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
+index 2a99b286b0..6fa56f14c0 100644
+--- a/hw/block/pflash_cfi02.c
++++ b/hw/block/pflash_cfi02.c
+@@ -546,7 +546,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
+ }
+ goto reset_flash;
+ }
+- trace_pflash_data_write(pfl->name, offset, width, value, 0);
++ trace_pflash_data_write(pfl->name, offset, width, value);
+ if (!pfl->ro) {
+ p = (uint8_t *)pfl->storage + offset;
+ if (pfl->be) {
+diff --git a/hw/block/trace-events b/hw/block/trace-events
+index 2c45a62bd5..196493feae 100644
+--- a/hw/block/trace-events
++++ b/hw/block/trace-events
+@@ -12,7 +12,8 @@ fdctrl_tc_pulse(int level) "TC pulse: %u"
+ pflash_chip_erase_invalid(const char *name, uint64_t offset) "%s: chip erase: invalid address 0x%" PRIx64
+ pflash_chip_erase_start(const char *name) "%s: start chip erase"
+ pflash_data_read(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x"
+-pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64
++pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x"
++pflash_data_write_block(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64
+ pflash_device_id(const char *name, uint16_t id) "%s: read device ID: 0x%04x"
+ pflash_device_info(const char *name, uint64_t offset) "%s: read device information offset:0x%04" PRIx64
+ pflash_erase_complete(const char *name) "%s: sector erase complete"
+@@ -32,7 +33,9 @@ pflash_unlock0_failed(const char *name, uint64_t offset, uint8_t cmd, uint16_t a
+ pflash_unlock1_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: unlock0 failed 0x%" PRIx64 " 0x%02x"
+ pflash_unsupported_device_configuration(const char *name, uint8_t width, uint8_t max) "%s: unsupported device configuration: device_width:%d max_device_width:%d"
+ pflash_write(const char *name, const char *str) "%s: %s"
+-pflash_write_block(const char *name, uint32_t value) "%s: block write: bytes:0x%x"
++pflash_write_block_start(const char *name, uint32_t value) "%s: block write start: bytes:0x%x"
++pflash_write_block_flush(const char *name) "%s: block write flush"
++pflash_write_block_abort(const char *name) "%s: block write abort"
+ pflash_write_block_erase(const char *name, uint64_t offset, uint64_t len) "%s: block erase offset:0x%" PRIx64 " bytes:0x%" PRIx64
+ pflash_write_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: command failed 0x%" PRIx64 " 0x%02x"
+ pflash_write_invalid(const char *name, uint8_t cmd) "%s: invalid write for command 0x%02x"
+diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
+index b17b29288c..f71b3b07d8 100644
+--- a/hw/intc/arm_gicv3_cpuif.c
++++ b/hw/intc/arm_gicv3_cpuif.c
+@@ -1432,16 +1432,25 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ idx = icv_find_active(cs, irq);
+
+ if (idx < 0) {
+- /* No valid list register corresponding to EOI ID */
+- icv_increment_eoicount(cs);
++ /*
++ * No valid list register corresponding to EOI ID; if this is a vLPI
++ * not in the list regs then do nothing; otherwise increment EOI count
++ */
++ if (irq < GICV3_LPI_INTID_START) {
++ icv_increment_eoicount(cs);
++ }
+ } else {
+ uint64_t lr = cs->ich_lr_el2[idx];
+ int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
+ int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp);
+
+ if (thisgrp == grp && lr_gprio == dropprio) {
+- if (!icv_eoi_split(env, cs)) {
+- /* Priority drop and deactivate not split: deactivate irq now */
++ if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) {
++ /*
++ * Priority drop and deactivate not split: deactivate irq now.
++ * LPIs always get their active state cleared immediately
++ * because no separate deactivate is expected.
++ */
+ icv_deactivate_irq(cs, idx);
+ }
+ }
+diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
+index 06f35ac2d8..412cba4927 100644
+--- a/hw/net/virtio-net.c
++++ b/hw/net/virtio-net.c
+@@ -664,6 +664,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
+
+ n->mergeable_rx_bufs = mergeable_rx_bufs;
+
++ /*
++ * Note: when extending the vnet header, please make sure to
++ * change the vnet header copying logic in virtio_net_flush_tx()
++ * as well.
++ */
+ if (version_1) {
+ n->guest_hdr_len = hash_report ?
+ sizeof(struct virtio_net_hdr_v1_hash) :
+@@ -2630,7 +2635,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
+ ssize_t ret;
+ unsigned int out_num;
+ struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
+- struct virtio_net_hdr_mrg_rxbuf mhdr;
++ struct virtio_net_hdr_v1_hash vhdr;
+
+ elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
+ if (!elem) {
+@@ -2647,7 +2652,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
+ }
+
+ if (n->has_vnet_hdr) {
+- if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
++ if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
+ n->guest_hdr_len) {
+ virtio_error(vdev, "virtio-net header incorrect");
+ virtqueue_detach_element(q->tx_vq, elem, 0);
+@@ -2655,8 +2660,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
+ return -EINVAL;
+ }
+ if (n->needs_vnet_hdr_swap) {
+- virtio_net_hdr_swap(vdev, (void *) &mhdr);
+- sg2[0].iov_base = &mhdr;
++ virtio_net_hdr_swap(vdev, (void *) &vhdr);
++ sg2[0].iov_base = &vhdr;
+ sg2[0].iov_len = n->guest_hdr_len;
+ out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
+ out_sg, out_num,
+diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c
+index 1792f84cea..6b7b963409 100644
+--- a/hw/scsi/esp-pci.c
++++ b/hw/scsi/esp-pci.c
+@@ -77,6 +77,41 @@ struct PCIESPState {
+ ESPState esp;
+ };
+
++static void esp_pci_update_irq(PCIESPState *pci)
++{
++ int scsi_level = !!(pci->dma_regs[DMA_STAT] & DMA_STAT_SCSIINT);
++ int dma_level = (pci->dma_regs[DMA_CMD] & DMA_CMD_INTE_D) ?
++ !!(pci->dma_regs[DMA_STAT] & DMA_STAT_DONE) : 0;
++ int level = scsi_level || dma_level;
++
++ pci_set_irq(PCI_DEVICE(pci), level);
++}
++
++static void esp_irq_handler(void *opaque, int irq_num, int level)
++{
++ PCIESPState *pci = PCI_ESP(opaque);
++
++ if (level) {
++ pci->dma_regs[DMA_STAT] |= DMA_STAT_SCSIINT;
++
++ /*
++ * If raising the ESP IRQ to indicate end of DMA transfer, set
++ * DMA_STAT_DONE at the same time. In theory this should be done in
++ * esp_pci_dma_memory_rw(), however there is a delay between setting
++ * DMA_STAT_DONE and the ESP IRQ arriving which is visible to the
++ * guest that can cause confusion e.g. Linux
++ */
++ if ((pci->dma_regs[DMA_CMD] & DMA_CMD_MASK) == 0x3 &&
++ pci->dma_regs[DMA_WBC] == 0) {
++ pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
++ }
++ } else {
++ pci->dma_regs[DMA_STAT] &= ~DMA_STAT_SCSIINT;
++ }
++
++ esp_pci_update_irq(pci);
++}
++
+ static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val)
+ {
+ ESPState *s = ESP(&pci->esp);
+@@ -89,6 +124,7 @@ static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val)
+ {
+ trace_esp_pci_dma_blast(val);
+ qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n");
++ pci->dma_regs[DMA_STAT] |= DMA_STAT_BCMBLT;
+ }
+
+ static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val)
+@@ -151,6 +187,7 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
+ /* clear some bits on write */
+ uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE;
+ pci->dma_regs[DMA_STAT] &= ~(val & mask);
++ esp_pci_update_irq(pci);
+ }
+ break;
+ default:
+@@ -161,17 +198,14 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
+
+ static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr)
+ {
+- ESPState *s = ESP(&pci->esp);
+ uint32_t val;
+
+ val = pci->dma_regs[saddr];
+ if (saddr == DMA_STAT) {
+- if (s->rregs[ESP_RSTAT] & STAT_INT) {
+- val |= DMA_STAT_SCSIINT;
+- }
+ if (!(pci->sbac & SBAC_STATUS)) {
+ pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT |
+ DMA_STAT_DONE);
++ esp_pci_update_irq(pci);
+ }
+ }
+
+@@ -275,7 +309,7 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
+ qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n");
+ }
+
+- addr = pci->dma_regs[DMA_SPA];
++ addr = pci->dma_regs[DMA_WAC];
+ if (pci->dma_regs[DMA_WBC] < len) {
+ len = pci->dma_regs[DMA_WBC];
+ }
+@@ -285,9 +319,6 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
+ /* update status registers */
+ pci->dma_regs[DMA_WBC] -= len;
+ pci->dma_regs[DMA_WAC] += len;
+- if (pci->dma_regs[DMA_WBC] == 0) {
+- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+- }
+ }
+
+ static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
+@@ -342,23 +373,13 @@ static const VMStateDescription vmstate_esp_pci_scsi = {
+ }
+ };
+
+-static void esp_pci_command_complete(SCSIRequest *req, size_t resid)
+-{
+- ESPState *s = req->hba_private;
+- PCIESPState *pci = container_of(s, PCIESPState, esp);
+-
+- esp_command_complete(req, resid);
+- pci->dma_regs[DMA_WBC] = 0;
+- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+-}
+-
+ static const struct SCSIBusInfo esp_pci_scsi_info = {
+ .tcq = false,
+ .max_target = ESP_MAX_DEVS,
+ .max_lun = 7,
+
+ .transfer_data = esp_transfer_data,
+- .complete = esp_pci_command_complete,
++ .complete = esp_command_complete,
+ .cancel = esp_request_cancelled,
+ };
+
+@@ -386,7 +407,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp)
+ "esp-io", 0x80);
+
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io);
+- s->irq = pci_allocate_irq(dev);
++ s->irq = qemu_allocate_irq(esp_irq_handler, pci, 0);
+
+ scsi_bus_init(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info);
+ }
+diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
+index 9b7bfbf09a..db677c856b 100644
+--- a/include/exec/exec-all.h
++++ b/include/exec/exec-all.h
+@@ -503,7 +503,6 @@ struct tb_tc {
+ };
+
+ struct TranslationBlock {
+-#if !TARGET_TB_PCREL
+ /*
+ * Guest PC corresponding to this block. This must be the true
+ * virtual address. Therefore e.g. x86 stores EIP + CS_BASE, and
+@@ -518,7 +517,6 @@ struct TranslationBlock {
+ * deposited into the "current" PC.
+ */
+ target_ulong pc;
+-#endif
+
+ /*
+ * Target-specific data associated with the TranslationBlock, e.g.:
+@@ -604,11 +602,7 @@ struct TranslationBlock {
+ /* Hide the read to avoid ifdefs for TARGET_TB_PCREL. */
+ static inline target_ulong tb_pc(const TranslationBlock *tb)
+ {
+-#if TARGET_TB_PCREL
+- qemu_build_not_reached();
+-#else
+ return tb->pc;
+-#endif
+ }
+
+ /* Hide the qatomic_read to make code a little easier on the eyes */
+diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h
+index fbe0b1e956..78f45c8115 100644
+--- a/include/hw/elf_ops.h
++++ b/include/hw/elf_ops.h
+@@ -499,7 +499,7 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd,
+ }
+
+ if (data_swab) {
+- int j;
++ elf_word j;
+ for (j = 0; j < file_size; j += (1 << data_swab)) {
+ uint8_t *dp = data + j;
+ switch (data_swab) {
+diff --git a/monitor/qmp.c b/monitor/qmp.c
+index 092c527b6f..acd0a350c2 100644
+--- a/monitor/qmp.c
++++ b/monitor/qmp.c
+@@ -296,14 +296,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data)
+ qemu_coroutine_yield();
+ }
+
+- /*
+- * Move the coroutine from iohandler_ctx to qemu_aio_context for
+- * executing the command handler so that it can make progress if it
+- * involves an AIO_WAIT_WHILE().
+- */
+- aio_co_schedule(qemu_get_aio_context(), qmp_dispatcher_co);
+- qemu_coroutine_yield();
+-
+ /* Process request */
+ if (req_obj->req) {
+ if (trace_event_get_state(TRACE_MONITOR_QMP_CMD_IN_BAND)) {
+@@ -330,15 +322,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data)
+ }
+
+ qmp_request_free(req_obj);
+-
+- /*
+- * Yield and reschedule so the main loop stays responsive.
+- *
+- * Move back to iohandler_ctx so that nested event loops for
+- * qemu_aio_context don't start new monitor commands.
+- */
+- aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co);
+- qemu_coroutine_yield();
+ }
+ }
+
+diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
+index 0990873ec8..5d000fae87 100644
+--- a/qapi/qmp-dispatch.c
++++ b/qapi/qmp-dispatch.c
+@@ -206,9 +206,31 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
+ assert(!(oob && qemu_in_coroutine()));
+ assert(monitor_cur() == NULL);
+ if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) {
++ if (qemu_in_coroutine()) {
++ /*
++ * Move the coroutine from iohandler_ctx to qemu_aio_context for
++ * executing the command handler so that it can make progress if it
++ * involves an AIO_WAIT_WHILE().
++ */
++ aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self());
++ qemu_coroutine_yield();
++ }
++
+ monitor_set_cur(qemu_coroutine_self(), cur_mon);
+ cmd->fn(args, &ret, &err);
+ monitor_set_cur(qemu_coroutine_self(), NULL);
++
++ if (qemu_in_coroutine()) {
++ /*
++ * Yield and reschedule so the main loop stays responsive.
++ *
++ * Move back to iohandler_ctx so that nested event loops for
++ * qemu_aio_context don't start new monitor commands.
++ */
++ aio_co_schedule(iohandler_get_aio_context(),
++ qemu_coroutine_self());
++ qemu_coroutine_yield();
++ }
+ } else {
+ /*
+ * Actual context doesn't match the one the command needs.
+@@ -232,7 +254,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
+ .errp = &err,
+ .co = qemu_coroutine_self(),
+ };
+- aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh,
++ aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh,
+ &data);
+ qemu_coroutine_yield();
+ }
+diff --git a/softmmu/vl.c b/softmmu/vl.c
+index 5115221efe..ce88869618 100644
+--- a/softmmu/vl.c
++++ b/softmmu/vl.c
+@@ -2321,6 +2321,10 @@ static void qemu_validate_options(const QDict *machine_opts)
+ }
+ }
+
++ if (loadvm && incoming) {
++ error_report("'incoming' and 'loadvm' options are mutually exclusive");
++ exit(EXIT_FAILURE);
++ }
+ if (loadvm && preconfig_requested) {
+ error_report("'preconfig' and 'loadvm' options are "
+ "mutually exclusive");
+diff --git a/target/i386/cpu.h b/target/i386/cpu.h
+index d4bc19577a..f67cee477a 100644
+--- a/target/i386/cpu.h
++++ b/target/i386/cpu.h
+@@ -2217,10 +2217,15 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env)
+ static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc,
+ target_ulong *cs_base, uint32_t *flags)
+ {
+- *cs_base = env->segs[R_CS].base;
+- *pc = *cs_base + env->eip;
+ *flags = env->hflags |
+ (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK));
++ if (env->hflags & HF_CS64_MASK) {
++ *cs_base = 0;
++ *pc = env->eip;
++ } else {
++ *cs_base = env->segs[R_CS].base;
++ *pc = (uint32_t)(*cs_base + env->eip);
++ }
+ }
+
+ void do_cpu_init(X86CPU *cpu);
+diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c
+index 79ac5908f7..cd49f86341 100644
+--- a/target/i386/tcg/tcg-cpu.c
++++ b/target/i386/tcg/tcg-cpu.c
+@@ -52,7 +52,12 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs,
+ /* The instruction pointer is always up to date with TARGET_TB_PCREL. */
+ if (!TARGET_TB_PCREL) {
+ CPUX86State *env = cs->env_ptr;
+- env->eip = tb_pc(tb) - tb->cs_base;
++
++ if (tb->flags & HF_CS64_MASK) {
++ env->eip = tb_pc(tb);
++ } else {
++ env->eip = (uint32_t)(tb_pc(tb) - tb->cs_base);
++ }
+ }
+ }
+
+@@ -63,12 +68,26 @@ static void x86_restore_state_to_opc(CPUState *cs,
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+ int cc_op = data[1];
++ uint64_t new_pc;
+
+ if (TARGET_TB_PCREL) {
+- env->eip = (env->eip & TARGET_PAGE_MASK) | data[0];
++ /*
++ * data[0] in PC-relative TBs is also a linear address, i.e. an address with
++ * the CS base added, because it is not guaranteed that EIP bits 12 and higher
++ * stay the same across the translation block. Add the CS base back before
++ * replacing the low bits, and subtract it below just like for !CF_PCREL.
++ */
++ uint64_t pc = env->eip + tb->cs_base;
++ new_pc = (pc & TARGET_PAGE_MASK) | data[0];
++ } else {
++ new_pc = data[0];
++ }
++ if (tb->flags & HF_CS64_MASK) {
++ env->eip = new_pc;
+ } else {
+- env->eip = data[0] - tb->cs_base;
++ env->eip = (uint32_t)(new_pc - tb->cs_base);
+ }
++
+ if (cc_op != CC_OP_DYNAMIC) {
+ env->cc_op = cc_op;
+ }
+diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
+index 7e0b2a709a..68c42fd9ff 100644
+--- a/target/i386/tcg/translate.c
++++ b/target/i386/tcg/translate.c
+@@ -547,8 +547,10 @@ static void gen_update_eip_cur(DisasContext *s)
+ assert(s->pc_save != -1);
+ if (TARGET_TB_PCREL) {
+ tcg_gen_addi_tl(cpu_eip, cpu_eip, s->base.pc_next - s->pc_save);
++ } else if (CODE64(s)) {
++ tcg_gen_movi_tl(cpu_eip, s->base.pc_next);
+ } else {
+- tcg_gen_movi_tl(cpu_eip, s->base.pc_next - s->cs_base);
++ tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base));
+ }
+ s->pc_save = s->base.pc_next;
+ }
+@@ -558,8 +560,10 @@ static void gen_update_eip_next(DisasContext *s)
+ assert(s->pc_save != -1);
+ if (TARGET_TB_PCREL) {
+ tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save);
++ } else if (CODE64(s)) {
++ tcg_gen_movi_tl(cpu_eip, s->pc);
+ } else {
+- tcg_gen_movi_tl(cpu_eip, s->pc - s->cs_base);
++ tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base));
+ }
+ s->pc_save = s->pc;
+ }
+@@ -605,8 +609,10 @@ static TCGv eip_next_tl(DisasContext *s)
+ TCGv ret = tcg_temp_new();
+ tcg_gen_addi_tl(ret, cpu_eip, s->pc - s->pc_save);
+ return ret;
++ } else if (CODE64(s)) {
++ return tcg_constant_tl(s->pc);
+ } else {
+- return tcg_constant_tl(s->pc - s->cs_base);
++ return tcg_constant_tl((uint32_t)(s->pc - s->cs_base));
+ }
+ }
+
+@@ -617,8 +623,10 @@ static TCGv eip_cur_tl(DisasContext *s)
+ TCGv ret = tcg_temp_new();
+ tcg_gen_addi_tl(ret, cpu_eip, s->base.pc_next - s->pc_save);
+ return ret;
++ } else if (CODE64(s)) {
++ return tcg_constant_tl(s->base.pc_next);
+ } else {
+- return tcg_constant_tl(s->base.pc_next - s->cs_base);
++ return tcg_constant_tl((uint32_t)(s->base.pc_next - s->cs_base));
+ }
+ }
+
+@@ -2890,10 +2898,11 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num)
+ tcg_gen_andi_tl(cpu_eip, cpu_eip, mask);
+ use_goto_tb = false;
+ }
++ } else if (!CODE64(s)) {
++ new_pc = (uint32_t)(new_eip + s->cs_base);
+ }
+
+- if (use_goto_tb &&
+- translator_use_goto_tb(&s->base, new_eip + s->cs_base)) {
++ if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) {
+ /* jump to same page: we can use a direct jump */
+ tcg_gen_goto_tb(tb_num);
+ if (!TARGET_TB_PCREL) {
+@@ -6974,7 +6983,6 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
+
+ dc->prev_insn_end = tcg_last_op();
+ if (TARGET_TB_PCREL) {
+- pc_arg -= dc->cs_base;
+ pc_arg &= ~TARGET_PAGE_MASK;
+ }
+ tcg_gen_insn_start(pc_arg, dc->cc_op);
+diff --git a/target/riscv/csr.c b/target/riscv/csr.c
+index 5c9a7ee287..15dba5f653 100644
+--- a/target/riscv/csr.c
++++ b/target/riscv/csr.c
+@@ -697,11 +697,11 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val)
+ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
+ bool upper_half, uint32_t ctr_idx)
+ {
+- PMUCTRState counter = env->pmu_ctrs[ctr_idx];
+- target_ulong ctr_prev = upper_half ? counter.mhpmcounterh_prev :
+- counter.mhpmcounter_prev;
+- target_ulong ctr_val = upper_half ? counter.mhpmcounterh_val :
+- counter.mhpmcounter_val;
++ PMUCTRState *counter = &env->pmu_ctrs[ctr_idx];
++ target_ulong ctr_prev = upper_half ? counter->mhpmcounterh_prev :
++ counter->mhpmcounter_prev;
++ target_ulong ctr_val = upper_half ? counter->mhpmcounterh_val :
++ counter->mhpmcounter_val;
+
+ if (get_field(env->mcountinhibit, BIT(ctr_idx))) {
+ /**
+@@ -709,12 +709,12 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
+ * stop the icount counting. Just return the counter value written by
+ * the supervisor to indicate that counter was not incremented.
+ */
+- if (!counter.started) {
++ if (!counter->started) {
+ *val = ctr_val;
+ return RISCV_EXCP_NONE;
+ } else {
+ /* Mark that the counter has been stopped */
+- counter.started = false;
++ counter->started = false;
+ }
+ }
+
+diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
+index b0173e968e..a257c06838 100644
+--- a/target/s390x/tcg/translate.c
++++ b/target/s390x/tcg/translate.c
+@@ -3394,6 +3394,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o)
+ {
+ int b2 = get_field(s, b2);
+ TCGv ar1 = tcg_temp_new_i64();
++ int r1 = get_field(s, r1);
+
+ o->out = o->in2;
+ o->g_out = o->g_in2;
+@@ -3419,7 +3420,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o)
+ break;
+ }
+
+- tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1]));
++ tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[r1]));
+ tcg_temp_free_i64(ar1);
+
+ return DISAS_NEXT;
+diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c
+index fa66e8e867..c4b4df4d74 100644
+--- a/target/xtensa/mmu_helper.c
++++ b/target/xtensa/mmu_helper.c
+@@ -226,22 +226,31 @@ static void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v,
+ * Split TLB address into TLB way, entry index and VPN (with index).
+ * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format
+ */
+-static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb,
+- uint32_t *vpn, uint32_t *wi, uint32_t *ei)
++static bool split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb,
++ uint32_t *vpn, uint32_t *wi, uint32_t *ei)
+ {
+ if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+ *wi = v & (dtlb ? 0xf : 0x7);
+- split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
++ if (*wi < (dtlb ? env->config->dtlb.nways : env->config->itlb.nways)) {
++ split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
++ return true;
++ } else {
++ return false;
++ }
+ } else {
+ *vpn = v & REGION_PAGE_MASK;
+ *wi = 0;
+ *ei = (v >> 29) & 0x7;
++ return true;
+ }
+ }
+
+ static xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env, bool dtlb,
+ unsigned wi, unsigned ei)
+ {
++ const xtensa_tlb *tlb = dtlb ? &env->config->dtlb : &env->config->itlb;
++
++ assert(wi < tlb->nways && ei < tlb->way_size[wi]);
+ return dtlb ?
+ env->dtlb[wi] + ei :
+ env->itlb[wi] + ei;
+@@ -254,11 +263,14 @@ static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env,
+ uint32_t wi;
+ uint32_t ei;
+
+- split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei);
+- if (pwi) {
+- *pwi = wi;
++ if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) {
++ if (pwi) {
++ *pwi = wi;
++ }
++ return xtensa_tlb_get_entry(env, dtlb, wi, ei);
++ } else {
++ return NULL;
+ }
+- return xtensa_tlb_get_entry(env, dtlb, wi, ei);
+ }
+
+ static void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env,
+@@ -484,7 +496,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+ if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+ uint32_t wi;
+ const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
+- return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
++
++ if (entry) {
++ return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
++ } else {
++ return 0;
++ }
+ } else {
+ return v & REGION_PAGE_MASK;
+ }
+@@ -493,7 +510,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+ uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+ {
+ const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL);
+- return entry->paddr | entry->attr;
++
++ if (entry) {
++ return entry->paddr | entry->attr;
++ } else {
++ return 0;
++ }
+ }
+
+ void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+@@ -501,7 +523,7 @@ void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+ if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+ uint32_t wi;
+ xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
+- if (entry->variable && entry->asid) {
++ if (entry && entry->variable && entry->asid) {
+ tlb_flush_page(env_cpu(env), entry->vaddr);
+ entry->asid = 0;
+ }
+@@ -539,8 +561,9 @@ void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb)
+ uint32_t vpn;
+ uint32_t wi;
+ uint32_t ei;
+- split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei);
+- xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
++ if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) {
++ xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
++ }
+ }
+
+ /*!
+diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out
+index 329977d9b9..a37bf446e9 100644
+--- a/tests/qemu-iotests/060.out
++++ b/tests/qemu-iotests/060.out
+@@ -421,8 +421,8 @@ QMP_VERSION
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "none0", "msg": "Preventing invalid write on metadata (overlaps with refcount table)", "offset": 65536, "node-name": "drive", "fatal": true, "size": 65536}}
+ write failed: Input/output error
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ === Testing incoming inactive corrupted image ===
+
+@@ -432,8 +432,8 @@ QMP_VERSION
+ qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}}
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ corrupt: false
+ *** done
+diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out
+index bca0c02f5c..a2923b05c2 100644
+--- a/tests/qemu-iotests/071.out
++++ b/tests/qemu-iotests/071.out
+@@ -45,8 +45,8 @@ QMP_VERSION
+ {"return": {}}
+ read failed: Input/output error
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === Testing blkverify on existing block device ===
+@@ -84,9 +84,9 @@ wrote 512/512 bytes at offset 0
+ {"return": ""}
+ read failed: Input/output error
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ QEMU_PROG: Failed to flush the L2 table cache: Input/output error
+ QEMU_PROG: Failed to flush the refcount block cache: Input/output error
++{"return": {}}
+
+ *** done
+diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out
+index 615c083549..aba85ea564 100644
+--- a/tests/qemu-iotests/081.out
++++ b/tests/qemu-iotests/081.out
+@@ -35,8 +35,8 @@ QMP_VERSION
+ read 10485760/10485760 bytes at offset 0
+ 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ == using quorum rewrite corrupted mode ==
+@@ -67,8 +67,8 @@ QMP_VERSION
+ read 10485760/10485760 bytes at offset 0
+ 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ -- checking that the image has been corrected --
+ read 10485760/10485760 bytes at offset 0
+@@ -106,8 +106,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ Testing:
+ QMP_VERSION
+@@ -115,8 +115,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "Cannot add a child to a quorum in blkverify mode"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ == dynamically removing a child from a quorum ==
+@@ -125,31 +125,31 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "blkverify=on can only be set if there are exactly two files and vote-threshold is 2"}}
+ {"error": {"class": "GenericError", "desc": "Cannot find device='drive0-quorum' nor node-name='drive0-quorum'"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ *** done
+diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
+index e1c23a6983..97b6d8036d 100644
+--- a/tests/qemu-iotests/087.out
++++ b/tests/qemu-iotests/087.out
+@@ -7,8 +7,8 @@ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "'node-name' must be specified for the root node"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === Duplicate ID ===
+@@ -18,8 +18,8 @@ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}}
+ {"error": {"class": "GenericError", "desc": "Duplicate nodes with node-name='test-node'"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === aio=native without O_DIRECT ===
+@@ -28,8 +28,8 @@ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "aio=native was specified, but it requires cache.direct=on, which was not specified."}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === Encrypted image QCow ===
+@@ -40,8 +40,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === Encrypted image LUKS ===
+@@ -52,8 +52,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === Missing driver ===
+@@ -63,7 +63,7 @@ Testing: -S
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "Parameter 'driver' is missing"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ *** done
+diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out
+index b5401d788d..b9c876b394 100644
+--- a/tests/qemu-iotests/108.out
++++ b/tests/qemu-iotests/108.out
+@@ -173,8 +173,8 @@ OK: Reftable is where we expect it
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}}
+ {"return": {}}
+ { "execute": "quit" }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109
+index e207a555f3..0fb580f9a5 100755
+--- a/tests/qemu-iotests/109
++++ b/tests/qemu-iotests/109
+@@ -57,13 +57,13 @@ run_qemu()
+ _launch_qemu -drive file="${source_img}",format=raw,cache=${CACHEMODE},aio=${AIOMODE},id=src
+ _send_qemu_cmd $QEMU_HANDLE "{ 'execute': 'qmp_capabilities' }" "return"
+
+- _send_qemu_cmd $QEMU_HANDLE \
++ capture_events="$qmp_event" _send_qemu_cmd $QEMU_HANDLE \
+ "{'execute':'drive-mirror', 'arguments':{
+ 'device': 'src', 'target': '$raw_img', $qmp_format
+ 'mode': 'existing', 'sync': 'full'}}" \
+ "return"
+
+- _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event"
++ capture_events="$qmp_event JOB_STATUS_CHANGE" _wait_event $QEMU_HANDLE "$qmp_event"
+ if test "$qmp_event" = BLOCK_JOB_ERROR; then
+ _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"'
+ fi
+diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
+index e29280015e..255b81fcdc 100644
+--- a/tests/qemu-iotests/109.out
++++ b/tests/qemu-iotests/109.out
+@@ -7,7 +7,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -23,8 +23,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -35,12 +35,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -48,6 +46,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Writing a qcow2 header into raw ===
+@@ -57,7 +56,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -73,8 +72,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -85,12 +84,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -98,6 +95,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Writing a qed header into raw ===
+@@ -107,7 +105,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -123,8 +121,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -135,12 +133,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -148,6 +144,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Writing a vdi header into raw ===
+@@ -157,7 +154,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -173,8 +170,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -185,12 +182,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -198,6 +193,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Writing a vmdk header into raw ===
+@@ -207,7 +203,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -223,8 +219,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -235,12 +231,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -248,6 +242,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Writing a vpc header into raw ===
+@@ -257,7 +252,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -273,8 +268,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -285,12 +280,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -298,6 +291,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Copying sample image empty.bochs into raw ===
+@@ -306,7 +300,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -322,8 +316,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -334,12 +328,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -347,6 +339,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Copying sample image iotest-dirtylog-10G-4M.vhdx into raw ===
+@@ -355,7 +348,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -371,8 +364,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -383,12 +376,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -396,6 +387,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Copying sample image parallels-v1 into raw ===
+@@ -404,7 +396,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -420,8 +412,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -432,12 +424,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -445,6 +435,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Copying sample image simple-pattern.cloop into raw ===
+@@ -453,7 +444,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -469,8 +460,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -481,12 +472,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -494,6 +483,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+
+ === Write legitimate MBR into raw ===
+@@ -502,7 +492,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
+ 'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -510,12 +500,10 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -523,6 +511,7 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+@@ -532,12 +521,10 @@ Images are identical.
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -545,5 +532,6 @@ Images are identical.
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ *** done
+diff --git a/tests/qemu-iotests/117.out b/tests/qemu-iotests/117.out
+index 735ffd25c6..1cea9e0217 100644
+--- a/tests/qemu-iotests/117.out
++++ b/tests/qemu-iotests/117.out
+@@ -18,8 +18,8 @@ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ No errors were found on the image.
+ read 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out
+index 0744c1f136..35d84a5bc5 100644
+--- a/tests/qemu-iotests/120.out
++++ b/tests/qemu-iotests/120.out
+@@ -5,8 +5,8 @@ QMP_VERSION
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ read 65536/65536 bytes at offset 0
+diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out
+index 1685c4850a..dd8c4a8aa9 100644
+--- a/tests/qemu-iotests/127.out
++++ b/tests/qemu-iotests/127.out
+@@ -28,6 +28,6 @@ wrote 42/42 bytes at offset 0
+ { 'execute': 'quit' }
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out
+index 312f76d5da..32866440ae 100644
+--- a/tests/qemu-iotests/140.out
++++ b/tests/qemu-iotests/140.out
+@@ -19,6 +19,6 @@ read 65536/65536 bytes at offset 0
+ qemu-io: can't open device nbd+unix:///drv?socket=SOCK_DIR/nbd: Requested export not available
+ server reported: export 'drv' not present
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
+index a37030ee17..a7d3985a02 100755
+--- a/tests/qemu-iotests/141
++++ b/tests/qemu-iotests/141
+@@ -1,9 +1,12 @@
+-#!/usr/bin/env bash
++#!/usr/bin/env python3
+ # group: rw auto quick
+ #
+ # Test case for ejecting BDSs with block jobs still running on them
+ #
+-# Copyright (C) 2016 Red Hat, Inc.
++# Originally written in bash by Hanna Czenczek, ported to Python by Stefan
++# Hajnoczi.
++#
++# Copyright Red Hat
+ #
+ # 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
+@@ -19,177 +22,129 @@
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
+ #
+
+-# creator
+-owner=hreitz@redhat.com
+-
+-seq="$(basename $0)"
+-echo "QA output created by $seq"
+-
+-status=1 # failure is the default!
+-
+-_cleanup()
+-{
+- _cleanup_qemu
+- _cleanup_test_img
+- for img in "$TEST_DIR"/{b,m,o}.$IMGFMT; do
+- _rm_test_img "$img"
+- done
+-}
+-trap "_cleanup; exit \$status" 0 1 2 3 15
+-
+-# get standard environment, filters and checks
+-. ./common.rc
+-. ./common.filter
+-. ./common.qemu
+-
+-# Needs backing file and backing format support
+-_supported_fmt qcow2 qed
+-_supported_proto file
+-_supported_os Linux
+-
+-
+-test_blockjob()
+-{
+- _send_qemu_cmd $QEMU_HANDLE \
+- "{'execute': 'blockdev-add',
+- 'arguments': {
+- 'node-name': 'drv0',
+- 'driver': '$IMGFMT',
+- 'file': {
+- 'driver': 'file',
+- 'filename': '$TEST_IMG'
+- }}}" \
+- 'return'
+-
+- # If "$2" is an event, we may or may not see it before the
+- # {"return": {}}. Therefore, filter the {"return": {}} out both
+- # here and in the next command. (Naturally, if we do not see it
+- # here, we will see it before the next command can be executed,
+- # so it will appear in the next _send_qemu_cmd's output.)
+- _send_qemu_cmd $QEMU_HANDLE \
+- "$1" \
+- "$2" \
+- | _filter_img_create | _filter_qmp_empty_return
+-
+- # We want this to return an error because the block job is still running
+- _send_qemu_cmd $QEMU_HANDLE \
+- "{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}" \
+- 'error' | _filter_generated_node_ids | _filter_qmp_empty_return
+-
+- _send_qemu_cmd $QEMU_HANDLE \
+- "{'execute': 'block-job-cancel',
+- 'arguments': {'device': 'job0'}}" \
+- "$3"
+-
+- _send_qemu_cmd $QEMU_HANDLE \
+- "{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}" \
+- 'return'
+-}
+-
+-
+-TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M
+-TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" -F $IMGFMT 1M
+-_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M -F $IMGFMT
+-
+-_launch_qemu -nodefaults
+-
+-_send_qemu_cmd $QEMU_HANDLE \
+- "{'execute': 'qmp_capabilities'}" \
+- 'return'
+-
+-echo
+-echo '=== Testing drive-backup ==='
+-echo
+-
+-# drive-backup will not send BLOCK_JOB_READY by itself, and cancelling the job
+-# will consequently result in BLOCK_JOB_CANCELLED being emitted.
+-
+-test_blockjob \
+- "{'execute': 'drive-backup',
+- 'arguments': {'job-id': 'job0',
+- 'device': 'drv0',
+- 'target': '$TEST_DIR/o.$IMGFMT',
+- 'format': '$IMGFMT',
+- 'sync': 'none'}}" \
+- 'return' \
+- '"status": "null"'
+-
+-echo
+-echo '=== Testing drive-mirror ==='
+-echo
+-
+-# drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling
+-# the job will consequently result in BLOCK_JOB_COMPLETED being emitted.
+-
+-test_blockjob \
+- "{'execute': 'drive-mirror',
+- 'arguments': {'job-id': 'job0',
+- 'device': 'drv0',
+- 'target': '$TEST_DIR/o.$IMGFMT',
+- 'format': '$IMGFMT',
+- 'sync': 'none'}}" \
+- 'BLOCK_JOB_READY' \
+- '"status": "null"'
+-
+-echo
+-echo '=== Testing active block-commit ==='
+-echo
+-
+-# An active block-commit will send BLOCK_JOB_READY basically immediately, and
+-# cancelling the job will consequently result in BLOCK_JOB_COMPLETED being
+-# emitted.
+-
+-test_blockjob \
+- "{'execute': 'block-commit',
+- 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \
+- 'BLOCK_JOB_READY' \
+- '"status": "null"'
+-
+-echo
+-echo '=== Testing non-active block-commit ==='
+-echo
+-
+-# Give block-commit something to work on, otherwise it would be done
+-# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just
+-# fine without the block job still running.
+-
+-$QEMU_IO -c 'write 0 1M' "$TEST_DIR/m.$IMGFMT" | _filter_qemu_io
+-
+-test_blockjob \
+- "{'execute': 'block-commit',
+- 'arguments': {'job-id': 'job0',
+- 'device': 'drv0',
+- 'top': '$TEST_DIR/m.$IMGFMT',
+- 'speed': 1}}" \
+- 'return' \
+- '"status": "null"'
+-
+-echo
+-echo '=== Testing block-stream ==='
+-echo
+-
+-# Give block-stream something to work on, otherwise it would be done
+-# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just
+-# fine without the block job still running.
+-
+-$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io
+-
+-# With some data to stream (and @speed set to 1), block-stream will not complete
+-# until we send the block-job-cancel command.
+-
+-test_blockjob \
+- "{'execute': 'block-stream',
+- 'arguments': {'job-id': 'job0',
+- 'device': 'drv0',
+- 'speed': 1}}" \
+- 'return' \
+- '"status": "null"'
+-
+-_cleanup_qemu
+-
+-# success, all done
+-echo "*** done"
+-rm -f $seq.full
+-status=0
++import iotests
++
++# Common filters to mask values that vary in the test output
++QMP_FILTERS = [iotests.filter_qmp_testfiles, \
++ iotests.filter_qmp_imgfmt]
++
++
++class TestCase:
++ def __init__(self, name, vm, image_path, cancel_event):
++ self.name = name
++ self.vm = vm
++ self.image_path = image_path
++ self.cancel_event = cancel_event
++
++ def __enter__(self):
++ iotests.log(f'=== Testing {self.name} ===')
++ self.vm.qmp_log('blockdev-add', \
++ node_name='drv0', \
++ driver=iotests.imgfmt, \
++ file={'driver': 'file', 'filename': self.image_path}, \
++ filters=QMP_FILTERS)
++
++ def __exit__(self, *exc_details):
++ # This is expected to fail because the job still exists
++ self.vm.qmp_log('blockdev-del', node_name='drv0', \
++ filters=[iotests.filter_qmp_generated_node_ids])
++
++ self.vm.qmp_log('block-job-cancel', device='job0')
++ event = self.vm.event_wait(self.cancel_event)
++ iotests.log(event, filters=[iotests.filter_qmp_event])
++
++ # This time it succeeds
++ self.vm.qmp_log('blockdev-del', node_name='drv0')
++
++ # Separate test cases in output
++ iotests.log('')
++
++
++def main() -> None:
++ with iotests.FilePath('bottom', 'middle', 'top', 'target') as \
++ (bottom_path, middle_path, top_path, target_path), \
++ iotests.VM() as vm:
++
++ iotests.log('Creating bottom <- middle <- top backing file chain...')
++ IMAGE_SIZE='1M'
++ iotests.qemu_img_create('-f', iotests.imgfmt, bottom_path, IMAGE_SIZE)
++ iotests.qemu_img_create('-f', iotests.imgfmt, \
++ '-F', iotests.imgfmt, \
++ '-b', bottom_path, \
++ middle_path, \
++ IMAGE_SIZE)
++ iotests.qemu_img_create('-f', iotests.imgfmt, \
++ '-F', iotests.imgfmt, \
++ '-b', middle_path, \
++ top_path, \
++ IMAGE_SIZE)
++
++ iotests.log('Starting VM...')
++ vm.add_args('-nodefaults')
++ vm.launch()
++
++ # drive-backup will not send BLOCK_JOB_READY by itself, and cancelling
++ # the job will consequently result in BLOCK_JOB_CANCELLED being
++ # emitted.
++ with TestCase('drive-backup', vm, top_path, 'BLOCK_JOB_CANCELLED'):
++ vm.qmp_log('drive-backup', \
++ job_id='job0', \
++ device='drv0', \
++ target=target_path, \
++ format=iotests.imgfmt, \
++ sync='none', \
++ filters=QMP_FILTERS)
++
++ # drive-mirror will send BLOCK_JOB_READY basically immediately, and
++ # cancelling the job will consequently result in BLOCK_JOB_COMPLETED
++ # being emitted.
++ with TestCase('drive-mirror', vm, top_path, 'BLOCK_JOB_COMPLETED'):
++ vm.qmp_log('drive-mirror', \
++ job_id='job0', \
++ device='drv0', \
++ target=target_path, \
++ format=iotests.imgfmt, \
++ sync='none', \
++ filters=QMP_FILTERS)
++ event = vm.event_wait('BLOCK_JOB_READY')
++ assert event is not None # silence mypy
++ iotests.log(event, filters=[iotests.filter_qmp_event])
++
++ # An active block-commit will send BLOCK_JOB_READY basically
++ # immediately, and cancelling the job will consequently result in
++ # BLOCK_JOB_COMPLETED being emitted.
++ with TestCase('active block-commit', vm, top_path, \
++ 'BLOCK_JOB_COMPLETED'):
++ vm.qmp_log('block-commit', \
++ job_id='job0', \
++ device='drv0')
++ event = vm.event_wait('BLOCK_JOB_READY')
++ assert event is not None # silence mypy
++ iotests.log(event, filters=[iotests.filter_qmp_event])
++
++ # Give block-commit something to work on, otherwise it would be done
++ # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would
++ # work just fine without the block job still running.
++ iotests.qemu_io(middle_path, '-c', f'write 0 {IMAGE_SIZE}')
++ with TestCase('non-active block-commit', vm, top_path, \
++ 'BLOCK_JOB_CANCELLED'):
++ vm.qmp_log('block-commit', \
++ job_id='job0', \
++ device='drv0', \
++ top=middle_path, \
++ speed=1, \
++ filters=[iotests.filter_qmp_testfiles])
++
++ # Give block-stream something to work on, otherwise it would be done
++ # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would
++ # work just fine without the block job still running.
++ iotests.qemu_io(bottom_path, '-c', f'write 0 {IMAGE_SIZE}')
++ with TestCase('block-stream', vm, top_path, 'BLOCK_JOB_CANCELLED'):
++ vm.qmp_log('block-stream', \
++ job_id='job0', \
++ device='drv0', \
++ speed=1)
++
++if __name__ == '__main__':
++ iotests.script_main(main, supported_fmts=['qcow2', 'qed'],
++ supported_protocols=['file'])
+diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
+index 63203d9944..91b7ba50af 100644
+--- a/tests/qemu-iotests/141.out
++++ b/tests/qemu-iotests/141.out
+@@ -1,179 +1,69 @@
+-QA output created by 141
+-Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576
+-Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT backing_fmt=IMGFMT
+-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT backing_fmt=IMGFMT
+-{'execute': 'qmp_capabilities'}
+-{"return": {}}
+-
++Creating bottom <- middle <- top backing file chain...
++Starting VM...
+ === Testing drive-backup ===
+-
+-{'execute': 'blockdev-add',
+- 'arguments': {
+- 'node-name': 'drv0',
+- 'driver': 'IMGFMT',
+- 'file': {
+- 'driver': 'file',
+- 'filename': 'TEST_DIR/t.IMGFMT'
+- }}}
+-{"return": {}}
+-{'execute': 'drive-backup',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'target': 'TEST_DIR/o.IMGFMT',
+-'format': 'IMGFMT',
+-'sync': 'none'}}
+-Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "drive-backup", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}}
++{"return": {}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
+-{'execute': 'block-job-cancel',
+- 'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+
+ === Testing drive-mirror ===
+-
+-{'execute': 'blockdev-add',
+- 'arguments': {
+- 'node-name': 'drv0',
+- 'driver': 'IMGFMT',
+- 'file': {
+- 'driver': 'file',
+- 'filename': 'TEST_DIR/t.IMGFMT'
+- }}}
+-{"return": {}}
+-{'execute': 'drive-mirror',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'target': 'TEST_DIR/o.IMGFMT',
+-'format': 'IMGFMT',
+-'sync': 'none'}}
+-Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "drive-mirror", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}}
++{"return": {}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}}
+-{'execute': 'block-job-cancel',
+- 'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+
+ === Testing active block-commit ===
+-
+-{'execute': 'blockdev-add',
+- 'arguments': {
+- 'node-name': 'drv0',
+- 'driver': 'IMGFMT',
+- 'file': {
+- 'driver': 'file',
+- 'filename': 'TEST_DIR/t.IMGFMT'
+- }}}
+-{"return": {}}
+-{'execute': 'block-commit',
+-'arguments': {'job-id': 'job0', 'device': 'drv0'}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0"}}
++{"return": {}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}}
+-{'execute': 'block-job-cancel',
+- 'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+
+ === Testing non-active block-commit ===
+-
+-wrote 1048576/1048576 bytes at offset 0
+-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+-{'execute': 'blockdev-add',
+- 'arguments': {
+- 'node-name': 'drv0',
+- 'driver': 'IMGFMT',
+- 'file': {
+- 'driver': 'file',
+- 'filename': 'TEST_DIR/t.IMGFMT'
+- }}}
+-{"return": {}}
+-{'execute': 'block-commit',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'top': 'TEST_DIR/m.IMGFMT',
+-'speed': 1}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1, "top": "TEST_DIR/PID-middle"}}
++{"return": {}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}}
+-{'execute': 'block-job-cancel',
+- 'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+
+ === Testing block-stream ===
+-
+-wrote 1048576/1048576 bytes at offset 0
+-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+-{'execute': 'blockdev-add',
+- 'arguments': {
+- 'node-name': 'drv0',
+- 'driver': 'IMGFMT',
+- 'file': {
+- 'driver': 'file',
+- 'filename': 'TEST_DIR/t.IMGFMT'
+- }}}
+-{"return": {}}
+-{'execute': 'block-stream',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'speed': 1}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "block-stream", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1}}
++{"return": {}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}}
+-{'execute': 'block-job-cancel',
+- 'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+- 'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+-*** done
++
+diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out
+index 9ec5888e0e..d6afa32abc 100644
+--- a/tests/qemu-iotests/143.out
++++ b/tests/qemu-iotests/143.out
+@@ -10,6 +10,6 @@ server reported: export 'no_such_export' not present
+ qemu-io: can't open device nbd+unix:///aa--aa1?socket=SOCK_DIR/nbd: Requested export not available
+ server reported: export 'aa--aa...' not present
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out
+index 4a22f0c41a..07e5e83f5d 100644
+--- a/tests/qemu-iotests/156.out
++++ b/tests/qemu-iotests/156.out
+@@ -72,8 +72,8 @@ read 65536/65536 bytes at offset 196608
+ {"return": ""}
+
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ read 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out
+index 9d09b60452..45e9153ef3 100644
+--- a/tests/qemu-iotests/176.out
++++ b/tests/qemu-iotests/176.out
+@@ -169,8 +169,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -206,8 +206,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ === Test pass bitmap.1 ===
+
+@@ -218,8 +218,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -256,8 +256,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ === Test pass bitmap.2 ===
+
+@@ -268,8 +268,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -306,8 +306,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ === Test pass bitmap.3 ===
+
+@@ -318,8 +318,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -353,6 +353,6 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out
+index 57f7265458..83fc1a4797 100644
+--- a/tests/qemu-iotests/182.out
++++ b/tests/qemu-iotests/182.out
+@@ -53,6 +53,6 @@ Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 cluster_size=65536 extended_l2=
+ {'execute': 'qmp_capabilities'}
+ {"return": {}}
+ {'execute': 'quit'}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out
+index fd9c2e52a5..51aa41c888 100644
+--- a/tests/qemu-iotests/183.out
++++ b/tests/qemu-iotests/183.out
+@@ -53,11 +53,11 @@ wrote 65536/65536 bytes at offset 1048576
+ === Shut down and check image ===
+
+ {"execute":"quit"}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"return": {}}
+ {"execute":"quit"}
+-{"return": {}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ No errors were found on the image.
+ No errors were found on the image.
+ wrote 65536/65536 bytes at offset 1048576
+diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out
+index 77e5489d65..e8f631f853 100644
+--- a/tests/qemu-iotests/184.out
++++ b/tests/qemu-iotests/184.out
+@@ -89,10 +89,6 @@ Testing:
+ "return": [
+ ]
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -104,6 +100,10 @@ Testing:
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ == property changes in ThrottleGroup ==
+@@ -169,10 +169,6 @@ Testing:
+ "iops-total-max": 0
+ }
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -184,6 +180,10 @@ Testing:
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ == object creation/set errors ==
+@@ -211,10 +211,6 @@ Testing:
+ "desc": "bps/iops/max total values and read/write values cannot be used at the same time"
+ }
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -226,6 +222,10 @@ Testing:
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ == don't specify group ==
+@@ -247,10 +247,6 @@ Testing:
+ "desc": "Parameter 'throttle-group' is missing"
+ }
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -262,6 +258,10 @@ Testing:
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ *** done
+diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
+index 8b1143dc16..61f13d0460 100755
+--- a/tests/qemu-iotests/185
++++ b/tests/qemu-iotests/185
+@@ -344,14 +344,14 @@ wait_for_job_and_quit() {
+
+ sleep 1
+
++ # List of expected events
++ capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN'
++
+ _send_qemu_cmd $h \
+ '{"execute": "quit"}' \
+ 'return'
+
+- # List of expected events
+- capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN'
+ _wait_event $h 'SHUTDOWN'
+- QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before SHUTDOWN
+ _wait_event $h 'JOB_STATUS_CHANGE' # standby
+ _wait_event $h 'JOB_STATUS_CHANGE' # ready
+ _wait_event $h 'JOB_STATUS_CHANGE' # aborting
+diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
+index 70e8dd6c87..1cccccb1b6 100644
+--- a/tests/qemu-iotests/185.out
++++ b/tests/qemu-iotests/185.out
+@@ -40,9 +40,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+
+ === Start active commit job and exit qemu ===
+
+@@ -56,9 +63,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+
+ === Start mirror job and exit qemu ===
+
+@@ -75,9 +89,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+
+ === Start backup job and exit qemu ===
+
+@@ -97,9 +118,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+
+ === Start streaming job and exit qemu ===
+
+@@ -112,9 +140,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+ No errors were found on the image.
+
+ === Start mirror to throttled QSD and exit qemu ===
+diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out
+index ea88777374..c3309e4bc6 100644
+--- a/tests/qemu-iotests/191.out
++++ b/tests/qemu-iotests/191.out
+@@ -378,10 +378,6 @@ wrote 65536/65536 bytes at offset 1048576
+ ]
+ }
+ { 'execute': 'quit' }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -393,6 +389,10 @@ wrote 65536/65536 bytes at offset 1048576
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+ image: TEST_DIR/t.IMGFMT
+ file format: IMGFMT
+ virtual size: 64 MiB (67108864 bytes)
+@@ -796,10 +796,6 @@ wrote 65536/65536 bytes at offset 1048576
+ ]
+ }
+ { 'execute': 'quit' }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -811,6 +807,10 @@ wrote 65536/65536 bytes at offset 1048576
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+ image: TEST_DIR/t.IMGFMT
+ file format: IMGFMT
+ virtual size: 64 MiB (67108864 bytes)
+diff --git a/tests/qemu-iotests/195.out b/tests/qemu-iotests/195.out
+index ec84df5012..91717d302e 100644
+--- a/tests/qemu-iotests/195.out
++++ b/tests/qemu-iotests/195.out
+@@ -17,10 +17,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid
+ "return": {
+ }
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -32,6 +28,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+ image: TEST_DIR/t.IMGFMT.mid
+ file format: IMGFMT
+@@ -55,10 +55,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top
+ "return": {
+ }
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -70,6 +66,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+ image: TEST_DIR/t.IMGFMT
+ file format: IMGFMT
+diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out
+index 26fb347c5d..65625c491e 100644
+--- a/tests/qemu-iotests/223.out
++++ b/tests/qemu-iotests/223.out
+@@ -11,8 +11,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+
+ === Write part of the file under active bitmap ===
+@@ -142,14 +142,14 @@ read 2097152/2097152 bytes at offset 2097152
+
+ {"execute":"nbd-server-remove",
+ "arguments":{"name":"n"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+ "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+ "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
+ {"execute":"nbd-server-stop"}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
+@@ -261,14 +261,14 @@ read 2097152/2097152 bytes at offset 2097152
+
+ {"execute":"nbd-server-remove",
+ "arguments":{"name":"n"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+ "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+ "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
+ {"execute":"nbd-server-stop"}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
+@@ -276,8 +276,8 @@ read 2097152/2097152 bytes at offset 2097152
+ {"execute":"nbd-server-stop"}
+ {"error": {"class": "GenericError", "desc": "NBD server not running"}}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+
+ === Use qemu-nbd as server ===
+
+diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out
+index 378c1b8fb1..b6a56118b7 100644
+--- a/tests/qemu-iotests/227.out
++++ b/tests/qemu-iotests/227.out
+@@ -48,10 +48,6 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio
+ }
+ ]
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -63,6 +59,10 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ === blockstats with -drive if=none ===
+@@ -112,10 +112,6 @@ Testing: -drive driver=null-co,if=none
+ }
+ ]
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -127,6 +123,10 @@ Testing: -drive driver=null-co,if=none
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ === blockstats with -blockdev ===
+@@ -143,10 +143,6 @@ Testing: -blockdev driver=null-co,node-name=null
+ "return": [
+ ]
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -158,6 +154,10 @@ Testing: -blockdev driver=null-co,node-name=null
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+
+ === blockstats with -blockdev and -device ===
+@@ -208,10 +208,6 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b
+ }
+ ]
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -223,5 +219,9 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+ *** done
+diff --git a/tests/qemu-iotests/247.out b/tests/qemu-iotests/247.out
+index e909e83994..7d252e7fe4 100644
+--- a/tests/qemu-iotests/247.out
++++ b/tests/qemu-iotests/247.out
+@@ -17,6 +17,6 @@ QMP_VERSION
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out
+index 6a74a8138b..71843f02de 100644
+--- a/tests/qemu-iotests/273.out
++++ b/tests/qemu-iotests/273.out
+@@ -282,10 +282,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
+ ]
+ }
+ }
+-{
+- "return": {
+- }
+-}
+ {
+ "timestamp": {
+ "seconds": TIMESTAMP,
+@@ -297,5 +293,9 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
+ "reason": "host-qmp-quit"
+ }
+ }
++{
++ "return": {
++ }
++}
+
+ *** done
+diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308
+index bde4aac2fa..d1bb49f1de 100755
+--- a/tests/qemu-iotests/308
++++ b/tests/qemu-iotests/308
+@@ -77,6 +77,7 @@ fuse_export_add()
+ # $1: Export ID
+ fuse_export_del()
+ {
++ capture_events="BLOCK_EXPORT_DELETED" \
+ _send_qemu_cmd $QEMU_HANDLE \
+ "{'execute': 'block-export-del',
+ 'arguments': {
+@@ -84,8 +85,7 @@ fuse_export_del()
+ } }" \
+ 'return'
+
+- _send_qemu_cmd $QEMU_HANDLE \
+- '' \
++ _wait_event $QEMU_HANDLE \
+ 'BLOCK_EXPORT_DELETED'
+ }
+
+diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out
+index e4467a10cf..9fcf8844d4 100644
+--- a/tests/qemu-iotests/308.out
++++ b/tests/qemu-iotests/308.out
+@@ -165,9 +165,9 @@ OK: Post-truncate image size is as expected
+
+ === Tear down ===
+ {'execute': 'quit'}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export-mp"}}
++{"return": {}}
+
+ === Compare copy with original ===
+ Images are identical.
+diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
+index da7d6637e1..6f7c3c0e44 100644
+--- a/tests/qemu-iotests/iotests.py
++++ b/tests/qemu-iotests/iotests.py
+@@ -642,6 +642,13 @@ def _filter(_key, value):
+ def filter_generated_node_ids(msg):
+ return re.sub("#block[0-9]+", "NODE_NAME", msg)
+
++def filter_qmp_generated_node_ids(qmsg):
++ def _filter(_key, value):
++ if is_str(value):
++ return filter_generated_node_ids(value)
++ return value
++ return filter_qmp(qmsg, _filter)
++
+ def filter_img_info(output, filename):
+ lines = []
+ for line in output.split('\n'):
+diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots b/tests/qemu-iotests/tests/qcow2-internal-snapshots
+new file mode 100755
+index 0000000000..36523aba06
+--- /dev/null
++++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots
+@@ -0,0 +1,170 @@
++#!/usr/bin/env bash
++# group: rw quick
++#
++# Test case for internal snapshots in qcow2
++#
++# Copyright (C) 2023 Red Hat, Inc.
++#
++# 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/>.
++#
++
++# creator
++owner=kwolf@redhat.com
++
++seq="$(basename $0)"
++echo "QA output created by $seq"
++
++status=1 # failure is the default!
++
++_cleanup()
++{
++ _cleanup_test_img
++}
++trap "_cleanup; exit \$status" 0 1 2 3 15
++
++# get standard environment, filters and checks
++. ../common.rc
++. ../common.filter
++
++# This tests qcow2-specific low-level functionality
++_supported_fmt qcow2
++_supported_proto generic
++# Internal snapshots are (currently) impossible with refcount_bits=1,
++# and generally impossible with external data files
++_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file
++
++IMG_SIZE=64M
++
++_qemu()
++{
++ $QEMU -no-shutdown -nographic -monitor stdio -serial none \
++ -blockdev file,filename="$TEST_IMG",node-name=disk0-file \
++ -blockdev "$IMGFMT",file=disk0-file,node-name=disk0 \
++ -object iothread,id=iothread0 \
++ -device virtio-scsi,iothread=iothread0 \
++ -device scsi-hd,drive=disk0,share-rw=on \
++ "$@" 2>&1 |\
++ _filter_qemu | _filter_hmp | _filter_qemu_io
++}
++
++_make_test_img $IMG_SIZE
++
++echo
++echo "=== Write some data, take a snapshot and overwrite part of it ==="
++echo
++
++{
++ echo 'qemu-io disk0 "write -P0x11 0 1M"'
++ # Give qemu some time to boot before saving the VM state
++ sleep 0.5
++ echo "savevm snap0"
++ echo 'qemu-io disk0 "write -P0x22 0 512k"'
++ echo "quit"
++} | _qemu
++
++echo
++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size
++_check_test_img
++
++echo
++echo "=== Verify that loading the snapshot reverts to the old content ==="
++echo
++
++{
++ # -loadvm reverted the write from the previous QEMU instance
++ echo 'qemu-io disk0 "read -P0x11 0 1M"'
++
++ # Verify that it works without restarting QEMU, too
++ echo 'qemu-io disk0 "write -P0x33 512k 512k"'
++ echo "loadvm snap0"
++ echo 'qemu-io disk0 "read -P0x11 0 1M"'
++
++ # Verify COW by writing a partial cluster
++ echo 'qemu-io disk0 "write -P0x33 63k 2k"'
++ echo 'qemu-io disk0 "read -P0x11 0 63k"'
++ echo 'qemu-io disk0 "read -P0x33 63k 2k"'
++ echo 'qemu-io disk0 "read -P0x11 65k 63k"'
++
++ # Take a second snapshot
++ echo "savevm snap1"
++
++ echo "quit"
++} | _qemu -loadvm snap0
++
++echo
++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size
++_check_test_img
++
++echo
++echo "=== qemu-img snapshot can revert to snapshots ==="
++echo
++
++$QEMU_IMG snapshot -a snap0 "$TEST_IMG"
++$QEMU_IO -c "read -P0x11 0 1M" "$TEST_IMG" | _filter_qemu_io
++$QEMU_IMG snapshot -a snap1 "$TEST_IMG"
++$QEMU_IO \
++ -c "read -P0x11 0 63k" \
++ -c "read -P0x33 63k 2k" \
++ -c "read -P0x11 65k 63k" \
++ "$TEST_IMG" | _filter_qemu_io
++
++echo
++echo "=== Deleting snapshots ==="
++echo
++{
++ # The active layer stays unaffected by deleting the snapshot
++ echo "delvm snap1"
++ echo 'qemu-io disk0 "read -P0x11 0 63k"'
++ echo 'qemu-io disk0 "read -P0x33 63k 2k"'
++ echo 'qemu-io disk0 "read -P0x11 65k 63k"'
++
++ echo "quit"
++} | _qemu
++
++
++echo
++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size
++_check_test_img
++
++echo
++echo "=== Error cases ==="
++echo
++
++# snap1 should not exist any more
++_qemu -loadvm snap1
++
++echo
++{
++ echo "loadvm snap1"
++ echo "quit"
++} | _qemu
++
++# Snapshot operations and inactive images are incompatible
++echo
++_qemu -loadvm snap0 -incoming defer
++{
++ echo "loadvm snap0"
++ echo "delvm snap0"
++ echo "savevm snap1"
++ echo "quit"
++} | _qemu -incoming defer
++
++# -loadvm and -preconfig are incompatible
++echo
++_qemu -loadvm snap0 -preconfig
++
++# success, all done
++echo "*** done"
++rm -f $seq.full
++status=0
+diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots.out b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out
+new file mode 100644
+index 0000000000..438f535e6a
+--- /dev/null
++++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out
+@@ -0,0 +1,107 @@
++QA output created by qcow2-internal-snapshots
++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
++
++=== Write some data, take a snapshot and overwrite part of it ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) qemu-io disk0 "write -P0x11 0 1M"
++wrote 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) savevm snap0
++(qemu) qemu-io disk0 "write -P0x22 0 512k"
++wrote 524288/524288 bytes at offset 0
++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) quit
++
++Snapshot list:
++ID TAG VM SIZE DATE VM CLOCK ICOUNT
++1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++No errors were found on the image.
++
++=== Verify that loading the snapshot reverts to the old content ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) qemu-io disk0 "read -P0x11 0 1M"
++read 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "write -P0x33 512k 512k"
++wrote 524288/524288 bytes at offset 524288
++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) loadvm snap0
++(qemu) qemu-io disk0 "read -P0x11 0 1M"
++read 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "write -P0x33 63k 2k"
++wrote 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x11 0 63k"
++read 64512/64512 bytes at offset 0
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x33 63k 2k"
++read 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x11 65k 63k"
++read 64512/64512 bytes at offset 66560
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) savevm snap1
++(qemu) quit
++
++Snapshot list:
++ID TAG VM SIZE DATE VM CLOCK ICOUNT
++1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++2 snap1 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++No errors were found on the image.
++
++=== qemu-img snapshot can revert to snapshots ===
++
++read 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 64512/64512 bytes at offset 0
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 64512/64512 bytes at offset 66560
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++
++=== Deleting snapshots ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) delvm snap1
++(qemu) qemu-io disk0 "read -P0x11 0 63k"
++read 64512/64512 bytes at offset 0
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x33 63k 2k"
++read 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x11 65k 63k"
++read 64512/64512 bytes at offset 66560
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) quit
++
++Snapshot list:
++ID TAG VM SIZE DATE VM CLOCK ICOUNT
++1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++No errors were found on the image.
++
++=== Error cases ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) QEMU_PROG: Snapshot 'snap1' does not exist in one or more devices
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) loadvm snap1
++Error: Snapshot 'snap1' does not exist in one or more devices
++(qemu) quit
++
++QEMU_PROG: 'incoming' and 'loadvm' options are mutually exclusive
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) loadvm snap0
++Error: Device 'disk0' is writable but does not support snapshots
++(qemu) delvm snap0
++Error: Device 'disk0' is writable but does not support snapshots
++(qemu) savevm snap1
++Error: Device 'disk0' is writable but does not support snapshots
++(qemu) quit
++
++QEMU_PROG: 'preconfig' and 'loadvm' options are mutually exclusive
++*** done
+diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out
+index c1bc9b8356..aa6b6d1aef 100644
+--- a/tests/qemu-iotests/tests/qsd-jobs.out
++++ b/tests/qemu-iotests/tests/qsd-jobs.out
+@@ -7,8 +7,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
+ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
++{"return": {}}
+
+ === Streaming can't get permission on base node ===
+
+@@ -17,6 +17,6 @@ QMP_VERSION
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+ {"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt_base': permissions 'write' are both required by an unnamed block device (uses node 'fmt_base' as 'root' child) and unshared by stream job 'job0' (uses node 'fmt_base' as 'intermediate node' child)."}}
+-{"return": {}}
+ {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
+index c07a5b1a5f..c00b92b89a 100644
+--- a/tests/qtest/meson.build
++++ b/tests/qtest/meson.build
+@@ -6,6 +6,7 @@ endif
+
+ slow_qtests = {
+ 'ahci-test' : 60,
++ 'aspeed_smc-test': 360,
+ 'bios-tables-test' : 120,
+ 'boot-serial-test' : 60,
+ 'migration-test' : 150,