diff options
author | Daniel Baumann <mail@daniel-baumann.ch> | 2025-06-06 10:05:27 +0000 |
---|---|---|
committer | Daniel Baumann <mail@daniel-baumann.ch> | 2025-06-06 10:05:27 +0000 |
commit | 43904a02caeb311a505bbb5ffa431ea9859db5f4 (patch) | |
tree | cd841d75f639d9092243b0d02a3bb93cbdea5804 /debian/patches/v7.2.9.diff | |
parent | Adding upstream version 1:7.2+dfsg. (diff) | |
download | qemu-debian.tar.xz qemu-debian.zip |
Adding debian version 1:7.2+dfsg-7+deb12u13.debian/1%7.2+dfsg-7+deb12u13debian
Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
Diffstat (limited to '')
-rw-r--r-- | debian/patches/v7.2.9.diff | 3566 |
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, |