summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml19
-rw-r--r--Makefile1
-rw-r--r--backends/hostmem.c10
-rw-r--r--block.c25
-rw-r--r--block/backup-top.c6
-rw-r--r--block/backup-top.h1
-rw-r--r--block/backup.c233
-rw-r--r--block/block-backend.c19
-rw-r--r--block/block-copy.c227
-rw-r--r--block/copy-on-read.c184
-rw-r--r--block/copy-on-read.h32
-rw-r--r--block/dirty-bitmap.c8
-rw-r--r--block/export/export.c7
-rw-r--r--block/file-posix.c38
-rw-r--r--block/gluster.c13
-rw-r--r--block/io.c10
-rw-r--r--block/monitor/block-hmp-cmds.c7
-rw-r--r--block/qapi.c37
-rw-r--r--block/qcow2-bitmap.c15
-rw-r--r--block/replication.c2
-rw-r--r--block/stream.c185
-rw-r--r--block/vmdk.c9
-rw-r--r--blockdev.c96
-rw-r--r--blockjob.c6
-rwxr-xr-xconfigure6
-rw-r--r--crypto/block-luks.c9
-rw-r--r--docs/devel/testing.rst112
-rw-r--r--dump/dump.c22
-rw-r--r--hw/acpi/cpu.c7
-rw-r--r--hw/acpi/memory_hotplug.c8
-rw-r--r--hw/block/dataplane/xen-block.c2
-rw-r--r--hw/block/fdc.c9
-rw-r--r--hw/block/m25p80.c6
-rw-r--r--hw/block/nand.c2
-rw-r--r--hw/block/nvme-ns.c7
-rw-r--r--hw/block/onenand.c2
-rw-r--r--hw/block/pflash_cfi01.c2
-rw-r--r--hw/block/pflash_cfi02.c2
-rw-r--r--hw/block/swim.c6
-rw-r--r--hw/block/virtio-blk.c6
-rw-r--r--hw/block/xen-block.c2
-rw-r--r--hw/core/machine-qmp-cmds.c127
-rw-r--r--hw/display/vmware_vga.c3
-rw-r--r--hw/ide/core.c2
-rw-r--r--hw/mem/memory-device.c12
-rw-r--r--hw/misc/sifive_u_otp.c2
-rw-r--r--hw/pci/pci.c60
-rw-r--r--hw/ppc/pnv_pnor.c2
-rw-r--r--hw/scsi/scsi-disk.c10
-rw-r--r--hw/scsi/scsi-generic.c4
-rw-r--r--hw/sd/sd.c6
-rw-r--r--hw/usb/dev-storage.c4
-rw-r--r--include/block/block-copy.h61
-rw-r--r--include/block/block.h10
-rw-r--r--include/block/block_int.h15
-rw-r--r--include/block/blockjob_int.h2
-rw-r--r--include/qapi/util.h13
-rw-r--r--include/sysemu/block-backend.h3
-rw-r--r--iothread.c12
-rw-r--r--job-qmp.c13
-rw-r--r--job.c3
-rw-r--r--meson.build3
-rw-r--r--migration/migration.c20
-rw-r--r--monitor/hmp-cmds.c35
-rw-r--r--monitor/qmp-cmds-control.c9
-rw-r--r--net/net.c15
-rw-r--r--qapi/block-core.json66
-rw-r--r--qemu-img.c8
-rw-r--r--qga/commands-posix.c50
-rw-r--r--qga/commands-win32.c99
-rwxr-xr-xscripts/simplebench/bench-backup.py167
-rw-r--r--scripts/simplebench/bench-example.py2
-rwxr-xr-xscripts/simplebench/bench_block_job.py13
-rw-r--r--scsi/pr-manager.c10
-rw-r--r--softmmu/tpm.c38
-rw-r--r--target/i386/cpu.c24
-rw-r--r--target/microblaze/cpu.c48
-rw-r--r--target/microblaze/cpu.h14
-rw-r--r--target/microblaze/helper.c26
-rw-r--r--target/microblaze/mmu.c2
-rw-r--r--target/microblaze/mmu.h2
-rwxr-xr-xtests/check-block.sh3
-rw-r--r--tests/docker/dockerfiles/centos7.docker1
-rw-r--r--tests/docker/dockerfiles/centos8.docker1
-rw-r--r--tests/docker/dockerfiles/debian-amd64.docker1
-rw-r--r--tests/docker/dockerfiles/fedora.docker3
-rw-r--r--tests/docker/dockerfiles/ubuntu2004.docker1
-rwxr-xr-xtests/qemu-iotests/03012
-rwxr-xr-xtests/qemu-iotests/0569
-rw-r--r--tests/qemu-iotests/109.out24
-rwxr-xr-xtests/qemu-iotests/11820
-rw-r--r--tests/qemu-iotests/118.out4
-rwxr-xr-xtests/qemu-iotests/1248
-rwxr-xr-xtests/qemu-iotests/12979
-rw-r--r--tests/qemu-iotests/141.out2
-rw-r--r--tests/qemu-iotests/146.out780
-rwxr-xr-xtests/qemu-iotests/1782
-rw-r--r--tests/qemu-iotests/178.out.qcow22
-rw-r--r--tests/qemu-iotests/178.out.raw2
-rwxr-xr-xtests/qemu-iotests/1853
-rw-r--r--tests/qemu-iotests/185.out3
-rwxr-xr-xtests/qemu-iotests/21913
-rwxr-xr-xtests/qemu-iotests/24520
-rwxr-xr-xtests/qemu-iotests/2571
-rw-r--r--tests/qemu-iotests/257.out306
-rwxr-xr-xtests/qemu-iotests/297112
-rw-r--r--tests/qemu-iotests/297.out5
-rwxr-xr-xtests/qemu-iotests/30019
-rwxr-xr-xtests/qemu-iotests/310117
-rw-r--r--tests/qemu-iotests/310.out15
-rwxr-xr-xtests/qemu-iotests/313104
-rw-r--r--tests/qemu-iotests/313.out29
-rwxr-xr-xtests/qemu-iotests/check1095
-rw-r--r--tests/qemu-iotests/common.env.in3
-rw-r--r--tests/qemu-iotests/common.rc7
-rw-r--r--tests/qemu-iotests/findtests.py159
-rw-r--r--tests/qemu-iotests/group321
-rw-r--r--tests/qemu-iotests/iotests.py45
-rw-r--r--tests/qemu-iotests/meson.build3
-rw-r--r--tests/qemu-iotests/testenv.py281
-rw-r--r--tests/qemu-iotests/testrunner.py367
-rwxr-xr-xtests/qemu-iotests/tests/migrate-bitmaps-postcopy-test (renamed from tests/qemu-iotests/199)0
-rw-r--r--tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test.out (renamed from tests/qemu-iotests/199.out)0
-rwxr-xr-xtests/qemu-iotests/tests/migrate-bitmaps-test (renamed from tests/qemu-iotests/169)0
-rw-r--r--tests/qemu-iotests/tests/migrate-bitmaps-test.out (renamed from tests/qemu-iotests/169.out)0
-rw-r--r--tests/qtest/libqtest.c50
-rw-r--r--tests/qtest/virtio-scsi-test.c39
-rw-r--r--tests/test-qobject-output-visitor.c84
-rw-r--r--tests/test-string-output-visitor.c6
-rw-r--r--ui/spice-core.c31
-rw-r--r--ui/vnc.c14
-rw-r--r--util/coroutine-sigaltstack.c9
132 files changed, 3763 insertions, 2867 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index de3a3d25b5..7c0db64710 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,9 +38,12 @@ include:
stage: test
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
script:
+ - scripts/git-submodule.sh update
+ $(grep GIT_SUBMODULES build/config-host.mak | sed 's/GIT_SUBMODULES=//')
- cd build
- find . -type f -exec touch {} +
- - make $MAKE_CHECK_ARGS
+ # Avoid recompiling by hiding ninja with NINJA=":"
+ - make NINJA=":" $MAKE_CHECK_ARGS
.acceptance_template: &acceptance_definition
cache:
@@ -109,6 +112,7 @@ build-system-ubuntu:
<<: *native_build_job_definition
variables:
IMAGE: ubuntu2004
+ CONFIGURE_ARGS: --enable-fdt=system --enable-slirp=system
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
moxie-softmmu microblazeel-softmmu mips64el-softmmu
MAKE_CHECK_ARGS: check-build
@@ -140,6 +144,7 @@ build-system-debian:
<<: *native_build_job_definition
variables:
IMAGE: debian-amd64
+ CONFIGURE_ARGS: --enable-fdt=system
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
MAKE_CHECK_ARGS: check-build
@@ -185,6 +190,7 @@ build-system-fedora:
variables:
IMAGE: fedora
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs
+ --enable-fdt=system --enable-slirp=system --enable-capstone=system
TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
MAKE_CHECK_ARGS: check-build
@@ -216,7 +222,7 @@ build-system-centos:
<<: *native_build_job_definition
variables:
IMAGE: centos8
- CONFIGURE_ARGS: --disable-nettle --enable-gcrypt
+ CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system
TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu
x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu
MAKE_CHECK_ARGS: check-build
@@ -248,6 +254,7 @@ build-system-opensuse:
<<: *native_build_job_definition
variables:
IMAGE: opensuse-leap
+ CONFIGURE_ARGS: --enable-fdt=system
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
MAKE_CHECK_ARGS: check-build
artifacts:
@@ -411,6 +418,13 @@ build-user-plugins:
MAKE_CHECK_ARGS: check-tcg
timeout: 1h 30m
+build-user-centos7:
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: centos7
+ CONFIGURE_ARGS: --disable-system --disable-tools --disable-docs
+ MAKE_CHECK_ARGS: check-tcg
+
build-some-softmmu-plugins:
<<: *native_build_job_definition
variables:
@@ -606,6 +620,7 @@ build-without-default-features:
variables:
IMAGE: debian-amd64
CONFIGURE_ARGS: --without-default-features --disable-user
+ --target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu
MAKE_CHECK_ARGS: check-unit
check-patch:
diff --git a/Makefile b/Makefile
index a380bbfa12..291ea19f2e 100644
--- a/Makefile
+++ b/Makefile
@@ -236,7 +236,6 @@ distclean: clean
rm -f config-host.mak config-host.h*
rm -f tests/tcg/config-*.mak
rm -f config-all-disas.mak config.status
- rm -f tests/qemu-iotests/common.env
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
rm -f *-config-target.h *-config-devices.mak *-config-devices.h
diff --git a/backends/hostmem.c b/backends/hostmem.c
index 9f9ac95edd..be0c3b079f 100644
--- a/backends/hostmem.c
+++ b/backends/hostmem.c
@@ -80,7 +80,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
uint16List *host_nodes = NULL;
- uint16List **node = &host_nodes;
+ uint16List **tail = &host_nodes;
unsigned long value;
value = find_first_bit(backend->host_nodes, MAX_NODES);
@@ -88,9 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
goto ret;
}
- *node = g_malloc0(sizeof(**node));
- (*node)->value = value;
- node = &(*node)->next;
+ QAPI_LIST_APPEND(tail, value);
do {
value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1);
@@ -98,9 +96,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
break;
}
- *node = g_malloc0(sizeof(**node));
- (*node)->value = value;
- node = &(*node)->next;
+ QAPI_LIST_APPEND(tail, value);
} while (true);
ret:
diff --git a/block.c b/block.c
index 8b9d457546..91a66d4f3e 100644
--- a/block.c
+++ b/block.c
@@ -4660,6 +4660,31 @@ static void bdrv_delete(BlockDriverState *bs)
g_free(bs);
}
+BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
+ int flags, Error **errp)
+{
+ BlockDriverState *new_node_bs;
+ Error *local_err = NULL;
+
+ new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp);
+ if (new_node_bs == NULL) {
+ error_prepend(errp, "Could not create node: ");
+ return NULL;
+ }
+
+ bdrv_drained_begin(bs);
+ bdrv_replace_node(bs, new_node_bs, &local_err);
+ bdrv_drained_end(bs);
+
+ if (local_err) {
+ bdrv_unref(new_node_bs);
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ return new_node_bs;
+}
+
/*
* Run consistency checks on an image
*
diff --git a/block/backup-top.c b/block/backup-top.c
index fe6883cc97..6e7e7bf340 100644
--- a/block/backup-top.c
+++ b/block/backup-top.c
@@ -61,7 +61,7 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
- return block_copy(s->bcs, off, end - off, NULL);
+ return block_copy(s->bcs, off, end - off, true);
}
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
@@ -186,6 +186,7 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
BlockDriverState *target,
const char *filter_node_name,
uint64_t cluster_size,
+ BackupPerf *perf,
BdrvRequestFlags write_flags,
BlockCopyState **bcs,
Error **errp)
@@ -244,7 +245,8 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
state->cluster_size = cluster_size;
state->bcs = block_copy_state_new(top->backing, state->target,
- cluster_size, write_flags, &local_err);
+ cluster_size, perf->use_copy_range,
+ write_flags, &local_err);
if (local_err) {
error_prepend(&local_err, "Cannot create block-copy-state: ");
goto fail;
diff --git a/block/backup-top.h b/block/backup-top.h
index e5cabfa197..b28b0031c4 100644
--- a/block/backup-top.h
+++ b/block/backup-top.h
@@ -33,6 +33,7 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
BlockDriverState *target,
const char *filter_node_name,
uint64_t cluster_size,
+ BackupPerf *perf,
BdrvRequestFlags write_flags,
BlockCopyState **bcs,
Error **errp);
diff --git a/block/backup.c b/block/backup.c
index 9afa0bf3b4..cc525d5544 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -22,7 +22,6 @@
#include "block/block-copy.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
-#include "qemu/ratelimit.h"
#include "qemu/cutils.h"
#include "sysemu/block-backend.h"
#include "qemu/bitmap.h"
@@ -44,40 +43,17 @@ typedef struct BackupBlockJob {
BlockdevOnError on_source_error;
BlockdevOnError on_target_error;
uint64_t len;
- uint64_t bytes_read;
int64_t cluster_size;
+ BackupPerf perf;
BlockCopyState *bcs;
+
+ bool wait;
+ BlockCopyCallState *bg_bcs_call;
} BackupBlockJob;
static const BlockJobDriver backup_job_driver;
-static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
-{
- BackupBlockJob *s = opaque;
-
- s->bytes_read += bytes;
-}
-
-static int coroutine_fn backup_do_cow(BackupBlockJob *job,
- int64_t offset, uint64_t bytes,
- bool *error_is_read)
-{
- int ret = 0;
- int64_t start, end; /* bytes */
-
- start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
- end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size);
-
- trace_backup_do_cow_enter(job, start, offset, bytes);
-
- ret = block_copy(job->bcs, start, end - start, error_is_read);
-
- trace_backup_do_cow_return(job, offset, bytes, ret);
-
- return ret;
-}
-
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{
BdrvDirtyBitmap *bm;
@@ -157,53 +133,96 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
}
}
-static bool coroutine_fn yield_and_check(BackupBlockJob *job)
+static void coroutine_fn backup_block_copy_callback(void *opaque)
{
- uint64_t delay_ns;
-
- if (job_is_cancelled(&job->common.job)) {
- return true;
- }
-
- /*
- * We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
- * return. Without a yield, the VM would not reboot.
- */
- delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
- job->bytes_read = 0;
- job_sleep_ns(&job->common.job, delay_ns);
+ BackupBlockJob *s = opaque;
- if (job_is_cancelled(&job->common.job)) {
- return true;
+ if (s->wait) {
+ s->wait = false;
+ aio_co_wake(s->common.job.co);
+ } else {
+ job_enter(&s->common.job);
}
-
- return false;
}
static int coroutine_fn backup_loop(BackupBlockJob *job)
{
- bool error_is_read;
- int64_t offset;
- BdrvDirtyBitmapIter *bdbi;
+ BlockCopyCallState *s = NULL;
int ret = 0;
+ bool error_is_read;
+ BlockErrorAction act;
+
+ while (true) { /* retry loop */
+ job->bg_bcs_call = s = block_copy_async(job->bcs, 0,
+ QEMU_ALIGN_UP(job->len, job->cluster_size),
+ job->perf.max_workers, job->perf.max_chunk,
+ backup_block_copy_callback, job);
+
+ while (!block_copy_call_finished(s) &&
+ !job_is_cancelled(&job->common.job))
+ {
+ job_yield(&job->common.job);
+ }
- bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs));
- while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) {
- do {
- if (yield_and_check(job)) {
- goto out;
- }
- ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read);
- if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
- BLOCK_ERROR_ACTION_REPORT)
- {
- goto out;
- }
- } while (ret < 0);
+ if (!block_copy_call_finished(s)) {
+ assert(job_is_cancelled(&job->common.job));
+ /*
+ * Note that we can't use job_yield() here, as it doesn't work for
+ * cancelled job.
+ */
+ block_copy_call_cancel(s);
+ job->wait = true;
+ qemu_coroutine_yield();
+ assert(block_copy_call_finished(s));
+ ret = 0;
+ goto out;
+ }
+
+ if (job_is_cancelled(&job->common.job) ||
+ block_copy_call_succeeded(s))
+ {
+ ret = 0;
+ goto out;
+ }
+
+ if (block_copy_call_cancelled(s)) {
+ /*
+ * Job is not cancelled but only block-copy call. This is possible
+ * after job pause. Now the pause is finished, start new block-copy
+ * iteration.
+ */
+ block_copy_call_free(s);
+ continue;
+ }
+
+ /* The only remaining case is failed block-copy call. */
+ assert(block_copy_call_failed(s));
+
+ ret = block_copy_call_status(s, &error_is_read);
+ act = backup_error_action(job, error_is_read, -ret);
+ switch (act) {
+ case BLOCK_ERROR_ACTION_REPORT:
+ goto out;
+ case BLOCK_ERROR_ACTION_STOP:
+ /*
+ * Go to pause prior to starting new block-copy call on the next
+ * iteration.
+ */
+ job_pause_point(&job->common.job);
+ break;
+ case BLOCK_ERROR_ACTION_IGNORE:
+ /* Proceed to new block-copy call to retry. */
+ break;
+ default:
+ abort();
+ }
+
+ block_copy_call_free(s);
}
- out:
- bdrv_dirty_iter_free(bdbi);
+out:
+ block_copy_call_free(s);
+ job->bg_bcs_call = NULL;
return ret;
}
@@ -235,7 +254,7 @@ static void backup_init_bcs_bitmap(BackupBlockJob *job)
static int coroutine_fn backup_run(Job *job, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
- int ret = 0;
+ int ret;
backup_init_bcs_bitmap(s);
@@ -244,14 +263,19 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
int64_t count;
for (offset = 0; offset < s->len; ) {
- if (yield_and_check(s)) {
- ret = -ECANCELED;
- goto out;
+ if (job_is_cancelled(job)) {
+ return -ECANCELED;
+ }
+
+ job_pause_point(job);
+
+ if (job_is_cancelled(job)) {
+ return -ECANCELED;
}
ret = block_copy_reset_unallocated(s->bcs, offset, &count);
if (ret < 0) {
- goto out;
+ return ret;
}
offset += count;
@@ -272,11 +296,37 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
job_yield(job);
}
} else {
- ret = backup_loop(s);
+ return backup_loop(s);
}
- out:
- return ret;
+ return 0;
+}
+
+static void coroutine_fn backup_pause(Job *job)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
+
+ if (s->bg_bcs_call && !block_copy_call_finished(s->bg_bcs_call)) {
+ block_copy_call_cancel(s->bg_bcs_call);
+ s->wait = true;
+ qemu_coroutine_yield();
+ }
+}
+
+static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+
+ /*
+ * block_job_set_speed() is called first from block_job_create(), when we
+ * don't yet have s->bcs.
+ */
+ if (s->bcs) {
+ block_copy_set_speed(s->bcs, speed);
+ if (s->bg_bcs_call) {
+ block_copy_kick(s->bg_bcs_call);
+ }
+ }
}
static const BlockJobDriver backup_job_driver = {
@@ -289,7 +339,9 @@ static const BlockJobDriver backup_job_driver = {
.commit = backup_commit,
.abort = backup_abort,
.clean = backup_clean,
- }
+ .pause = backup_pause,
+ },
+ .set_speed = backup_set_speed,
};
static int64_t backup_calculate_cluster_size(BlockDriverState *target,
@@ -335,6 +387,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BitmapSyncMode bitmap_mode,
bool compress,
const char *filter_node_name,
+ BackupPerf *perf,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
@@ -386,6 +439,29 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
return NULL;
}
+ cluster_size = backup_calculate_cluster_size(target, errp);
+ if (cluster_size < 0) {
+ goto error;
+ }
+
+ if (perf->max_workers < 1) {
+ error_setg(errp, "max-workers must be greater than zero");
+ return NULL;
+ }
+
+ if (perf->max_chunk < 0) {
+ error_setg(errp, "max-chunk must be zero (which means no limit) or "
+ "positive");
+ return NULL;
+ }
+
+ if (perf->max_chunk && perf->max_chunk < cluster_size) {
+ error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
+ "cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
+ return NULL;
+ }
+
+
if (sync_bitmap) {
/* If we need to write to this bitmap, check that we can: */
if (bitmap_mode != BITMAP_SYNC_MODE_NEVER &&
@@ -418,11 +494,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
goto error;
}
- cluster_size = backup_calculate_cluster_size(target, errp);
- if (cluster_size < 0) {
- goto error;
- }
-
/*
* If source is in backing chain of target assume that target is going to be
* used for "image fleecing", i.e. it should represent a kind of snapshot of
@@ -441,7 +512,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
(compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
- cluster_size, write_flags, &bcs, errp);
+ cluster_size, perf,
+ write_flags, &bcs, errp);
if (!backup_top) {
goto error;
}
@@ -464,9 +536,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
job->bcs = bcs;
job->cluster_size = cluster_size;
job->len = len;
+ job->perf = *perf;
- block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job);
block_copy_set_progress_meter(bcs, &job->common.job.progress);
+ block_copy_set_speed(bcs, speed);
/* Required permissions are already taken by backup-top target */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
diff --git a/block/block-backend.c b/block/block-backend.c
index ce78d30794..e493f17515 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1826,17 +1826,30 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
}
}
-bool blk_is_read_only(BlockBackend *blk)
+/*
+ * Returns true if the BlockBackend can support taking write permissions
+ * (because its root node is not read-only).
+ */
+bool blk_supports_write_perm(BlockBackend *blk)
{
BlockDriverState *bs = blk_bs(blk);
if (bs) {
- return bdrv_is_read_only(bs);
+ return !bdrv_is_read_only(bs);
} else {
- return blk->root_state.read_only;
+ return !blk->root_state.read_only;
}
}
+/*
+ * Returns true if the BlockBackend can be written to in its current
+ * configuration (i.e. if write permission have been requested)
+ */
+bool blk_is_writable(BlockBackend *blk)
+{
+ return blk->perm & BLK_PERM_WRITE;
+}
+
bool blk_is_sg(BlockBackend *blk)
{
BlockDriverState *bs = blk_bs(blk);
diff --git a/block/block-copy.c b/block/block-copy.c
index cd9bc47c8f..39ae481c8b 100644
--- a/block/block-copy.c
+++ b/block/block-copy.c
@@ -26,11 +26,34 @@
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
#define BLOCK_COPY_MAX_MEM (128 * MiB)
#define BLOCK_COPY_MAX_WORKERS 64
+#define BLOCK_COPY_SLICE_TIME 100000000ULL /* ns */
static coroutine_fn int block_copy_task_entry(AioTask *task);
typedef struct BlockCopyCallState {
- bool failed;
+ /* IN parameters. Initialized in block_copy_async() and never changed. */
+ BlockCopyState *s;
+ int64_t offset;
+ int64_t bytes;
+ int max_workers;
+ int64_t max_chunk;
+ bool ignore_ratelimit;
+ BlockCopyAsyncCallbackFunc cb;
+ void *cb_opaque;
+
+ /* Coroutine where async block-copy is running */
+ Coroutine *co;
+
+ /* To reference all call states from BlockCopyState */
+ QLIST_ENTRY(BlockCopyCallState) list;
+
+ /* State */
+ int ret;
+ bool finished;
+ QemuCoSleepState *sleep_state;
+ bool cancelled;
+
+ /* OUT parameters */
bool error_is_read;
} BlockCopyCallState;
@@ -65,7 +88,8 @@ typedef struct BlockCopyState {
bool use_copy_range;
int64_t copy_size;
uint64_t len;
- QLIST_HEAD(, BlockCopyTask) tasks;
+ QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */
+ QLIST_HEAD(, BlockCopyCallState) calls;
BdrvRequestFlags write_flags;
@@ -86,11 +110,11 @@ typedef struct BlockCopyState {
bool skip_unallocated;
ProgressMeter *progress;
- /* progress_bytes_callback: called when some copying progress is done. */
- ProgressBytesCallbackFunc progress_bytes_callback;
- void *progress_opaque;
SharedResource *mem;
+
+ uint64_t speed;
+ RateLimit rate_limit;
} BlockCopyState;
static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
@@ -134,10 +158,11 @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
int64_t offset, int64_t bytes)
{
BlockCopyTask *task;
+ int64_t max_chunk = MIN_NON_ZERO(s->copy_size, call_state->max_chunk);
if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap,
offset, offset + bytes,
- s->copy_size, &offset, &bytes))
+ max_chunk, &offset, &bytes))
{
return NULL;
}
@@ -218,7 +243,7 @@ static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target)
}
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
- int64_t cluster_size,
+ int64_t cluster_size, bool use_copy_range,
BdrvRequestFlags write_flags, Error **errp)
{
BlockCopyState *s;
@@ -260,24 +285,16 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
* We enable copy-range, but keep small copy_size, until first
* successful copy_range (look at block_copy_do_copy).
*/
- s->use_copy_range = true;
+ s->use_copy_range = use_copy_range;
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
}
QLIST_INIT(&s->tasks);
+ QLIST_INIT(&s->calls);
return s;
}
-void block_copy_set_progress_callback(
- BlockCopyState *s,
- ProgressBytesCallbackFunc progress_bytes_callback,
- void *progress_opaque)
-{
- s->progress_bytes_callback = progress_bytes_callback;
- s->progress_opaque = progress_opaque;
-}
-
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
{
s->progress = pm;
@@ -420,12 +437,11 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
&error_is_read);
- if (ret < 0 && !t->call_state->failed) {
- t->call_state->failed = true;
+ if (ret < 0 && !t->call_state->ret) {
+ t->call_state->ret = ret;
t->call_state->error_is_read = error_is_read;
} else {
progress_work_done(t->s->progress, t->bytes);
- t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
}
co_put_to_shres(t->s->mem, t->bytes);
block_copy_task_end(t, ret);
@@ -544,15 +560,17 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
* Returns 1 if dirty clusters found and successfully copied, 0 if no dirty
* clusters found and -errno on failure.
*/
-static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
- int64_t offset, int64_t bytes,
- bool *error_is_read)
+static int coroutine_fn
+block_copy_dirty_clusters(BlockCopyCallState *call_state)
{
+ BlockCopyState *s = call_state->s;
+ int64_t offset = call_state->offset;
+ int64_t bytes = call_state->bytes;
+
int ret = 0;
bool found_dirty = false;
int64_t end = offset + bytes;
AioTaskPool *aio = NULL;
- BlockCopyCallState call_state = {false, false};
/*
* block_copy() user is responsible for keeping source and target in same
@@ -564,11 +582,11 @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
- while (bytes && aio_task_pool_status(aio) == 0) {
+ while (bytes && aio_task_pool_status(aio) == 0 && !call_state->cancelled) {
BlockCopyTask *task;
int64_t status_bytes;
- task = block_copy_task_create(s, &call_state, offset, bytes);
+ task = block_copy_task_create(s, call_state, offset, bytes);
if (!task) {
/* No more dirty bits in the bitmap */
trace_block_copy_skip_range(s, offset, bytes);
@@ -599,6 +617,21 @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
}
task->zeroes = ret & BDRV_BLOCK_ZERO;
+ if (s->speed) {
+ if (!call_state->ignore_ratelimit) {
+ uint64_t ns = ratelimit_calculate_delay(&s->rate_limit, 0);
+ if (ns > 0) {
+ block_copy_task_end(task, -EAGAIN);
+ g_free(task);
+ qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, ns,
+ &call_state->sleep_state);
+ continue;
+ }
+ }
+
+ ratelimit_calculate_delay(&s->rate_limit, task->bytes);
+ }
+
trace_block_copy_process(s, task->offset);
co_get_from_shres(s->mem, task->bytes);
@@ -607,7 +640,7 @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
bytes = end - offset;
if (!aio && bytes) {
- aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS);
+ aio = aio_task_pool_new(call_state->max_workers);
}
ret = block_copy_task_run(aio, task);
@@ -633,15 +666,19 @@ out:
aio_task_pool_free(aio);
}
- if (error_is_read && ret < 0) {
- *error_is_read = call_state.error_is_read;
- }
return ret < 0 ? ret : found_dirty;
}
+void block_copy_kick(BlockCopyCallState *call_state)
+{
+ if (call_state->sleep_state) {
+ qemu_co_sleep_wake(call_state->sleep_state);
+ }
+}
+
/*
- * block_copy
+ * block_copy_common
*
* Copy requested region, accordingly to dirty bitmap.
* Collaborate with parallel block_copy requests: if they succeed it will help
@@ -649,16 +686,18 @@ out:
* it means that some I/O operation failed in context of _this_ block_copy call,
* not some parallel operation.
*/
-int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
- bool *error_is_read)
+static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
{
int ret;
+ QLIST_INSERT_HEAD(&call_state->s->calls, call_state, list);
+
do {
- ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read);
+ ret = block_copy_dirty_clusters(call_state);
- if (ret == 0) {
- ret = block_copy_wait_one(s, offset, bytes);
+ if (ret == 0 && !call_state->cancelled) {
+ ret = block_copy_wait_one(call_state->s, call_state->offset,
+ call_state->bytes);
}
/*
@@ -670,11 +709,110 @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
* 2. We have waited for some intersecting block-copy request
* It may have failed and produced new dirty bits.
*/
- } while (ret > 0);
+ } while (ret > 0 && !call_state->cancelled);
+
+ call_state->finished = true;
+
+ if (call_state->cb) {
+ call_state->cb(call_state->cb_opaque);
+ }
+
+ QLIST_REMOVE(call_state, list);
return ret;
}
+int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
+ bool ignore_ratelimit)
+{
+ BlockCopyCallState call_state = {
+ .s = s,
+ .offset = start,
+ .bytes = bytes,
+ .ignore_ratelimit = ignore_ratelimit,
+ .max_workers = BLOCK_COPY_MAX_WORKERS,
+ };
+
+ return block_copy_common(&call_state);
+}
+
+static void coroutine_fn block_copy_async_co_entry(void *opaque)
+{
+ block_copy_common(opaque);
+}
+
+BlockCopyCallState *block_copy_async(BlockCopyState *s,
+ int64_t offset, int64_t bytes,
+ int max_workers, int64_t max_chunk,
+ BlockCopyAsyncCallbackFunc cb,
+ void *cb_opaque)
+{
+ BlockCopyCallState *call_state = g_new(BlockCopyCallState, 1);
+
+ *call_state = (BlockCopyCallState) {
+ .s = s,
+ .offset = offset,
+ .bytes = bytes,
+ .max_workers = max_workers,
+ .max_chunk = max_chunk,
+ .cb = cb,
+ .cb_opaque = cb_opaque,
+
+ .co = qemu_coroutine_create(block_copy_async_co_entry, call_state),
+ };
+
+ qemu_coroutine_enter(call_state->co);
+
+ return call_state;
+}
+
+void block_copy_call_free(BlockCopyCallState *call_state)
+{
+ if (!call_state) {
+ return;
+ }
+
+ assert(call_state->finished);
+ g_free(call_state);
+}
+
+bool block_copy_call_finished(BlockCopyCallState *call_state)
+{
+ return call_state->finished;
+}
+
+bool block_copy_call_succeeded(BlockCopyCallState *call_state)
+{
+ return call_state->finished && !call_state->cancelled &&
+ call_state->ret == 0;
+}
+
+bool block_copy_call_failed(BlockCopyCallState *call_state)
+{
+ return call_state->finished && !call_state->cancelled &&
+ call_state->ret < 0;
+}
+
+bool block_copy_call_cancelled(BlockCopyCallState *call_state)
+{
+ return call_state->cancelled;
+}
+
+int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read)
+{
+ assert(call_state->finished);
+ if (error_is_read) {
+ *error_is_read = call_state->error_is_read;
+ }
+ return call_state->ret;
+}
+
+void block_copy_call_cancel(BlockCopyCallState *call_state)
+{
+ call_state->cancelled = true;
+ block_copy_kick(call_state);
+}
+
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
{
return s->copy_bitmap;
@@ -684,3 +822,18 @@ void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
{
s->skip_unallocated = skip;
}
+
+void block_copy_set_speed(BlockCopyState *s, uint64_t speed)
+{
+ s->speed = speed;
+ if (speed > 0) {
+ ratelimit_set_speed(&s->rate_limit, speed, BLOCK_COPY_SLICE_TIME);
+ }
+
+ /*
+ * Note: it's good to kick all call states from here, but it should be done
+ * only from a coroutine, to not crash if s->calls list changed while
+ * entering one call. So for now, the only user of this function kicks its
+ * only one call_state by hand.
+ */
+}
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
index 2816e61afe..9cad9e1b8c 100644
--- a/block/copy-on-read.c
+++ b/block/copy-on-read.c
@@ -23,11 +23,26 @@
#include "qemu/osdep.h"
#include "block/block_int.h"
#include "qemu/module.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
+#include "block/copy-on-read.h"
+
+
+typedef struct BDRVStateCOR {
+ bool active;
+ BlockDriverState *bottom_bs;
+ bool chain_frozen;
+} BDRVStateCOR;
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
+ BlockDriverState *bottom_bs = NULL;
+ BDRVStateCOR *state = bs->opaque;
+ /* Find a bottom node name, if any */
+ const char *bottom_node = qdict_get_try_str(options, "bottom");
+
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
false, errp);
@@ -35,6 +50,8 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
return -EINVAL;
}
+ bs->supported_read_flags = BDRV_REQ_PREFETCH;
+
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
@@ -42,6 +59,44 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
bs->file->bs->supported_zero_flags);
+ if (bottom_node) {
+ bottom_bs = bdrv_find_node(bottom_node);
+ if (!bottom_bs) {
+ error_setg(errp, "Bottom node '%s' not found", bottom_node);
+ qdict_del(options, "bottom");
+ return -EINVAL;
+ }
+ qdict_del(options, "bottom");
+
+ if (!bottom_bs->drv) {
+ error_setg(errp, "Bottom node '%s' not opened", bottom_node);
+ return -EINVAL;
+ }
+
+ if (bottom_bs->drv->is_filter) {
+ error_setg(errp, "Bottom node '%s' is a filter", bottom_node);
+ return -EINVAL;
+ }
+
+ if (bdrv_freeze_backing_chain(bs, bottom_bs, errp) < 0) {
+ return -EINVAL;
+ }
+ state->chain_frozen = true;
+
+ /*
+ * We do freeze the chain, so it shouldn't be removed. Still, storing a
+ * pointer worth bdrv_ref().
+ */
+ bdrv_ref(bottom_bs);
+ }
+ state->active = true;
+ state->bottom_bs = bottom_bs;
+
+ /*
+ * We don't need to call bdrv_child_refresh_perms() now as the permissions
+ * will be updated later when the filter node gets its parent.
+ */
+
return 0;
}
@@ -57,6 +112,17 @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
+ BDRVStateCOR *s = bs->opaque;
+
+ if (!s->active) {
+ /*
+ * While the filter is being removed
+ */
+ *nperm = 0;
+ *nshared = BLK_PERM_ALL;
+ return;
+ }
+
*nperm = perm & PERM_PASSTHROUGH;
*nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
@@ -74,21 +140,67 @@ static int64_t cor_getlength(BlockDriverState *bs)
}
-static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
- uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov,
+ size_t qiov_offset,
+ int flags)
{
- return bdrv_co_preadv(bs->file, offset, bytes, qiov,
- flags | BDRV_REQ_COPY_ON_READ);
+ int64_t n;
+ int local_flags;
+ int ret;
+ BDRVStateCOR *state = bs->opaque;
+
+ if (!state->bottom_bs) {
+ return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
+ flags | BDRV_REQ_COPY_ON_READ);
+ }
+
+ while (bytes) {
+ local_flags = flags;
+
+ /* In case of failure, try to copy-on-read anyway */
+ ret = bdrv_is_allocated(bs->file->bs, offset, bytes, &n);
+ if (ret <= 0) {
+ ret = bdrv_is_allocated_above(bdrv_backing_chain_next(bs->file->bs),
+ state->bottom_bs, true, offset,
+ n, &n);
+ if (ret > 0 || ret < 0) {
+ local_flags |= BDRV_REQ_COPY_ON_READ;
+ }
+ /* Finish earlier if the end of a backing file has been reached */
+ if (n == 0) {
+ break;
+ }
+ }
+
+ /* Skip if neither read nor write are needed */
+ if ((local_flags & (BDRV_REQ_PREFETCH | BDRV_REQ_COPY_ON_READ)) !=
+ BDRV_REQ_PREFETCH) {
+ ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset,
+ local_flags);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ offset += n;
+ qiov_offset += n;
+ bytes -= n;
+ }
+
+ return 0;
}
-static int coroutine_fn cor_co_pwritev(BlockDriverState *bs,
- uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov,
+ size_t qiov_offset, int flags)
{
-
- return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
+ return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
+ flags);
}
@@ -129,16 +241,31 @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
}
+static void cor_close(BlockDriverState *bs)
+{
+ BDRVStateCOR *s = bs->opaque;
+
+ if (s->chain_frozen) {
+ s->chain_frozen = false;
+ bdrv_unfreeze_backing_chain(bs, s->bottom_bs);
+ }
+
+ bdrv_unref(s->bottom_bs);
+}
+
+
static BlockDriver bdrv_copy_on_read = {
.format_name = "copy-on-read",
+ .instance_size = sizeof(BDRVStateCOR),
.bdrv_open = cor_open,
+ .bdrv_close = cor_close,
.bdrv_child_perm = cor_child_perm,
.bdrv_getlength = cor_getlength,
- .bdrv_co_preadv = cor_co_preadv,
- .bdrv_co_pwritev = cor_co_pwritev,
+ .bdrv_co_preadv_part = cor_co_preadv_part,
+ .bdrv_co_pwritev_part = cor_co_pwritev_part,
.bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes,
.bdrv_co_pdiscard = cor_co_pdiscard,
.bdrv_co_pwritev_compressed = cor_co_pwritev_compressed,
@@ -150,6 +277,39 @@ static BlockDriver bdrv_copy_on_read = {
.is_filter = true,
};
+
+void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs)
+{
+ BdrvChild *child;
+ BlockDriverState *bs;
+ BDRVStateCOR *s = cor_filter_bs->opaque;
+
+ child = bdrv_filter_child(cor_filter_bs);
+ if (!child) {
+ return;
+ }
+ bs = child->bs;
+
+ /* Retain the BDS until we complete the graph change. */
+ bdrv_ref(bs);
+ /* Hold a guest back from writing while permissions are being reset. */
+ bdrv_drained_begin(bs);
+ /* Drop permissions before the graph change. */
+ s->active = false;
+ /* unfreeze, as otherwise bdrv_replace_node() will fail */
+ if (s->chain_frozen) {
+ s->chain_frozen = false;
+ bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs);
+ }
+ bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort);
+ bdrv_replace_node(cor_filter_bs, bs, &error_abort);
+
+ bdrv_drained_end(bs);
+ bdrv_unref(bs);
+ bdrv_unref(cor_filter_bs);
+}
+
+
static void bdrv_copy_on_read_init(void)
{
bdrv_register(&bdrv_copy_on_read);
diff --git a/block/copy-on-read.h b/block/copy-on-read.h
new file mode 100644
index 0000000000..7bf405dccd
--- /dev/null
+++ b/block/copy-on-read.h
@@ -0,0 +1,32 @@
+/*
+ * Copy-on-read filter block driver
+ *
+ * The filter driver performs Copy-On-Read (COR) operations
+ *
+ * Copyright (c) 2018-2020 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
+ *
+ * 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/>.
+ */
+
+#ifndef BLOCK_COPY_ON_READ
+#define BLOCK_COPY_ON_READ
+
+#include "block/block_int.h"
+
+void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs);
+
+#endif /* BLOCK_COPY_ON_READ */
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index c01319b188..9b9cd71238 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -572,12 +572,12 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
{
BdrvDirtyBitmap *bm;
BlockDirtyInfoList *list = NULL;
- BlockDirtyInfoList **plist = &list;
+ BlockDirtyInfoList **tail = &list;
bdrv_dirty_bitmaps_lock(bs);
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
- BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
+
info->count = bdrv_get_dirty_count(bm);
info->granularity = bdrv_dirty_bitmap_granularity(bm);
info->has_name = !!bm->name;
@@ -588,9 +588,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
info->persistent = bm->persistent;
info->has_inconsistent = bm->inconsistent;
info->inconsistent = bm->inconsistent;
- entry->value = info;
- *plist = entry;
- plist = &entry->next;
+ QAPI_LIST_APPEND(tail, info);
}
bdrv_dirty_bitmaps_unlock(bs);
diff --git a/block/export/export.c b/block/export/export.c
index b716c1522c..fec7d9f738 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -342,11 +342,10 @@ void qmp_block_export_del(const char *id,
BlockExportInfoList *qmp_query_block_exports(Error **errp)
{
- BlockExportInfoList *head = NULL, **p_next = &head;
+ BlockExportInfoList *head = NULL, **tail = &head;
BlockExport *exp;
QLIST_FOREACH(exp, &block_exports, next) {
- BlockExportInfoList *entry = g_new0(BlockExportInfoList, 1);
BlockExportInfo *info = g_new(BlockExportInfo, 1);
*info = (BlockExportInfo) {
.id = g_strdup(exp->id),
@@ -355,9 +354,7 @@ BlockExportInfoList *qmp_query_block_exports(Error **errp)
.shutting_down = !exp->user_owned,
};
- entry->value = info;
- *p_next = entry;
- p_next = &entry->next;
+ QAPI_LIST_APPEND(tail, info);
}
return head;
diff --git a/block/file-posix.c b/block/file-posix.c
index 00cdaaa2d4..11aafa9d82 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -216,6 +216,20 @@ typedef struct RawPosixAIOData {
static int cdrom_reopen(BlockDriverState *bs);
#endif
+/*
+ * Elide EAGAIN and EACCES details when failing to lock, as this
+ * indicates that the specified file region is already locked by
+ * another process, which is considered a common scenario.
+ */
+#define raw_lock_error_setg_errno(errp, err, fmt, ...) \
+ do { \
+ if ((err) == EAGAIN || (err) == EACCES) { \
+ error_setg((errp), (fmt), ## __VA_ARGS__); \
+ } else { \
+ error_setg_errno((errp), (err), (fmt), ## __VA_ARGS__); \
+ } \
+ } while (0)
+
#if defined(__NetBSD__)
static int raw_normalize_devicepath(const char **filename, Error **errp)
{
@@ -836,7 +850,8 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
if ((perm_lock_bits & bit) && !(locked_perm & bit)) {
ret = qemu_lock_fd(fd, off, 1, false);
if (ret) {
- error_setg(errp, "Failed to lock byte %d", off);
+ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d",
+ off);
return ret;
} else if (s) {
s->locked_perm |= bit;
@@ -844,7 +859,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
} else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) {
ret = qemu_unlock_fd(fd, off, 1);
if (ret) {
- error_setg(errp, "Failed to unlock byte %d", off);
+ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off);
return ret;
} else if (s) {
s->locked_perm &= ~bit;
@@ -857,7 +872,8 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) {
ret = qemu_lock_fd(fd, off, 1, false);
if (ret) {
- error_setg(errp, "Failed to lock byte %d", off);
+ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d",
+ off);
return ret;
} else if (s) {
s->locked_shared_perm |= bit;
@@ -866,7 +882,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
!(shared_perm_lock_bits & bit)) {
ret = qemu_unlock_fd(fd, off, 1);
if (ret) {
- error_setg(errp, "Failed to unlock byte %d", off);
+ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off);
return ret;
} else if (s) {
s->locked_shared_perm &= ~bit;
@@ -890,9 +906,10 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
ret = qemu_lock_fd_test(fd, off, 1, true);
if (ret) {
char *perm_name = bdrv_perm_names(p);
- error_setg(errp,
- "Failed to get \"%s\" lock",
- perm_name);
+
+ raw_lock_error_setg_errno(errp, -ret,
+ "Failed to get \"%s\" lock",
+ perm_name);
g_free(perm_name);
return ret;
}
@@ -905,9 +922,10 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
ret = qemu_lock_fd_test(fd, off, 1, true);
if (ret) {
char *perm_name = bdrv_perm_names(p);
- error_setg(errp,
- "Failed to get shared \"%s\" lock",
- perm_name);
+
+ raw_lock_error_setg_errno(errp, -ret,
+ "Failed to get shared \"%s\" lock",
+ perm_name);
g_free(perm_name);
return ret;
}
diff --git a/block/gluster.c b/block/gluster.c
index 1f8699b938..e8ee14c8e9 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -514,7 +514,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
{
QemuOpts *opts;
SocketAddress *gsconf = NULL;
- SocketAddressList *curr = NULL;
+ SocketAddressList **tail;
QDict *backing_options = NULL;
Error *local_err = NULL;
char *str = NULL;
@@ -547,6 +547,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
}
gconf->path = g_strdup(ptr);
qemu_opts_del(opts);
+ tail = &gconf->server;
for (i = 0; i < num_servers; i++) {
str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", i);
@@ -655,15 +656,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
qemu_opts_del(opts);
}
- if (gconf->server == NULL) {
- gconf->server = g_new0(SocketAddressList, 1);
- gconf->server->value = gsconf;
- curr = gconf->server;
- } else {
- curr->next = g_new0(SocketAddressList, 1);
- curr->next->value = gsconf;
- curr = curr->next;
- }
+ QAPI_LIST_APPEND(tail, gsconf);
gsconf = NULL;
qobject_unref(backing_options);
diff --git a/block/io.c b/block/io.c
index 95b1c56c06..d203435a73 100644
--- a/block/io.c
+++ b/block/io.c
@@ -1453,6 +1453,9 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
if (flags & BDRV_REQ_COPY_ON_READ) {
int64_t pnum;
+ /* The flag BDRV_REQ_COPY_ON_READ has reached its addressee */
+ flags &= ~BDRV_REQ_COPY_ON_READ;
+
ret = bdrv_is_allocated(bs, offset, bytes, &pnum);
if (ret < 0) {
goto out;
@@ -1474,9 +1477,11 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
goto out;
}
+ assert(!(flags & ~bs->supported_read_flags));
+
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
if (bytes <= max_bytes && bytes <= max_transfer) {
- ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, 0);
+ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, flags);
goto out;
}
@@ -1489,7 +1494,8 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
num, qiov,
- qiov_offset + bytes - bytes_remaining, 0);
+ qiov_offset + bytes - bytes_remaining,
+ flags);
max_bytes -= num;
} else {
num = bytes_remaining;
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index d15a2be827..afd75ab628 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -507,9 +507,10 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
qmp_block_stream(true, device, device, base != NULL, base, false, NULL,
- false, NULL, qdict_haskey(qdict, "speed"), speed, true,
- BLOCKDEV_ON_ERROR_REPORT, false, false, false, false,
- &error);
+ false, NULL, false, NULL,
+ qdict_haskey(qdict, "speed"), speed, true,
+ BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false,
+ false, &error);
hmp_handle_error(mon, error);
}
diff --git a/block/qapi.c b/block/qapi.c
index 0ca206f559..0a96099e36 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -198,7 +198,7 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
{
int i, sn_count;
QEMUSnapshotInfo *sn_tab = NULL;
- SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL;
+ SnapshotInfoList *head = NULL, **tail = &head;
SnapshotInfo *info;
sn_count = bdrv_snapshot_list(bs, &sn_tab);
@@ -233,17 +233,7 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
info->icount = sn_tab[i].icount;
info->has_icount = sn_tab[i].icount != -1ULL;
- info_list = g_new0(SnapshotInfoList, 1);
- info_list->value = info;
-
- /* XXX: waiting for the qapi to support qemu-queue.h types */
- if (!cur_item) {
- head = cur_item = info_list;
- } else {
- cur_item->next = info_list;
- cur_item = info_list;
- }
-
+ QAPI_LIST_APPEND(tail, info);
}
g_free(sn_tab);
@@ -418,17 +408,12 @@ static uint64List *uint64_list(uint64_t *list, int size)
{
int i;
uint64List *out_list = NULL;
- uint64List **pout_list = &out_list;
+ uint64List **tail = &out_list;
for (i = 0; i < size; i++) {
- uint64List *entry = g_new(uint64List, 1);
- entry->value = list[i];
- *pout_list = entry;
- pout_list = &entry->next;
+ QAPI_LIST_APPEND(tail, list[i]);
}
- *pout_list = NULL;
-
return out_list;
}
@@ -636,26 +621,21 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
bool query_nodes,
Error **errp)
{
- BlockStatsList *head = NULL, **p_next = &head;
+ BlockStatsList *head = NULL, **tail = &head;
BlockBackend *blk;
BlockDriverState *bs;
/* Just to be safe if query_nodes is not always initialized */
if (has_query_nodes && query_nodes) {
for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) {
- BlockStatsList *info = g_malloc0(sizeof(*info));
AioContext *ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
- info->value = bdrv_query_bds_stats(bs, false);
+ QAPI_LIST_APPEND(tail, bdrv_query_bds_stats(bs, false));
aio_context_release(ctx);
-
- *p_next = info;
- p_next = &info->next;
}
} else {
for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
- BlockStatsList *info;
AioContext *ctx = blk_get_aio_context(blk);
BlockStats *s;
char *qdev;
@@ -680,10 +660,7 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
bdrv_query_blk_stats(s->stats, blk);
aio_context_release(ctx);
- info = g_malloc0(sizeof(*info));
- info->value = s;
- *p_next = info;
- p_next = &info->next;
+ QAPI_LIST_APPEND(tail, s);
}
}
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index d7a31a8ddc..5eef82fa55 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -1061,7 +1061,7 @@ fail:
static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags)
{
Qcow2BitmapInfoFlagsList *list = NULL;
- Qcow2BitmapInfoFlagsList **plist = &list;
+ Qcow2BitmapInfoFlagsList **tail = &list;
int i;
static const struct {
@@ -1076,11 +1076,7 @@ static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags)
for (i = 0; i < map_size; ++i) {
if (flags & map[i].bme) {
- Qcow2BitmapInfoFlagsList *entry =
- g_new0(Qcow2BitmapInfoFlagsList, 1);
- entry->value = map[i].info;
- *plist = entry;
- plist = &entry->next;
+ QAPI_LIST_APPEND(tail, map[i].info);
flags &= ~map[i].bme;
}
}
@@ -1105,7 +1101,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
Qcow2BitmapList *bm_list;
Qcow2Bitmap *bm;
Qcow2BitmapInfoList *list = NULL;
- Qcow2BitmapInfoList **plist = &list;
+ Qcow2BitmapInfoList **tail = &list;
if (s->nb_bitmaps == 0) {
return NULL;
@@ -1119,13 +1115,10 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1);
- Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1);
info->granularity = 1U << bm->granularity_bits;
info->name = g_strdup(bm->name);
info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS);
- obj->value = info;
- *plist = obj;
- plist = &obj->next;
+ QAPI_LIST_APPEND(tail, info);
}
bitmap_list_free(bm_list);
diff --git a/block/replication.c b/block/replication.c
index 0c70215784..97be7ef4de 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -454,6 +454,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
int64_t active_length, hidden_length, disk_length;
AioContext *aio_context;
Error *local_err = NULL;
+ BackupPerf perf = { .use_copy_range = true, .max_workers = 1 };
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
@@ -558,6 +559,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
s->backup_job = backup_job_create(
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
+ &perf,
BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
backup_job_completed, bs, NULL, &local_err);
diff --git a/block/stream.c b/block/stream.c
index 236384f2f7..1fa742b0db 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -17,8 +17,10 @@
#include "block/blockjob_int.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qdict.h"
#include "qemu/ratelimit.h"
#include "sysemu/block-backend.h"
+#include "block/copy-on-read.h"
enum {
/*
@@ -33,10 +35,11 @@ typedef struct StreamBlockJob {
BlockJob common;
BlockDriverState *base_overlay; /* COW overlay (stream from this) */
BlockDriverState *above_base; /* Node directly above the base */
+ BlockDriverState *cor_filter_bs;
+ BlockDriverState *target_bs;
BlockdevOnError on_error;
char *backing_file_str;
bool bs_read_only;
- bool chain_frozen;
} StreamBlockJob;
static int coroutine_fn stream_populate(BlockBackend *blk,
@@ -44,39 +47,28 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
{
assert(bytes < SIZE_MAX);
- return blk_co_preadv(blk, offset, bytes, NULL,
- BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH);
-}
-
-static void stream_abort(Job *job)
-{
- StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
-
- if (s->chain_frozen) {
- BlockJob *bjob = &s->common;
- bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->above_base);
- }
+ return blk_co_preadv(blk, offset, bytes, NULL, BDRV_REQ_PREFETCH);
}
static int stream_prepare(Job *job)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
- BlockJob *bjob = &s->common;
- BlockDriverState *bs = blk_bs(bjob->blk);
- BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
+ BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base);
+ BlockDriverState *unfiltered_base = bdrv_skip_filters(base);
Error *local_err = NULL;
int ret = 0;
- bdrv_unfreeze_backing_chain(bs, s->above_base);
- s->chain_frozen = false;
+ /* We should drop filter at this point, as filter hold the backing chain */
+ bdrv_cor_filter_drop(s->cor_filter_bs);
+ s->cor_filter_bs = NULL;
if (bdrv_cow_child(unfiltered_bs)) {
const char *base_id = NULL, *base_fmt = NULL;
- if (base) {
- base_id = s->backing_file_str;
- if (base->drv) {
- base_fmt = base->drv->format_name;
+ if (unfiltered_base) {
+ base_id = s->backing_file_str ?: unfiltered_base->filename;
+ if (unfiltered_base->drv) {
+ base_fmt = unfiltered_base->drv->format_name;
}
}
bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
@@ -94,13 +86,17 @@ static void stream_clean(Job *job)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
BlockJob *bjob = &s->common;
- BlockDriverState *bs = blk_bs(bjob->blk);
+
+ if (s->cor_filter_bs) {
+ bdrv_cor_filter_drop(s->cor_filter_bs);
+ s->cor_filter_bs = NULL;
+ }
/* Reopen the image back in read-only mode if necessary */
if (s->bs_read_only) {
/* Give up write permissions before making it read-only */
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
- bdrv_reopen_set_read_only(bs, true, NULL);
+ bdrv_reopen_set_read_only(s->target_bs, true, NULL);
}
g_free(s->backing_file_str);
@@ -110,9 +106,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
BlockBackend *blk = s->common.blk;
- BlockDriverState *bs = blk_bs(blk);
- BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
- bool enable_cor = !bdrv_cow_child(s->base_overlay);
+ BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
int64_t len;
int64_t offset = 0;
uint64_t delay_ns = 0;
@@ -124,21 +118,12 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
return 0;
}
- len = bdrv_getlength(bs);
+ len = bdrv_getlength(s->target_bs);
if (len < 0) {
return len;
}
job_progress_set_remaining(&s->common.job, len);
- /* Turn on copy-on-read for the whole block device so that guest read
- * requests help us make progress. Only do this when copying the entire
- * backing chain since the copy-on-read operation does not take base into
- * account.
- */
- if (enable_cor) {
- bdrv_enable_copy_on_read(bs);
- }
-
for ( ; offset < len; offset += n) {
bool copy;
int ret;
@@ -197,10 +182,6 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
}
}
- if (enable_cor) {
- bdrv_disable_copy_on_read(bs);
- }
-
/* Do not remove the backing file if an error was there but ignored. */
return error;
}
@@ -212,7 +193,6 @@ static const BlockJobDriver stream_job_driver = {
.free = block_job_free,
.run = stream_run,
.prepare = stream_prepare,
- .abort = stream_abort,
.clean = stream_clean,
.user_resume = block_job_user_resume,
},
@@ -220,59 +200,113 @@ static const BlockJobDriver stream_job_driver = {
void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, const char *backing_file_str,
+ BlockDriverState *bottom,
int creation_flags, int64_t speed,
- BlockdevOnError on_error, Error **errp)
+ BlockdevOnError on_error,
+ const char *filter_node_name,
+ Error **errp)
{
StreamBlockJob *s;
BlockDriverState *iter;
bool bs_read_only;
int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
- BlockDriverState *base_overlay = bdrv_find_overlay(bs, base);
+ BlockDriverState *base_overlay;
+ BlockDriverState *cor_filter_bs = NULL;
BlockDriverState *above_base;
-
- if (!base_overlay) {
- error_setg(errp, "'%s' is not in the backing chain of '%s'",
- base->node_name, bs->node_name);
- return;
- }
-
- /*
- * Find the node directly above @base. @base_overlay is a COW overlay, so
- * it must have a bdrv_cow_child(), but it is the immediate overlay of
- * @base, so between the two there can only be filters.
- */
- above_base = base_overlay;
- if (bdrv_cow_bs(above_base) != base) {
- above_base = bdrv_cow_bs(above_base);
- while (bdrv_filter_bs(above_base) != base) {
- above_base = bdrv_filter_bs(above_base);
+ QDict *opts;
+
+ assert(!(base && bottom));
+ assert(!(backing_file_str && bottom));
+
+ if (bottom) {
+ /*
+ * New simple interface. The code is written in terms of old interface
+ * with @base parameter (still, it doesn't freeze link to base, so in
+ * this mean old code is correct for new interface). So, for now, just
+ * emulate base_overlay and above_base. Still, when old interface
+ * finally removed, we should refactor code to use only "bottom", but
+ * not "*base*" things.
+ */
+ assert(!bottom->drv->is_filter);
+ base_overlay = above_base = bottom;
+ } else {
+ base_overlay = bdrv_find_overlay(bs, base);
+ if (!base_overlay) {
+ error_setg(errp, "'%s' is not in the backing chain of '%s'",
+ base->node_name, bs->node_name);
+ return;
}
- }
- if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) {
- return;
+ /*
+ * Find the node directly above @base. @base_overlay is a COW overlay,
+ * so it must have a bdrv_cow_child(), but it is the immediate overlay
+ * of @base, so between the two there can only be filters.
+ */
+ above_base = base_overlay;
+ if (bdrv_cow_bs(above_base) != base) {
+ above_base = bdrv_cow_bs(above_base);
+ while (bdrv_filter_bs(above_base) != base) {
+ above_base = bdrv_filter_bs(above_base);
+ }
+ }
}
/* Make sure that the image is opened in read-write mode */
bs_read_only = bdrv_is_read_only(bs);
if (bs_read_only) {
- if (bdrv_reopen_set_read_only(bs, false, errp) != 0) {
- bs_read_only = false;
- goto fail;
+ int ret;
+ /* Hold the chain during reopen */
+ if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) {
+ return;
+ }
+
+ ret = bdrv_reopen_set_read_only(bs, false, errp);
+
+ /* failure, or cor-filter will hold the chain */
+ bdrv_unfreeze_backing_chain(bs, above_base);
+
+ if (ret < 0) {
+ return;
}
}
- /* Prevent concurrent jobs trying to modify the graph structure here, we
- * already have our own plans. Also don't allow resize as the image size is
- * queried only at the job start and then cached. */
- s = block_job_create(job_id, &stream_job_driver, NULL, bs,
- basic_flags | BLK_PERM_GRAPH_MOD,
+ opts = qdict_new();
+
+ qdict_put_str(opts, "driver", "copy-on-read");
+ qdict_put_str(opts, "file", bdrv_get_node_name(bs));
+ /* Pass the base_overlay node name as 'bottom' to COR driver */
+ qdict_put_str(opts, "bottom", base_overlay->node_name);
+ if (filter_node_name) {
+ qdict_put_str(opts, "node-name", filter_node_name);
+ }
+
+ cor_filter_bs = bdrv_insert_node(bs, opts, BDRV_O_RDWR, errp);
+ if (!cor_filter_bs) {
+ goto fail;
+ }
+
+ if (!filter_node_name) {
+ cor_filter_bs->implicit = true;
+ }
+
+ s = block_job_create(job_id, &stream_job_driver, NULL, cor_filter_bs,
+ BLK_PERM_CONSISTENT_READ,
basic_flags | BLK_PERM_WRITE,
speed, creation_flags, NULL, NULL, errp);
if (!s) {
goto fail;
}
+ /*
+ * Prevent concurrent jobs trying to modify the graph structure here, we
+ * already have our own plans. Also don't allow resize as the image size is
+ * queried only at the job start and then cached.
+ */
+ if (block_job_add_bdrv(&s->common, "active node", bs, 0,
+ basic_flags | BLK_PERM_WRITE, &error_abort)) {
+ goto fail;
+ }
+
/* Block all intermediate nodes between bs and base, because they will
* disappear from the chain after this operation. The streaming job reads
* every block only once, assuming that it doesn't change, so forbid writes
@@ -293,8 +327,9 @@ void stream_start(const char *job_id, BlockDriverState *bs,
s->base_overlay = base_overlay;
s->above_base = above_base;
s->backing_file_str = g_strdup(backing_file_str);
+ s->cor_filter_bs = cor_filter_bs;
+ s->target_bs = bs;
s->bs_read_only = bs_read_only;
- s->chain_frozen = true;
s->on_error = on_error;
trace_stream_start(bs, base, s);
@@ -302,8 +337,10 @@ void stream_start(const char *job_id, BlockDriverState *bs,
return;
fail:
+ if (cor_filter_bs) {
+ bdrv_cor_filter_drop(cor_filter_bs);
+ }
if (bs_read_only) {
bdrv_reopen_set_read_only(bs, true, NULL);
}
- bdrv_unfreeze_backing_chain(bs, above_base);
}
diff --git a/block/vmdk.c b/block/vmdk.c
index a00dc00eb4..4499f136bd 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2928,7 +2928,7 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs,
int i;
BDRVVmdkState *s = bs->opaque;
ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1);
- ImageInfoList **next;
+ ImageInfoList **tail;
*spec_info = (ImageInfoSpecific){
.type = IMAGE_INFO_SPECIFIC_KIND_VMDK,
@@ -2943,12 +2943,9 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs,
.parent_cid = s->parent_cid,
};
- next = &spec_info->u.vmdk.data->extents;
+ tail = &spec_info->u.vmdk.data->extents;
for (i = 0; i < s->num_extents; i++) {
- *next = g_new0(ImageInfoList, 1);
- (*next)->value = vmdk_get_extent_info(&s->extents[i]);
- (*next)->next = NULL;
- next = &(*next)->next;
+ QAPI_LIST_APPEND(tail, vmdk_get_extent_info(&s->extents[i]));
}
return spec_info;
diff --git a/blockdev.c b/blockdev.c
index 2431448c5d..b250b9b959 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2500,19 +2500,39 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bool has_base, const char *base,
bool has_base_node, const char *base_node,
bool has_backing_file, const char *backing_file,
+ bool has_bottom, const char *bottom,
bool has_speed, int64_t speed,
bool has_on_error, BlockdevOnError on_error,
+ bool has_filter_node_name, const char *filter_node_name,
bool has_auto_finalize, bool auto_finalize,
bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
{
- BlockDriverState *bs, *iter;
+ BlockDriverState *bs, *iter, *iter_end;
BlockDriverState *base_bs = NULL;
+ BlockDriverState *bottom_bs = NULL;
AioContext *aio_context;
Error *local_err = NULL;
- const char *base_name = NULL;
int job_flags = JOB_DEFAULT;
+ if (has_base && has_base_node) {
+ error_setg(errp, "'base' and 'base-node' cannot be specified "
+ "at the same time");
+ return;
+ }
+
+ if (has_base && has_bottom) {
+ error_setg(errp, "'base' and 'bottom' cannot be specified "
+ "at the same time");
+ return;
+ }
+
+ if (has_bottom && has_base_node) {
+ error_setg(errp, "'bottom' and 'base-node' cannot be specified "
+ "at the same time");
+ return;
+ }
+
if (!has_on_error) {
on_error = BLOCKDEV_ON_ERROR_REPORT;
}
@@ -2525,12 +2545,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (has_base && has_base_node) {
- error_setg(errp, "'base' and 'base-node' cannot be specified "
- "at the same time");
- goto out;
- }
-
if (has_base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
@@ -2538,7 +2552,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
goto out;
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
- base_name = base;
}
if (has_base_node) {
@@ -2553,11 +2566,35 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
bdrv_refresh_filename(base_bs);
- base_name = base_bs->filename;
}
- /* Check for op blockers in the whole chain between bs and base */
- for (iter = bs; iter && iter != base_bs;
+ if (has_bottom) {
+ bottom_bs = bdrv_lookup_bs(NULL, bottom, errp);
+ if (!bottom_bs) {
+ goto out;
+ }
+ if (!bottom_bs->drv) {
+ error_setg(errp, "Node '%s' is not open", bottom);
+ goto out;
+ }
+ if (bottom_bs->drv->is_filter) {
+ error_setg(errp, "Node '%s' is a filter, use a non-filter node "
+ "as 'bottom'", bottom);
+ goto out;
+ }
+ if (!bdrv_chain_contains(bs, bottom_bs)) {
+ error_setg(errp, "Node '%s' is not in a chain starting from '%s'",
+ bottom, device);
+ goto out;
+ }
+ assert(bdrv_get_aio_context(bottom_bs) == aio_context);
+ }
+
+ /*
+ * Check for op blockers in the whole chain between bs and base (or bottom)
+ */
+ iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
+ for (iter = bs; iter && iter != iter_end;
iter = bdrv_filter_or_cow_bs(iter))
{
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
@@ -2573,9 +2610,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
goto out;
}
- /* backing_file string overrides base bs filename */
- base_name = has_backing_file ? backing_file : base_name;
-
if (has_auto_finalize && !auto_finalize) {
job_flags |= JOB_MANUAL_FINALIZE;
}
@@ -2583,8 +2617,9 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
job_flags |= JOB_MANUAL_DISMISS;
}
- stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
- job_flags, has_speed ? speed : 0, on_error, &local_err);
+ stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
+ bottom_bs, job_flags, has_speed ? speed : 0, on_error,
+ filter_node_name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
@@ -2794,6 +2829,7 @@ static BlockJob *do_backup_common(BackupCommon *backup,
{
BlockJob *job = NULL;
BdrvDirtyBitmap *bmap = NULL;
+ BackupPerf perf = { .max_workers = 64 };
int job_flags = JOB_DEFAULT;
if (!backup->has_speed) {
@@ -2818,6 +2854,18 @@ static BlockJob *do_backup_common(BackupCommon *backup,
backup->compress = false;
}
+ if (backup->x_perf) {
+ if (backup->x_perf->has_use_copy_range) {
+ perf.use_copy_range = backup->x_perf->use_copy_range;
+ }
+ if (backup->x_perf->has_max_workers) {
+ perf.max_workers = backup->x_perf->max_workers;
+ }
+ if (backup->x_perf->has_max_chunk) {
+ perf.max_chunk = backup->x_perf->max_chunk;
+ }
+ }
+
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
(backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
/* done before desugaring 'incremental' to print the right message */
@@ -2891,6 +2939,7 @@ static BlockJob *do_backup_common(BackupCommon *backup,
backup->sync, bmap, backup->bitmap_mode,
backup->compress,
backup->filter_node_name,
+ &perf,
backup->on_source_error,
backup->on_target_error,
job_flags, NULL, NULL, txn, errp);
@@ -3676,28 +3725,25 @@ void qmp_x_blockdev_change(const char *parent, bool has_child,
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
{
- BlockJobInfoList *head = NULL, **p_next = &head;
+ BlockJobInfoList *head = NULL, **tail = &head;
BlockJob *job;
for (job = block_job_next(NULL); job; job = block_job_next(job)) {
- BlockJobInfoList *elem;
+ BlockJobInfo *value;
AioContext *aio_context;
if (block_job_is_internal(job)) {
continue;
}
- elem = g_new0(BlockJobInfoList, 1);
aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context);
- elem->value = block_job_query(job, errp);
+ value = block_job_query(job, errp);
aio_context_release(aio_context);
- if (!elem->value) {
- g_free(elem);
+ if (!value) {
qapi_free_BlockJobInfoList(head);
return NULL;
}
- *p_next = elem;
- p_next = &elem->next;
+ QAPI_LIST_APPEND(tail, value);
}
return head;
diff --git a/blockjob.c b/blockjob.c
index 98ac8af982..db3a21699c 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -256,6 +256,7 @@ static bool job_timer_pending(Job *job)
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
+ const BlockJobDriver *drv = block_job_driver(job);
int64_t old_speed = job->speed;
if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
@@ -270,6 +271,11 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME);
job->speed = speed;
+
+ if (drv->set_speed) {
+ drv->set_speed(job, speed);
+ }
+
if (speed && speed <= old_speed) {
return;
}
diff --git a/configure b/configure
index dcc5ea7d63..87de49e2c2 100755
--- a/configure
+++ b/configure
@@ -2320,6 +2320,12 @@ if test -z "$want_tools"; then
fi
##########################################
+# Disable features only meaningful for system-mode emulation
+if test "$softmmu" = "no"; then
+ audio_drv_list=""
+fi
+
+##########################################
# Some versions of Mac OS X incorrectly define SIZE_MAX
cat > $TMPC << EOF
#include <stdint.h>
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 564caa1094..fe8f04ffb2 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -1885,7 +1885,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
{
QCryptoBlockLUKS *luks = block->opaque;
QCryptoBlockInfoLUKSSlot *slot;
- QCryptoBlockInfoLUKSSlotList *slots = NULL, **prev = &info->u.luks.slots;
+ QCryptoBlockInfoLUKSSlotList **tail = &info->u.luks.slots;
size_t i;
info->u.luks.cipher_alg = luks->cipher_alg;
@@ -1902,10 +1902,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
sizeof(luks->header.uuid));
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
- slots = g_new0(QCryptoBlockInfoLUKSSlotList, 1);
- *prev = slots;
-
- slots->value = slot = g_new0(QCryptoBlockInfoLUKSSlot, 1);
+ slot = g_new0(QCryptoBlockInfoLUKSSlot, 1);
slot->active = luks->header.key_slots[i].active ==
QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
slot->key_offset = luks->header.key_slots[i].key_offset_sector
@@ -1917,7 +1914,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
slot->stripes = luks->header.key_slots[i].stripes;
}
- prev = &slots->next;
+ QAPI_LIST_APPEND(tail, slot);
}
return 0;
diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst
index 0aa7a13bba..809af69725 100644
--- a/docs/devel/testing.rst
+++ b/docs/devel/testing.rst
@@ -111,7 +111,7 @@ check-block
-----------
``make check-block`` runs a subset of the block layer iotests (the tests that
-are in the "auto" group in ``tests/qemu-iotests/group``).
+are in the "auto" group).
See the "QEMU iotests" section below for more information.
GCC gcov support
@@ -224,6 +224,54 @@ another application on the host may have locked the file, possibly leading to a
test failure. If using such devices are explicitly desired, consider adding
``locking=off`` option to disable image locking.
+Test case groups
+----------------
+
+"Tests may belong to one or more test groups, which are defined in the form
+of a comment in the test source file. By convention, test groups are listed
+in the second line of the test file, after the "#!/..." line, like this:
+
+.. code::
+
+ #!/usr/bin/env python3
+ # group: auto quick
+ #
+ ...
+
+Another way of defining groups is creating the tests/qemu-iotests/group.local
+file. This should be used only for downstream (this file should never appear
+in upstream). This file may be used for defining some downstream test groups
+or for temporarily disabling tests, like this:
+
+.. code::
+
+ # groups for some company downstream process
+ #
+ # ci - tests to run on build
+ # down - our downstream tests, not for upstream
+ #
+ # Format of each line is:
+ # TEST_NAME TEST_GROUP [TEST_GROUP ]...
+
+ 013 ci
+ 210 disabled
+ 215 disabled
+ our-ugly-workaround-test down ci
+
+Note that the following group names have a special meaning:
+
+- quick: Tests in this group should finish within a few seconds.
+
+- auto: Tests in this group are used during "make check" and should be
+ runnable in any case. That means they should run with every QEMU binary
+ (also non-x86), with every QEMU configuration (i.e. must not fail if
+ an optional feature is not compiled in - but reporting a "skip" is ok),
+ work at least with the qcow2 file format, work with all kind of host
+ filesystems and users (e.g. "nobody" or "root") and must not take too
+ much memory and disk space (since CI pipelines tend to fail otherwise).
+
+- disabled: Tests in this group are disabled and ignored by check.
+
.. _docker-ref:
Docker based tests
@@ -871,6 +919,68 @@ qemu_bin
The exact QEMU binary to be used on QEMUMachine.
+Skipping tests
+--------------
+The Avocado framework provides Python decorators which allow for easily skip
+tests running under certain conditions. For example, on the lack of a binary
+on the test system or when the running environment is a CI system. For further
+information about those decorators, please refer to::
+
+ https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests
+
+While the conditions for skipping tests are often specifics of each one, there
+are recurring scenarios identified by the QEMU developers and the use of
+environment variables became a kind of standard way to enable/disable tests.
+
+Here is a list of the most used variables:
+
+AVOCADO_ALLOW_LARGE_STORAGE
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Tests which are going to fetch or produce assets considered *large* are not
+going to run unless that `AVOCADO_ALLOW_LARGE_STORAGE=1` is exported on
+the environment.
+
+The definition of *large* is a bit arbitrary here, but it usually means an
+asset which occupies at least 1GB of size on disk when uncompressed.
+
+AVOCADO_ALLOW_UNTRUSTED_CODE
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+There are tests which will boot a kernel image or firmware that can be
+considered not safe to run on the developer's workstation, thus they are
+skipped by default. The definition of *not safe* is also arbitrary but
+usually it means a blob which either its source or build process aren't
+public available.
+
+You should export `AVOCADO_ALLOW_UNTRUSTED_CODE=1` on the environment in
+order to allow tests which make use of those kind of assets.
+
+AVOCADO_TIMEOUT_EXPECTED
+~~~~~~~~~~~~~~~~~~~~~~~~
+The Avocado framework has a timeout mechanism which interrupts tests to avoid the
+test suite of getting stuck. The timeout value can be set via test parameter or
+property defined in the test class, for further details::
+
+ https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout
+
+Even though the timeout can be set by the test developer, there are some tests
+that may not have a well-defined limit of time to finish under certain
+conditions. For example, tests that take longer to execute when QEMU is
+compiled with debug flags. Therefore, the `AVOCADO_TIMEOUT_EXPECTED` variable
+has been used to determine whether those tests should run or not.
+
+GITLAB_CI
+~~~~~~~~~
+A number of tests are flagged to not run on the GitLab CI. Usually because
+they proved to the flaky or there are constraints on the CI environment which
+would make them fail. If you encounter a similar situation then use that
+variable as shown on the code snippet below to skip the test:
+
+.. code::
+
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
+ def test(self):
+ do_something()
+
Uninstalling Avocado
--------------------
diff --git a/dump/dump.c b/dump/dump.c
index dec32468d9..929138e91d 100644
--- a/dump/dump.c
+++ b/dump/dump.c
@@ -2030,39 +2030,29 @@ void qmp_dump_guest_memory(bool paging, const char *file,
DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp)
{
- DumpGuestMemoryFormatList *item;
DumpGuestMemoryCapability *cap =
g_malloc0(sizeof(DumpGuestMemoryCapability));
+ DumpGuestMemoryFormatList **tail = &cap->formats;
/* elf is always available */
- item = g_malloc0(sizeof(DumpGuestMemoryFormatList));
- cap->formats = item;
- item->value = DUMP_GUEST_MEMORY_FORMAT_ELF;
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_ELF);
/* kdump-zlib is always available */
- item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList));
- item = item->next;
- item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB;
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB);
/* add new item if kdump-lzo is available */
#ifdef CONFIG_LZO
- item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList));
- item = item->next;
- item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO;
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO);
#endif
/* add new item if kdump-snappy is available */
#ifdef CONFIG_SNAPPY
- item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList));
- item = item->next;
- item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY;
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY);
#endif
/* Windows dump is available only if target is x86_64 */
#ifdef TARGET_X86_64
- item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList));
- item = item->next;
- item->value = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP;
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_WIN_DMP);
#endif
return cap;
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index 6350caa765..e2317be546 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -44,14 +44,11 @@ static ACPIOSTInfo *acpi_cpu_device_status(int idx, AcpiCpuStatus *cdev)
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list)
{
+ ACPIOSTInfoList ***tail = list;
int i;
for (i = 0; i < cpu_st->dev_count; i++) {
- ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1);
- elem->value = acpi_cpu_device_status(i, &cpu_st->devs[i]);
- elem->next = NULL;
- **list = elem;
- *list = &elem->next;
+ QAPI_LIST_APPEND(*tail, acpi_cpu_device_status(i, &cpu_st->devs[i]));
}
}
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index f2552b2a46..0bdcf15528 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -53,14 +53,12 @@ static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev)
void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list)
{
+ ACPIOSTInfoList ***tail = list;
int i;
for (i = 0; i < mem_st->dev_count; i++) {
- ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1);
- elem->value = acpi_memory_device_status(i, &mem_st->devs[i]);
- elem->next = NULL;
- **list = elem;
- *list = &elem->next;
+ QAPI_LIST_APPEND(*tail,
+ acpi_memory_device_status(i, &mem_st->devs[i]));
}
}
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index 3675f8deaf..860787580a 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -168,7 +168,7 @@ static int xen_block_parse_request(XenBlockRequest *request)
};
if (request->req.operation != BLKIF_OP_READ &&
- blk_is_read_only(dataplane->blk)) {
+ !blk_is_writable(dataplane->blk)) {
error_report("error: write req for ro device");
goto err;
}
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 3636874432..292ea87805 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -444,7 +444,7 @@ static void fd_revalidate(FDrive *drv)
FLOPPY_DPRINTF("revalidate\n");
if (drv->blk != NULL) {
- drv->ro = blk_is_read_only(drv->blk);
+ drv->ro = !blk_is_writable(drv->blk);
if (!blk_is_inserted(drv->blk)) {
FLOPPY_DPRINTF("No disk in drive\n");
drv->disk = FLOPPY_DRIVE_TYPE_NONE;
@@ -479,8 +479,8 @@ static void fd_change_cb(void *opaque, bool load, Error **errp)
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
} else {
if (!blkconf_apply_backend_options(drive->conf,
- blk_is_read_only(drive->blk), false,
- errp)) {
+ !blk_supports_write_perm(drive->blk),
+ false, errp)) {
return;
}
}
@@ -553,7 +553,8 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
* read-only node later */
read_only = true;
} else {
- read_only = !blk_bs(dev->conf.blk) || blk_is_read_only(dev->conf.blk);
+ read_only = !blk_bs(dev->conf.blk) ||
+ !blk_supports_write_perm(dev->conf.blk);
}
if (!blkconf_blocksizes(&dev->conf, errp)) {
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index b744a58d1c..0412d3e7f4 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -508,7 +508,7 @@ static void flash_sync_page(Flash *s, int page)
{
QEMUIOVector *iov;
- if (!s->blk || blk_is_read_only(s->blk)) {
+ if (!s->blk || !blk_is_writable(s->blk)) {
return;
}
@@ -524,7 +524,7 @@ static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
{
QEMUIOVector *iov;
- if (!s->blk || blk_is_read_only(s->blk)) {
+ if (!s->blk || !blk_is_writable(s->blk)) {
return;
}
@@ -1434,7 +1434,7 @@ static void m25p80_realize(SSIPeripheral *ss, Error **errp)
if (s->blk) {
uint64_t perm = BLK_PERM_CONSISTENT_READ |
- (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
+ (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
if (ret < 0) {
return;
diff --git a/hw/block/nand.c b/hw/block/nand.c
index 913292ad1d..8bc80e3514 100644
--- a/hw/block/nand.c
+++ b/hw/block/nand.c
@@ -400,7 +400,7 @@ static void nand_realize(DeviceState *dev, Error **errp)
pagesize = 1 << s->oob_shift;
s->mem_oob = 1;
if (s->blk) {
- if (blk_is_read_only(s->blk)) {
+ if (!blk_supports_write_perm(s->blk)) {
error_setg(errp, "Can't use a read-only drive");
return;
}
diff --git a/hw/block/nvme-ns.c b/hw/block/nvme-ns.c
index 31c80cdf5b..2670787d26 100644
--- a/hw/block/nvme-ns.c
+++ b/hw/block/nvme-ns.c
@@ -48,13 +48,14 @@ static void nvme_ns_init(NvmeNamespace *ns)
static int nvme_ns_init_blk(NvmeCtrl *n, NvmeNamespace *ns, Error **errp)
{
+ bool read_only;
+
if (!blkconf_blocksizes(&ns->blkconf, errp)) {
return -1;
}
- if (!blkconf_apply_backend_options(&ns->blkconf,
- blk_is_read_only(ns->blkconf.blk),
- false, errp)) {
+ read_only = !blk_supports_write_perm(ns->blkconf.blk);
+ if (!blkconf_apply_backend_options(&ns->blkconf, read_only, false, errp)) {
return -1;
}
diff --git a/hw/block/onenand.c b/hw/block/onenand.c
index 579a73d7f7..afc0cd3a0f 100644
--- a/hw/block/onenand.c
+++ b/hw/block/onenand.c
@@ -797,7 +797,7 @@ static void onenand_realize(DeviceState *dev, Error **errp)
s->image = memset(g_malloc(size + (size >> 5)),
0xff, size + (size >> 5));
} else {
- if (blk_is_read_only(s->blk)) {
+ if (!blk_supports_write_perm(s->blk)) {
error_setg(errp, "Can't use a read-only drive");
return;
}
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index ccf326793d..22287a1522 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -745,7 +745,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
if (pfl->blk) {
uint64_t perm;
- pfl->ro = blk_is_read_only(pfl->blk);
+ pfl->ro = !blk_supports_write_perm(pfl->blk);
perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
if (ret < 0) {
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
index 2ad2f6baea..7962cff745 100644
--- a/hw/block/pflash_cfi02.c
+++ b/hw/block/pflash_cfi02.c
@@ -802,7 +802,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
if (pfl->blk) {
uint64_t perm;
- pfl->ro = blk_is_read_only(pfl->blk);
+ pfl->ro = !blk_supports_write_perm(pfl->blk);
perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
if (ret < 0) {
diff --git a/hw/block/swim.c b/hw/block/swim.c
index 20133a814c..509c2f4900 100644
--- a/hw/block/swim.c
+++ b/hw/block/swim.c
@@ -137,8 +137,8 @@ static void swim_change_cb(void *opaque, bool load, Error **errp)
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
} else {
if (!blkconf_apply_backend_options(drive->conf,
- blk_is_read_only(drive->blk), false,
- errp)) {
+ !blk_supports_write_perm(drive->blk),
+ false, errp)) {
return;
}
}
@@ -210,7 +210,7 @@ static void swim_drive_realize(DeviceState *qdev, Error **errp)
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
if (!blkconf_apply_backend_options(&dev->conf,
- blk_is_read_only(dev->conf.blk),
+ !blk_supports_write_perm(dev->conf.blk),
false, errp)) {
return;
}
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index bac2d6fa2b..e8600b069d 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1021,7 +1021,7 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
virtio_has_feature(features, VIRTIO_BLK_F_CONFIG_WCE))) {
virtio_add_feature(&features, VIRTIO_BLK_F_WCE);
}
- if (blk_is_read_only(s->blk)) {
+ if (!blk_is_writable(s->blk)) {
virtio_add_feature(&features, VIRTIO_BLK_F_RO);
}
if (s->conf.num_queues > 1) {
@@ -1175,8 +1175,8 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
}
if (!blkconf_apply_backend_options(&conf->conf,
- blk_is_read_only(conf->conf.blk), true,
- errp)) {
+ !blk_supports_write_perm(conf->conf.blk),
+ true, errp)) {
return;
}
s->original_wce = blk_enable_write_cache(conf->conf.blk);
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
index 718d886e5c..0e7d66c2a7 100644
--- a/hw/block/xen-block.c
+++ b/hw/block/xen-block.c
@@ -567,7 +567,7 @@ static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp)
return;
}
- blockdev->info = blk_is_read_only(conf->blk) ? VDISK_READONLY : 0;
+ blockdev->info = blk_supports_write_perm(conf->blk) ? 0 : VDISK_READONLY;
}
static void xen_disk_class_init(ObjectClass *class, void *data)
diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c
index affffe0c4a..44e979e503 100644
--- a/hw/core/machine-qmp-cmds.c
+++ b/hw/core/machine-qmp-cmds.c
@@ -28,11 +28,11 @@ CpuInfoList *qmp_query_cpus(Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
MachineClass *mc = MACHINE_GET_CLASS(ms);
- CpuInfoList *head = NULL, *cur_item = NULL;
+ CpuInfoList *head = NULL, **tail = &head;
CPUState *cpu;
CPU_FOREACH(cpu) {
- CpuInfoList *info;
+ CpuInfo *value;
#if defined(TARGET_I386)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
@@ -58,53 +58,46 @@ CpuInfoList *qmp_query_cpus(Error **errp)
cpu_synchronize_state(cpu);
- info = g_malloc0(sizeof(*info));
- info->value = g_malloc0(sizeof(*info->value));
- info->value->CPU = cpu->cpu_index;
- info->value->current = (cpu == first_cpu);
- info->value->halted = cpu->halted;
- info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
- info->value->thread_id = cpu->thread_id;
+ value = g_malloc0(sizeof(*value));
+ value->CPU = cpu->cpu_index;
+ value->current = (cpu == first_cpu);
+ value->halted = cpu->halted;
+ value->qom_path = object_get_canonical_path(OBJECT(cpu));
+ value->thread_id = cpu->thread_id;
#if defined(TARGET_I386)
- info->value->arch = CPU_INFO_ARCH_X86;
- info->value->u.x86.pc = env->eip + env->segs[R_CS].base;
+ value->arch = CPU_INFO_ARCH_X86;
+ value->u.x86.pc = env->eip + env->segs[R_CS].base;
#elif defined(TARGET_PPC)
- info->value->arch = CPU_INFO_ARCH_PPC;
- info->value->u.ppc.nip = env->nip;
+ value->arch = CPU_INFO_ARCH_PPC;
+ value->u.ppc.nip = env->nip;
#elif defined(TARGET_SPARC)
- info->value->arch = CPU_INFO_ARCH_SPARC;
- info->value->u.q_sparc.pc = env->pc;
- info->value->u.q_sparc.npc = env->npc;
+ value->arch = CPU_INFO_ARCH_SPARC;
+ value->u.q_sparc.pc = env->pc;
+ value->u.q_sparc.npc = env->npc;
#elif defined(TARGET_MIPS)
- info->value->arch = CPU_INFO_ARCH_MIPS;
- info->value->u.q_mips.PC = env->active_tc.PC;
+ value->arch = CPU_INFO_ARCH_MIPS;
+ value->u.q_mips.PC = env->active_tc.PC;
#elif defined(TARGET_TRICORE)
- info->value->arch = CPU_INFO_ARCH_TRICORE;
- info->value->u.tricore.PC = env->PC;
+ value->arch = CPU_INFO_ARCH_TRICORE;
+ value->u.tricore.PC = env->PC;
#elif defined(TARGET_S390X)
- info->value->arch = CPU_INFO_ARCH_S390;
- info->value->u.s390.cpu_state = env->cpu_state;
+ value->arch = CPU_INFO_ARCH_S390;
+ value->u.s390.cpu_state = env->cpu_state;
#elif defined(TARGET_RISCV)
- info->value->arch = CPU_INFO_ARCH_RISCV;
- info->value->u.riscv.pc = env->pc;
+ value->arch = CPU_INFO_ARCH_RISCV;
+ value->u.riscv.pc = env->pc;
#else
- info->value->arch = CPU_INFO_ARCH_OTHER;
+ value->arch = CPU_INFO_ARCH_OTHER;
#endif
- info->value->has_props = !!mc->cpu_index_to_instance_props;
- if (info->value->has_props) {
+ value->has_props = !!mc->cpu_index_to_instance_props;
+ if (value->has_props) {
CpuInstanceProperties *props;
props = g_malloc0(sizeof(*props));
*props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
- info->value->props = props;
+ value->props = props;
}
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ QAPI_LIST_APPEND(tail, value);
}
return head;
@@ -170,39 +163,33 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
MachineClass *mc = MACHINE_GET_CLASS(ms);
- CpuInfoFastList *head = NULL, *cur_item = NULL;
+ CpuInfoFastList *head = NULL, **tail = &head;
SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME,
-1, &error_abort);
CPUState *cpu;
CPU_FOREACH(cpu) {
- CpuInfoFastList *info = g_malloc0(sizeof(*info));
- info->value = g_malloc0(sizeof(*info->value));
+ CpuInfoFast *value = g_malloc0(sizeof(*value));
- info->value->cpu_index = cpu->cpu_index;
- info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
- info->value->thread_id = cpu->thread_id;
+ value->cpu_index = cpu->cpu_index;
+ value->qom_path = object_get_canonical_path(OBJECT(cpu));
+ value->thread_id = cpu->thread_id;
- info->value->has_props = !!mc->cpu_index_to_instance_props;
- if (info->value->has_props) {
+ value->has_props = !!mc->cpu_index_to_instance_props;
+ if (value->has_props) {
CpuInstanceProperties *props;
props = g_malloc0(sizeof(*props));
*props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
- info->value->props = props;
+ value->props = props;
}
- info->value->arch = sysemu_target_to_cpuinfo_arch(target);
- info->value->target = target;
+ value->arch = sysemu_target_to_cpuinfo_arch(target);
+ value->target = target;
if (target == SYS_EMU_TARGET_S390X) {
- cpustate_to_cpuinfo_s390(&info->value->u.s390x, cpu);
+ cpustate_to_cpuinfo_s390(&value->u.s390x, cpu);
}
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ QAPI_LIST_APPEND(tail, value);
}
return head;
@@ -293,41 +280,31 @@ void qmp_set_numa_node(NumaOptions *cmd, Error **errp)
static int query_memdev(Object *obj, void *opaque)
{
MemdevList **list = opaque;
- MemdevList *m = NULL;
+ Memdev *m;
QObject *host_nodes;
Visitor *v;
if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
m = g_malloc0(sizeof(*m));
- m->value = g_malloc0(sizeof(*m->value));
-
- m->value->id = g_strdup(object_get_canonical_path_component(obj));
- m->value->has_id = !!m->value->id;
-
- m->value->size = object_property_get_uint(obj, "size",
- &error_abort);
- m->value->merge = object_property_get_bool(obj, "merge",
- &error_abort);
- m->value->dump = object_property_get_bool(obj, "dump",
- &error_abort);
- m->value->prealloc = object_property_get_bool(obj,
- "prealloc",
- &error_abort);
- m->value->policy = object_property_get_enum(obj,
- "policy",
- "HostMemPolicy",
- &error_abort);
+ m->id = g_strdup(object_get_canonical_path_component(obj));
+ m->has_id = !!m->id;
+
+ m->size = object_property_get_uint(obj, "size", &error_abort);
+ m->merge = object_property_get_bool(obj, "merge", &error_abort);
+ m->dump = object_property_get_bool(obj, "dump", &error_abort);
+ m->prealloc = object_property_get_bool(obj, "prealloc", &error_abort);
+ m->policy = object_property_get_enum(obj, "policy", "HostMemPolicy",
+ &error_abort);
host_nodes = object_property_get_qobject(obj,
"host-nodes",
&error_abort);
v = qobject_input_visitor_new(host_nodes);
- visit_type_uint16List(v, NULL, &m->value->host_nodes, &error_abort);
+ visit_type_uint16List(v, NULL, &m->host_nodes, &error_abort);
visit_free(v);
qobject_unref(host_nodes);
- m->next = *list;
- *list = m;
+ QAPI_LIST_PREPEND(*list, m);
}
return 0;
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index bef0d7d69a..e2969a6c81 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -29,7 +29,6 @@
#include "qemu/log.h"
#include "hw/loader.h"
#include "trace.h"
-#include "ui/vnc.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
@@ -220,7 +219,7 @@ enum {
/* These values can probably be changed arbitrarily. */
#define SVGA_SCRATCH_SIZE 0x8000
-#define SVGA_MAX_WIDTH ROUND_UP(2360, VNC_DIRTY_PIXELS_PER_BIT)
+#define SVGA_MAX_WIDTH 2368
#define SVGA_MAX_HEIGHT 1770
#ifdef VERBOSE
diff --git a/hw/ide/core.c b/hw/ide/core.c
index b49e4cfbc6..81db2c95de 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2537,7 +2537,7 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
error_setg(errp, "Device needs media, but drive is empty");
return -1;
}
- if (blk_is_read_only(blk)) {
+ if (!blk_is_writable(blk)) {
error_setg(errp, "Can't use a read-only drive");
return -1;
}
diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c
index cf0627fd01..d9f8301711 100644
--- a/hw/mem/memory-device.c
+++ b/hw/mem/memory-device.c
@@ -199,7 +199,7 @@ out:
MemoryDeviceInfoList *qmp_memory_device_list(void)
{
GSList *devices = NULL, *item;
- MemoryDeviceInfoList *list = NULL, *prev = NULL;
+ MemoryDeviceInfoList *list = NULL, **tail = &list;
object_child_foreach(qdev_get_machine(), memory_device_build_list,
&devices);
@@ -207,19 +207,11 @@ MemoryDeviceInfoList *qmp_memory_device_list(void)
for (item = devices; item; item = g_slist_next(item)) {
const MemoryDeviceState *md = MEMORY_DEVICE(item->data);
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data);
- MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1);
MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1);
mdc->fill_device_info(md, info);
- elem->value = info;
- elem->next = NULL;
- if (prev) {
- prev->next = elem;
- } else {
- list = elem;
- }
- prev = elem;
+ QAPI_LIST_APPEND(tail, info);
}
g_slist_free(devices);
diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c
index f921c67644..b8e8b9eebe 100644
--- a/hw/misc/sifive_u_otp.c
+++ b/hw/misc/sifive_u_otp.c
@@ -228,7 +228,7 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp)
if (s->blk) {
perm = BLK_PERM_CONSISTENT_READ |
- (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
+ (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
if (ret < 0) {
return;
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index a6b0c5602e..512e9042ff 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1683,41 +1683,34 @@ static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num);
static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev)
{
- PciMemoryRegionList *head = NULL, *cur_item = NULL;
+ PciMemoryRegionList *head = NULL, **tail = &head;
int i;
for (i = 0; i < PCI_NUM_REGIONS; i++) {
const PCIIORegion *r = &dev->io_regions[i];
- PciMemoryRegionList *region;
+ PciMemoryRegion *region;
if (!r->size) {
continue;
}
region = g_malloc0(sizeof(*region));
- region->value = g_malloc0(sizeof(*region->value));
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
- region->value->type = g_strdup("io");
+ region->type = g_strdup("io");
} else {
- region->value->type = g_strdup("memory");
- region->value->has_prefetch = true;
- region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH);
- region->value->has_mem_type_64 = true;
- region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64);
+ region->type = g_strdup("memory");
+ region->has_prefetch = true;
+ region->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH);
+ region->has_mem_type_64 = true;
+ region->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64);
}
- region->value->bar = i;
- region->value->address = r->addr;
- region->value->size = r->size;
+ region->bar = i;
+ region->address = r->addr;
+ region->size = r->size;
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = region;
- } else {
- cur_item->next = region;
- cur_item = region;
- }
+ QAPI_LIST_APPEND(tail, region);
}
return head;
@@ -1814,23 +1807,14 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus,
static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num)
{
- PciDeviceInfoList *info, *head = NULL, *cur_item = NULL;
+ PciDeviceInfoList *head = NULL, **tail = &head;
PCIDevice *dev;
int devfn;
for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
dev = bus->devices[devfn];
if (dev) {
- info = g_malloc0(sizeof(*info));
- info->value = qmp_query_pci_device(dev, bus, bus_num);
-
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ QAPI_LIST_APPEND(tail, qmp_query_pci_device(dev, bus, bus_num));
}
}
@@ -1853,21 +1837,13 @@ static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num)
PciInfoList *qmp_query_pci(Error **errp)
{
- PciInfoList *info, *head = NULL, *cur_item = NULL;
+ PciInfoList *head = NULL, **tail = &head;
PCIHostState *host_bridge;
QLIST_FOREACH(host_bridge, &pci_host_bridges, next) {
- info = g_malloc0(sizeof(*info));
- info->value = qmp_query_pci_bus(host_bridge->bus,
- pci_bus_num(host_bridge->bus));
-
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ QAPI_LIST_APPEND(tail,
+ qmp_query_pci_bus(host_bridge->bus,
+ pci_bus_num(host_bridge->bus)));
}
return head;
diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c
index ef8dff03e0..4b455de1ea 100644
--- a/hw/ppc/pnv_pnor.c
+++ b/hw/ppc/pnv_pnor.c
@@ -86,7 +86,7 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp)
if (s->blk) {
uint64_t perm = BLK_PERM_CONSISTENT_READ |
- (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
+ (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
if (ret < 0) {
return;
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 29504ea081..ed52fcd49f 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1270,7 +1270,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
if (s->qdev.type == TYPE_DISK) {
dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
- if (blk_is_read_only(s->qdev.conf.blk)) {
+ if (!blk_is_writable(s->qdev.conf.blk)) {
dev_specific_param |= 0x80; /* Readonly. */
}
} else {
@@ -1704,7 +1704,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
goto invalid_param_len;
}
- if (blk_is_read_only(s->qdev.conf.blk)) {
+ if (!blk_is_writable(s->qdev.conf.blk)) {
block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return;
@@ -1795,7 +1795,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
return;
}
- if (blk_is_read_only(s->qdev.conf.blk)) {
+ if (!blk_is_writable(s->qdev.conf.blk)) {
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return;
}
@@ -2207,7 +2207,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
- if (blk_is_read_only(s->qdev.conf.blk)) {
+ if (!blk_is_writable(s->qdev.conf.blk)) {
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return 0;
}
@@ -2380,7 +2380,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
}
}
- read_only = blk_is_read_only(s->qdev.conf.blk);
+ read_only = !blk_supports_write_perm(s->qdev.conf.blk);
if (dev->type == TYPE_ROM) {
read_only = true;
}
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 9740f7e36a..ab220141f5 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -306,7 +306,7 @@ static void scsi_read_complete(void * opaque, int ret)
* readonly.
*/
if ((s->type == TYPE_DISK || s->type == TYPE_TAPE || s->type == TYPE_ZBC) &&
- blk_is_read_only(s->conf.blk) &&
+ !blk_is_writable(s->conf.blk) &&
(r->req.cmd.buf[0] == MODE_SENSE ||
r->req.cmd.buf[0] == MODE_SENSE_10) &&
(r->req.cmd.buf[1] & 0x8) == 0) {
@@ -694,7 +694,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
return;
}
if (!blkconf_apply_backend_options(&s->conf,
- blk_is_read_only(s->conf.blk),
+ !blk_supports_write_perm(s->conf.blk),
true, errp)) {
return;
}
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index b3952514fe..8517dbce8b 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -567,7 +567,7 @@ static void sd_reset(DeviceState *dev)
sd_set_sdstatus(sd);
g_free(sd->wp_groups);
- sd->wp_switch = sd->blk ? blk_is_read_only(sd->blk) : false;
+ sd->wp_switch = sd->blk ? !blk_is_writable(sd->blk) : false;
sd->wpgrps_size = sect;
sd->wp_groups = bitmap_new(sd->wpgrps_size);
memset(sd->function_group, 0, sizeof(sd->function_group));
@@ -735,7 +735,7 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
{
sd->readonly_cb = readonly;
sd->inserted_cb = insert;
- qemu_set_irq(readonly, sd->blk ? blk_is_read_only(sd->blk) : 0);
+ qemu_set_irq(readonly, sd->blk ? !blk_is_writable(sd->blk) : 0);
qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0);
}
@@ -2131,7 +2131,7 @@ static void sd_realize(DeviceState *dev, Error **errp)
if (sd->blk) {
int64_t blk_size;
- if (blk_is_read_only(sd->blk)) {
+ if (!blk_supports_write_perm(sd->blk)) {
error_setg(errp, "Cannot use read-only drive as SD card");
return;
}
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index f0f005869d..c49e8b819e 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -613,8 +613,8 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
return;
}
- if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true,
- errp)) {
+ if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk),
+ true, errp)) {
return;
}
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
index aac85e1488..338f2ea7fd 100644
--- a/include/block/block-copy.h
+++ b/include/block/block-copy.h
@@ -18,19 +18,15 @@
#include "block/block.h"
#include "qemu/co-shared-resource.h"
-typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
+typedef void (*BlockCopyAsyncCallbackFunc)(void *opaque);
typedef struct BlockCopyState BlockCopyState;
+typedef struct BlockCopyCallState BlockCopyCallState;
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
- int64_t cluster_size,
+ int64_t cluster_size, bool use_copy_range,
BdrvRequestFlags write_flags,
Error **errp);
-void block_copy_set_progress_callback(
- BlockCopyState *s,
- ProgressBytesCallbackFunc progress_bytes_callback,
- void *progress_opaque);
-
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm);
void block_copy_state_free(BlockCopyState *s);
@@ -39,7 +35,56 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
int64_t offset, int64_t *count);
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
- bool *error_is_read);
+ bool ignore_ratelimit);
+
+/*
+ * Run block-copy in a coroutine, create corresponding BlockCopyCallState
+ * object and return pointer to it. Never returns NULL.
+ *
+ * Caller is responsible to call block_copy_call_free() to free
+ * BlockCopyCallState object.
+ *
+ * @max_workers means maximum of parallel coroutines to execute sub-requests,
+ * must be > 0.
+ *
+ * @max_chunk means maximum length for one IO operation. Zero means unlimited.
+ */
+BlockCopyCallState *block_copy_async(BlockCopyState *s,
+ int64_t offset, int64_t bytes,
+ int max_workers, int64_t max_chunk,
+ BlockCopyAsyncCallbackFunc cb,
+ void *cb_opaque);
+
+/*
+ * Free finished BlockCopyCallState. Trying to free running
+ * block-copy will crash.
+ */
+void block_copy_call_free(BlockCopyCallState *call_state);
+
+/*
+ * Note, that block-copy call is marked finished prior to calling
+ * the callback.
+ */
+bool block_copy_call_finished(BlockCopyCallState *call_state);
+bool block_copy_call_succeeded(BlockCopyCallState *call_state);
+bool block_copy_call_failed(BlockCopyCallState *call_state);
+bool block_copy_call_cancelled(BlockCopyCallState *call_state);
+int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read);
+
+void block_copy_set_speed(BlockCopyState *s, uint64_t speed);
+void block_copy_kick(BlockCopyCallState *call_state);
+
+/*
+ * Cancel running block-copy call.
+ *
+ * Cancel leaves block-copy state valid: dirty bits are correct and you may use
+ * cancel + <run block_copy with same parameters> to emulate pause/resume.
+ *
+ * Note also, that the cancel is async: it only marks block-copy call to be
+ * cancelled. So, the call may be cancelled (block_copy_call_cancelled() reports
+ * true) but not yet finished (block_copy_call_finished() reports false).
+ */
+void block_copy_call_cancel(BlockCopyCallState *call_state);
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s);
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip);
diff --git a/include/block/block.h b/include/block/block.h
index a193545b6a..81fcaad5ac 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -72,9 +72,11 @@ typedef enum {
BDRV_REQ_NO_FALLBACK = 0x100,
/*
- * BDRV_REQ_PREFETCH may be used only together with BDRV_REQ_COPY_ON_READ
- * on read request and means that caller doesn't really need data to be
- * written to qiov parameter which may be NULL.
+ * BDRV_REQ_PREFETCH makes sense only in the context of copy-on-read
+ * (i.e., together with the BDRV_REQ_COPY_ON_READ flag or when a COR
+ * filter is involved), in which case it signals that the COR operation
+ * need not read the data into memory (qiov) but only ensure they are
+ * copied to the top layer (i.e., that COR operation is done).
*/
BDRV_REQ_PREFETCH = 0x200,
@@ -358,6 +360,8 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
Error **errp);
void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp);
+BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
+ int flags, Error **errp);
int bdrv_parse_aio(const char *mode, int *flags);
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index b9ef61fe4d..d01fc23720 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -881,6 +881,10 @@ struct BlockDriverState {
/* I/O Limits */
BlockLimits bl;
+ /*
+ * Flags honored during pread
+ */
+ unsigned int supported_read_flags;
/* Flags honored during pwrite (so far: BDRV_REQ_FUA,
* BDRV_REQ_WRITE_UNCHANGED).
* If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those
@@ -1143,6 +1147,9 @@ int is_windows_drive(const char *filename);
* See @BlockJobCreateFlags
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @on_error: The action to take upon error.
+ * @filter_node_name: The node name that should be assigned to the filter
+ * driver that the stream job inserts into the graph above
+ * @bs. NULL means that a node name should be autogenerated.
* @errp: Error object.
*
* Start a streaming operation on @bs. Clusters that are unallocated
@@ -1154,8 +1161,11 @@ int is_windows_drive(const char *filename);
*/
void stream_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *base, const char *backing_file_str,
+ BlockDriverState *bottom,
int creation_flags, int64_t speed,
- BlockdevOnError on_error, Error **errp);
+ BlockdevOnError on_error,
+ const char *filter_node_name,
+ Error **errp);
/**
* commit_start:
@@ -1256,6 +1266,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
* @sync_mode: What parts of the disk image should be copied to the destination.
* @sync_bitmap: The dirty bitmap if sync_mode is 'bitmap' or 'incremental'
* @bitmap_mode: The bitmap synchronization policy to use.
+ * @perf: Performance options. All actual fields assumed to be present,
+ * all ".has_*" fields are ignored.
* @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target.
* @creation_flags: Flags that control the behavior of the Job lifetime.
@@ -1274,6 +1286,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BitmapSyncMode bitmap_mode,
bool compress,
const char *filter_node_name,
+ BackupPerf *perf,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
index e2824a36a8..6633d83da2 100644
--- a/include/block/blockjob_int.h
+++ b/include/block/blockjob_int.h
@@ -52,6 +52,8 @@ struct BlockJobDriver {
* besides job->blk to the new AioContext.
*/
void (*attached_aio_context)(BlockJob *job, AioContext *new_context);
+
+ void (*set_speed)(BlockJob *job, int64_t speed);
};
/**
diff --git a/include/qapi/util.h b/include/qapi/util.h
index 6178e98e97..d7bfb30e25 100644
--- a/include/qapi/util.h
+++ b/include/qapi/util.h
@@ -37,4 +37,17 @@ int parse_qapi_name(const char *name, bool complete);
(list) = _tmp; \
} while (0)
+/*
+ * For any pointer to a GenericList @tail (usually the 'next' member of a
+ * list element), insert @element at the back and update the tail.
+ *
+ * Note that this macro evaluates @element exactly once, so it is safe
+ * to have side-effects with that argument.
+ */
+#define QAPI_LIST_APPEND(tail, element) do { \
+ *(tail) = g_malloc0(sizeof(**(tail))); \
+ (*(tail))->value = (element); \
+ (tail) = &(*(tail))->next; \
+} while (0)
+
#endif
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 8203d7f6f9..880e903293 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -191,7 +191,8 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
int error);
void blk_error_action(BlockBackend *blk, BlockErrorAction action,
bool is_read, int error);
-bool blk_is_read_only(BlockBackend *blk);
+bool blk_supports_write_perm(BlockBackend *blk);
+bool blk_is_writable(BlockBackend *blk);
bool blk_is_sg(BlockBackend *blk);
bool blk_enable_write_cache(BlockBackend *blk);
void blk_set_enable_write_cache(BlockBackend *blk, bool wce);
diff --git a/iothread.c b/iothread.c
index 69eff9efbc..b9f2751382 100644
--- a/iothread.c
+++ b/iothread.c
@@ -1,7 +1,7 @@
/*
* Event loop thread
*
- * Copyright Red Hat Inc., 2013
+ * Copyright Red Hat Inc., 2013, 2020
*
* Authors:
* Stefan Hajnoczi <stefanha@redhat.com>
@@ -310,8 +310,7 @@ AioContext *iothread_get_aio_context(IOThread *iothread)
static int query_one_iothread(Object *object, void *opaque)
{
- IOThreadInfoList ***prev = opaque;
- IOThreadInfoList *elem;
+ IOThreadInfoList ***tail = opaque;
IOThreadInfo *info;
IOThread *iothread;
@@ -327,12 +326,7 @@ static int query_one_iothread(Object *object, void *opaque)
info->poll_grow = iothread->poll_grow;
info->poll_shrink = iothread->poll_shrink;
- elem = g_new0(IOThreadInfoList, 1);
- elem->value = info;
- elem->next = NULL;
-
- **prev = elem;
- *prev = &elem->next;
+ QAPI_LIST_APPEND(*tail, info);
return 0;
}
diff --git a/job-qmp.c b/job-qmp.c
index 645601b2cc..34c4da094f 100644
--- a/job-qmp.c
+++ b/job-qmp.c
@@ -164,28 +164,25 @@ static JobInfo *job_query_single(Job *job, Error **errp)
JobInfoList *qmp_query_jobs(Error **errp)
{
- JobInfoList *head = NULL, **p_next = &head;
+ JobInfoList *head = NULL, **tail = &head;
Job *job;
for (job = job_next(NULL); job; job = job_next(job)) {
- JobInfoList *elem;
+ JobInfo *value;
AioContext *aio_context;
if (job_is_internal(job)) {
continue;
}
- elem = g_new0(JobInfoList, 1);
aio_context = job->aio_context;
aio_context_acquire(aio_context);
- elem->value = job_query_single(job, errp);
+ value = job_query_single(job, errp);
aio_context_release(aio_context);
- if (!elem->value) {
- g_free(elem);
+ if (!value) {
qapi_free_JobInfoList(head);
return NULL;
}
- *p_next = elem;
- p_next = &elem->next;
+ QAPI_LIST_APPEND(tail, value);
}
return head;
diff --git a/job.c b/job.c
index 8fecf38960..3aaaebafe2 100644
--- a/job.c
+++ b/job.c
@@ -553,6 +553,9 @@ static bool job_timer_not_pending(Job *job)
void job_pause(Job *job)
{
job->pause_count++;
+ if (!job->paused) {
+ job_enter(job);
+ }
}
void job_resume(Job *job)
diff --git a/meson.build b/meson.build
index 35a9eddf5c..a58c6f6785 100644
--- a/meson.build
+++ b/meson.build
@@ -1460,6 +1460,7 @@ if capstone_opt == 'internal'
]
libcapstone = static_library('capstone',
+ build_by_default: false,
sources: capstone_files,
c_args: capstone_cargs,
include_directories: 'capstone/include')
@@ -1537,6 +1538,7 @@ if have_system
slirp_inc = include_directories('slirp', 'slirp/src')
libslirp = static_library('slirp',
+ build_by_default: false,
sources: slirp_files,
c_args: slirp_cargs,
include_directories: slirp_inc)
@@ -1582,6 +1584,7 @@ if have_system
fdt_inc = include_directories('dtc/libfdt')
libfdt = static_library('fdt',
+ build_by_default: false,
sources: fdt_files,
include_directories: fdt_inc)
fdt = declare_dependency(link_with: libfdt,
diff --git a/migration/migration.c b/migration/migration.c
index d5136419bf..1986cb8573 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -793,29 +793,21 @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value)
MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
{
- MigrationCapabilityStatusList *head = NULL;
- MigrationCapabilityStatusList *caps;
+ MigrationCapabilityStatusList *head = NULL, **tail = &head;
+ MigrationCapabilityStatus *caps;
MigrationState *s = migrate_get_current();
int i;
- caps = NULL; /* silence compiler warning */
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
#ifndef CONFIG_LIVE_BLOCK_MIGRATION
if (i == MIGRATION_CAPABILITY_BLOCK) {
continue;
}
#endif
- if (head == NULL) {
- head = g_malloc0(sizeof(*caps));
- caps = head;
- } else {
- caps->next = g_malloc0(sizeof(*caps));
- caps = caps->next;
- }
- caps->value =
- g_malloc(sizeof(*caps->value));
- caps->value->capability = i;
- caps->value->state = s->enabled_capabilities[i];
+ caps = g_malloc0(sizeof(*caps));
+ caps->capability = i;
+ caps->state = s->enabled_capabilities[i];
+ QAPI_LIST_APPEND(tail, caps);
}
return head;
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 499647a578..a48bc1e904 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -76,20 +76,20 @@ void hmp_handle_error(Monitor *mon, Error *err)
static strList *strList_from_comma_list(const char *in)
{
strList *res = NULL;
- strList **hook = &res;
+ strList **tail = &res;
while (in && in[0]) {
char *comma = strchr(in, ',');
- *hook = g_new0(strList, 1);
+ char *value;
if (comma) {
- (*hook)->value = g_strndup(in, comma - in);
+ value = g_strndup(in, comma - in);
in = comma + 1; /* skip the , */
} else {
- (*hook)->value = g_strdup(in);
+ value = g_strdup(in);
in = NULL;
}
- hook = &(*hook)->next;
+ QAPI_LIST_APPEND(tail, value);
}
return res;
@@ -1706,7 +1706,8 @@ void hmp_closefd(Monitor *mon, const QDict *qdict)
void hmp_sendkey(Monitor *mon, const QDict *qdict)
{
const char *keys = qdict_get_str(qdict, "keys");
- KeyValueList *keylist, *head = NULL, *tmp = NULL;
+ KeyValue *v = NULL;
+ KeyValueList *head = NULL, **tail = &head;
int has_hold_time = qdict_haskey(qdict, "hold-time");
int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
Error *err = NULL;
@@ -1723,16 +1724,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
keyname_len = 4;
}
- keylist = g_malloc0(sizeof(*keylist));
- keylist->value = g_malloc0(sizeof(*keylist->value));
-
- if (!head) {
- head = keylist;
- }
- if (tmp) {
- tmp->next = keylist;
- }
- tmp = keylist;
+ v = g_malloc0(sizeof(*v));
if (strstart(keys, "0x", NULL)) {
char *endp;
@@ -1741,16 +1733,18 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
if (endp != keys + keyname_len) {
goto err_out;
}
- keylist->value->type = KEY_VALUE_KIND_NUMBER;
- keylist->value->u.number.data = value;
+ v->type = KEY_VALUE_KIND_NUMBER;
+ v->u.number.data = value;
} else {
int idx = index_from_key(keys, keyname_len);
if (idx == Q_KEY_CODE__MAX) {
goto err_out;
}
- keylist->value->type = KEY_VALUE_KIND_QCODE;
- keylist->value->u.qcode.data = idx;
+ v->type = KEY_VALUE_KIND_QCODE;
+ v->u.qcode.data = idx;
}
+ QAPI_LIST_APPEND(tail, v);
+ v = NULL;
if (!*separator) {
break;
@@ -1762,6 +1756,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, err);
out:
+ qapi_free_KeyValue(v);
qapi_free_KeyValueList(head);
return;
diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c
index 17514f4959..509ae870bd 100644
--- a/monitor/qmp-cmds-control.c
+++ b/monitor/qmp-cmds-control.c
@@ -104,17 +104,16 @@ VersionInfo *qmp_query_version(Error **errp)
static void query_commands_cb(const QmpCommand *cmd, void *opaque)
{
- CommandInfoList *info, **list = opaque;
+ CommandInfo *info;
+ CommandInfoList **list = opaque;
if (!cmd->enabled) {
return;
}
info = g_malloc0(sizeof(*info));
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = g_strdup(cmd->name);
- info->next = *list;
- *list = info;
+ info->name = g_strdup(cmd->name);
+ QAPI_LIST_PREPEND(*list, info);
}
CommandInfoList *qmp_query_commands(Error **errp)
diff --git a/net/net.c b/net/net.c
index c1cd9c75f6..fb7b7dcc25 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1213,10 +1213,9 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
Error **errp)
{
NetClientState *nc;
- RxFilterInfoList *filter_list = NULL, *last_entry = NULL;
+ RxFilterInfoList *filter_list = NULL, **tail = &filter_list;
QTAILQ_FOREACH(nc, &net_clients, next) {
- RxFilterInfoList *entry;
RxFilterInfo *info;
if (has_name && strcmp(nc->name, name) != 0) {
@@ -1227,6 +1226,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
if (has_name) {
error_setg(errp, "net client(%s) isn't a NIC", name);
+ assert(!filter_list);
return NULL;
}
continue;
@@ -1240,18 +1240,11 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
if (nc->info->query_rx_filter) {
info = nc->info->query_rx_filter(nc);
- entry = g_malloc0(sizeof(*entry));
- entry->value = info;
-
- if (!filter_list) {
- filter_list = entry;
- } else {
- last_entry->next = entry;
- }
- last_entry = entry;
+ QAPI_LIST_APPEND(tail, info);
} else if (has_name) {
error_setg(errp, "net client(%s) doesn't support"
" rx-filter querying", name);
+ assert(!filter_list);
return NULL;
}
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 3484986d1c..9f555d5c1d 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1372,6 +1372,30 @@
'data': { 'node': 'str', 'overlay': 'str' } }
##
+# @BackupPerf:
+#
+# Optional parameters for backup. These parameters don't affect
+# functionality, but may significantly affect performance.
+#
+# @use-copy-range: Use copy offloading. Default false.
+#
+# @max-workers: Maximum number of parallel requests for the sustained background
+# copying process. Doesn't influence copy-before-write operations.
+# Default 64.
+#
+# @max-chunk: Maximum request length for the sustained background copying
+# process. Doesn't influence copy-before-write operations.
+# 0 means unlimited. If max-chunk is non-zero then it should not be
+# less than job cluster size which is calculated as maximum of
+# target image cluster size and 64k. Default 0.
+#
+# Since: 6.0
+##
+{ 'struct': 'BackupPerf',
+ 'data': { '*use-copy-range': 'bool',
+ '*max-workers': 'int', '*max-chunk': 'int64' } }
+
+##
# @BackupCommon:
#
# @job-id: identifier for the newly-created block job. If
@@ -1426,6 +1450,8 @@
# above node specified by @drive. If this option is not given,
# a node name is autogenerated. (Since: 4.2)
#
+# @x-perf: Performance options. (Since 6.0)
+#
# Note: @on-source-error and @on-target-error only affect background
# I/O. If an error occurs during a guest write request, the device's
# rerror/werror actions will be used.
@@ -1440,7 +1466,7 @@
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool',
- '*filter-node-name': 'str' } }
+ '*filter-node-name': 'str', '*x-perf': 'BackupPerf' } }
##
# @DriveBackup:
@@ -2517,10 +2543,14 @@
# @device: the device or node name of the top image
#
# @base: the common backing file name.
-# It cannot be set if @base-node is also set.
+# It cannot be set if @base-node or @bottom is also set.
#
# @base-node: the node name of the backing file.
-# It cannot be set if @base is also set. (Since 2.8)
+# It cannot be set if @base or @bottom is also set. (Since 2.8)
+#
+# @bottom: the last node in the chain that should be streamed into
+# top. It cannot be set if @base or @base-node is also set.
+# It cannot be filter node. (Since 6.0)
#
# @backing-file: The backing file string to write into the top
# image. This filename is not validated.
@@ -2543,6 +2573,11 @@
# 'stop' and 'enospc' can only be used if the block device
# supports io-status (see BlockInfo). Since 1.3.
#
+# @filter-node-name: the node name that should be assigned to the
+# filter driver that the stream job inserts into the graph
+# above @device. If this option is not given, a node name is
+# autogenerated. (Since: 6.0)
+#
# @auto-finalize: When false, this job will wait in a PENDING state after it has
# finished its work, waiting for @block-job-finalize before
# making any block graph changes.
@@ -2571,8 +2606,9 @@
##
{ 'command': 'block-stream',
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
- '*base-node': 'str', '*backing-file': 'str', '*speed': 'int',
- '*on-error': 'BlockdevOnError',
+ '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str',
+ '*speed': 'int', '*on-error': 'BlockdevOnError',
+ '*filter-node-name': 'str',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
##
@@ -3953,6 +3989,24 @@
'data': { 'throttle-group': 'str',
'file' : 'BlockdevRef'
} }
+
+##
+# @BlockdevOptionsCor:
+#
+# Driver specific block device options for the copy-on-read driver.
+#
+# @bottom: The name of a non-filter node (allocation-bearing layer) that
+# limits the COR operations in the backing chain (inclusive), so
+# that no data below this node will be copied by this filter.
+# If option is absent, the limit is not applied, so that data
+# from all backing layers may be copied.
+#
+# Since: 6.0
+##
+{ 'struct': 'BlockdevOptionsCor',
+ 'base': 'BlockdevOptionsGenericFormat',
+ 'data': { '*bottom': 'str' } }
+
##
# @BlockdevOptions:
#
@@ -4005,7 +4059,7 @@
'bochs': 'BlockdevOptionsGenericFormat',
'cloop': 'BlockdevOptionsGenericFormat',
'compress': 'BlockdevOptionsGenericFormat',
- 'copy-on-read':'BlockdevOptionsGenericFormat',
+ 'copy-on-read':'BlockdevOptionsCor',
'dmg': 'BlockdevOptionsGenericFormat',
'file': 'BlockdevOptionsFile',
'ftp': 'BlockdevOptionsCurlFtp',
diff --git a/qemu-img.c b/qemu-img.c
index 8597d069af..e2952fe955 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -2856,7 +2856,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts,
bool chain, bool force_share)
{
ImageInfoList *head = NULL;
- ImageInfoList **last = &head;
+ ImageInfoList **tail = &head;
GHashTable *filenames;
Error *err = NULL;
@@ -2866,7 +2866,6 @@ static ImageInfoList *collect_image_info_list(bool image_opts,
BlockBackend *blk;
BlockDriverState *bs;
ImageInfo *info;
- ImageInfoList *elem;
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
error_report("Backing file '%s' creates an infinite loop.",
@@ -2890,10 +2889,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts,
goto err;
}
- elem = g_new0(ImageInfoList, 1);
- elem->value = info;
- *last = elem;
- last = &elem->next;
+ QAPI_LIST_APPEND(tail, info);
blk_unref(blk);
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index edf785b2da..8dd94a3314 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -2474,18 +2474,17 @@ static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
{
int64_t current;
- GuestLogicalProcessorList *head, **link;
+ GuestLogicalProcessorList *head, **tail;
long sc_max;
Error *local_err = NULL;
current = 0;
head = NULL;
- link = &head;
+ tail = &head;
sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err);
while (local_err == NULL && current < sc_max) {
GuestLogicalProcessor *vcpu;
- GuestLogicalProcessorList *entry;
int64_t id = current++;
char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
id);
@@ -2495,10 +2494,7 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
vcpu->logical_id = id;
vcpu->has_can_offline = true; /* lolspeak ftw */
transfer_vcpu(vcpu, true, path, &local_err);
- entry = g_malloc0(sizeof *entry);
- entry->value = vcpu;
- *link = entry;
- link = &entry->next;
+ QAPI_LIST_APPEND(tail, vcpu);
}
g_free(path);
}
@@ -2831,13 +2827,13 @@ out1:
GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
{
- GuestMemoryBlockList *head, **link;
+ GuestMemoryBlockList *head, **tail;
Error *local_err = NULL;
struct dirent *de;
DIR *dp;
head = NULL;
- link = &head;
+ tail = &head;
dp = opendir("/sys/devices/system/memory/");
if (!dp) {
@@ -2859,7 +2855,6 @@ GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
*/
while ((de = readdir(dp)) != NULL) {
GuestMemoryBlock *mem_blk;
- GuestMemoryBlockList *entry;
if ((strncmp(de->d_name, "memory", 6) != 0) ||
!(de->d_type & DT_DIR)) {
@@ -2875,11 +2870,7 @@ GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
break;
}
- entry = g_malloc0(sizeof *entry);
- entry->value = mem_blk;
-
- *link = entry;
- link = &entry->next;
+ QAPI_LIST_APPEND(tail, mem_blk);
}
closedir(dp);
@@ -2899,15 +2890,14 @@ GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
GuestMemoryBlockResponseList *
qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
{
- GuestMemoryBlockResponseList *head, **link;
+ GuestMemoryBlockResponseList *head, **tail;
Error *local_err = NULL;
head = NULL;
- link = &head;
+ tail = &head;
while (mem_blks != NULL) {
GuestMemoryBlockResponse *result;
- GuestMemoryBlockResponseList *entry;
GuestMemoryBlock *current_mem_blk = mem_blks->value;
result = g_malloc0(sizeof(*result));
@@ -2916,11 +2906,8 @@ qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
if (local_err) { /* should never happen */
goto err;
}
- entry = g_malloc0(sizeof *entry);
- entry->value = result;
- *link = entry;
- link = &entry->next;
+ QAPI_LIST_APPEND(tail, result);
mem_blks = mem_blks->next;
}
@@ -3151,11 +3138,10 @@ static double ga_get_login_time(struct utmpx *user_info)
GuestUserList *qmp_guest_get_users(Error **errp)
{
GHashTable *cache = NULL;
- GuestUserList *head = NULL, *cur_item = NULL;
+ GuestUserList *head = NULL, **tail = &head;
struct utmpx *user_info = NULL;
gpointer value = NULL;
GuestUser *user = NULL;
- GuestUserList *item = NULL;
double login_time = 0;
cache = g_hash_table_new(g_str_hash, g_str_equal);
@@ -3178,19 +3164,13 @@ GuestUserList *qmp_guest_get_users(Error **errp)
continue;
}
- item = g_new0(GuestUserList, 1);
- item->value = g_new0(GuestUser, 1);
- item->value->user = g_strdup(user_info->ut_user);
- item->value->login_time = ga_get_login_time(user_info);
+ user = g_new0(GuestUser, 1);
+ user->user = g_strdup(user_info->ut_user);
+ user->login_time = ga_get_login_time(user_info);
- g_hash_table_insert(cache, item->value->user, item->value);
+ g_hash_table_insert(cache, user->user, user);
- if (!cur_item) {
- head = cur_item = item;
- } else {
- cur_item->next = item;
- cur_item = item;
- }
+ QAPI_LIST_APPEND(tail, user);
}
endutxent();
g_hash_table_destroy(cache);
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 684639bd13..a00e6cb165 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1624,11 +1624,11 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
{
IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
- GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
- GuestIpAddressList *head_addr, *cur_addr;
- GuestNetworkInterfaceList *info;
+ GuestNetworkInterfaceList *head = NULL, **tail = &head;
+ GuestIpAddressList *head_addr, **tail_addr;
+ GuestNetworkInterface *info;
GuestNetworkInterfaceStat *interface_stat = NULL;
- GuestIpAddressList *address_item = NULL;
+ GuestIpAddress *address_item = NULL;
unsigned char *mac_addr;
char *addr_str;
WORD wsa_version;
@@ -1651,30 +1651,24 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
for (addr = adptr_addrs; addr; addr = addr->Next) {
info = g_malloc0(sizeof(*info));
- if (cur_item == NULL) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ QAPI_LIST_APPEND(tail, info);
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = guest_wctomb_dup(addr->FriendlyName);
+ info->name = guest_wctomb_dup(addr->FriendlyName);
if (addr->PhysicalAddressLength != 0) {
mac_addr = addr->PhysicalAddress;
- info->value->hardware_address =
+ info->hardware_address =
g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
(int) mac_addr[0], (int) mac_addr[1],
(int) mac_addr[2], (int) mac_addr[3],
(int) mac_addr[4], (int) mac_addr[5]);
- info->value->has_hardware_address = true;
+ info->has_hardware_address = true;
}
head_addr = NULL;
- cur_addr = NULL;
+ tail_addr = &head_addr;
for (ip_addr = addr->FirstUnicastAddress;
ip_addr;
ip_addr = ip_addr->Next) {
@@ -1685,37 +1679,29 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
address_item = g_malloc0(sizeof(*address_item));
- if (!cur_addr) {
- head_addr = cur_addr = address_item;
- } else {
- cur_addr->next = address_item;
- cur_addr = address_item;
- }
+ QAPI_LIST_APPEND(tail_addr, address_item);
- address_item->value = g_malloc0(sizeof(*address_item->value));
- address_item->value->ip_address = addr_str;
- address_item->value->prefix = guest_ip_prefix(ip_addr);
+ address_item->ip_address = addr_str;
+ address_item->prefix = guest_ip_prefix(ip_addr);
if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
- address_item->value->ip_address_type =
- GUEST_IP_ADDRESS_TYPE_IPV4;
+ address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
} else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
- address_item->value->ip_address_type =
- GUEST_IP_ADDRESS_TYPE_IPV6;
+ address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
}
}
if (head_addr) {
- info->value->has_ip_addresses = true;
- info->value->ip_addresses = head_addr;
+ info->has_ip_addresses = true;
+ info->ip_addresses = head_addr;
}
- if (!info->value->has_statistics) {
+ if (!info->has_statistics) {
interface_stat = g_malloc0(sizeof(*interface_stat));
if (guest_get_network_stats(addr->AdapterName,
interface_stat) == -1) {
- info->value->has_statistics = false;
+ info->has_statistics = false;
g_free(interface_stat);
} else {
- info->value->statistics = interface_stat;
- info->value->has_statistics = true;
+ info->statistics = interface_stat;
+ info->has_statistics = true;
}
}
}
@@ -1833,7 +1819,7 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
{
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pslpi, ptr;
DWORD length;
- GuestLogicalProcessorList *head, **link;
+ GuestLogicalProcessorList *head, **tail;
Error *local_err = NULL;
int64_t current;
@@ -1841,7 +1827,7 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
length = 0;
current = 0;
head = NULL;
- link = &head;
+ tail = &head;
if ((GetLogicalProcessorInformation(pslpi, &length) == FALSE) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
@@ -1864,18 +1850,13 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
while (cpu_bits > 0) {
if (!!(cpu_bits & 1)) {
GuestLogicalProcessor *vcpu;
- GuestLogicalProcessorList *entry;
vcpu = g_malloc0(sizeof *vcpu);
vcpu->logical_id = current++;
vcpu->online = true;
vcpu->has_can_offline = true;
- entry = g_malloc0(sizeof *entry);
- entry->value = vcpu;
-
- *link = entry;
- link = &entry->next;
+ QAPI_LIST_APPEND(tail, vcpu);
}
cpu_bits >>= 1;
}
@@ -2087,12 +2068,11 @@ GuestUserList *qmp_guest_get_users(Error **errp)
#define QGA_NANOSECONDS 10000000
GHashTable *cache = NULL;
- GuestUserList *head = NULL, *cur_item = NULL;
+ GuestUserList *head = NULL, **tail = &head;
DWORD buffer_size = 0, count = 0, i = 0;
GA_WTSINFOA *info = NULL;
WTS_SESSION_INFOA *entries = NULL;
- GuestUserList *item = NULL;
GuestUser *user = NULL;
gpointer value = NULL;
INT64 login = 0;
@@ -2128,23 +2108,17 @@ GuestUserList *qmp_guest_get_users(Error **errp)
user->login_time = login_time;
}
} else {
- item = g_new0(GuestUserList, 1);
- item->value = g_new0(GuestUser, 1);
+ user = g_new0(GuestUser, 1);
- item->value->user = g_strdup(info->UserName);
- item->value->domain = g_strdup(info->Domain);
- item->value->has_domain = true;
+ user->user = g_strdup(info->UserName);
+ user->domain = g_strdup(info->Domain);
+ user->has_domain = true;
- item->value->login_time = login_time;
+ user->login_time = login_time;
- g_hash_table_add(cache, item->value->user);
+ g_hash_table_add(cache, user->user);
- if (!cur_item) {
- head = cur_item = item;
- } else {
- cur_item->next = item;
- cur_item = item;
- }
+ QAPI_LIST_APPEND(tail, user);
}
}
WTSFreeMemory(info);
@@ -2429,7 +2403,7 @@ static GStrv ga_get_hardware_ids(DEVINST devInstance)
GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
{
- GuestDeviceInfoList *head = NULL, *cur_item = NULL, *item = NULL;
+ GuestDeviceInfoList *head = NULL, **tail = &head;
HDEVINFO dev_info = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA dev_info_data;
int i, j;
@@ -2527,14 +2501,7 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
slog("driver: %s\ndriver version: %" PRId64 ",%s\n",
device->driver_name, device->driver_date,
device->driver_version);
- item = g_new0(GuestDeviceInfoList, 1);
- item->value = g_steal_pointer(&device);
- if (!cur_item) {
- head = cur_item = item;
- } else {
- cur_item->next = item;
- cur_item = item;
- }
+ QAPI_LIST_APPEND(tail, g_steal_pointer(&device));
}
if (dev_info != INVALID_HANDLE_VALUE) {
diff --git a/scripts/simplebench/bench-backup.py b/scripts/simplebench/bench-backup.py
new file mode 100755
index 0000000000..33a1ecfefa
--- /dev/null
+++ b/scripts/simplebench/bench-backup.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+#
+# Bench backup block-job
+#
+# Copyright (c) 2020 Virtuozzo International GmbH.
+#
+# 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/>.
+#
+
+import argparse
+import json
+
+import simplebench
+from results_to_text import results_to_text
+from bench_block_job import bench_block_copy, drv_file, drv_nbd
+
+
+def bench_func(env, case):
+ """ Handle one "cell" of benchmarking table. """
+ cmd_options = env['cmd-options'] if 'cmd-options' in env else {}
+ return bench_block_copy(env['qemu-binary'], env['cmd'],
+ cmd_options,
+ case['source'], case['target'])
+
+
+def bench(args):
+ test_cases = []
+
+ sources = {}
+ targets = {}
+ for d in args.dir:
+ label, path = d.split(':') # paths with colon not supported
+ sources[label] = drv_file(path + '/test-source')
+ targets[label] = drv_file(path + '/test-target')
+
+ if args.nbd:
+ nbd = args.nbd.split(':')
+ host = nbd[0]
+ port = '10809' if len(nbd) == 1 else nbd[1]
+ drv = drv_nbd(host, port)
+ sources['nbd'] = drv
+ targets['nbd'] = drv
+
+ for t in args.test:
+ src, dst = t.split(':')
+
+ test_cases.append({
+ 'id': t,
+ 'source': sources[src],
+ 'target': targets[dst]
+ })
+
+ binaries = [] # list of (<label>, <path>, [<options>])
+ for i, q in enumerate(args.env):
+ name_path = q.split(':')
+ if len(name_path) == 1:
+ label = f'q{i}'
+ path_opts = name_path[0].split(',')
+ else:
+ assert len(name_path) == 2 # paths with colon not supported
+ label = name_path[0]
+ path_opts = name_path[1].split(',')
+
+ binaries.append((label, path_opts[0], path_opts[1:]))
+
+ test_envs = []
+
+ bin_paths = {}
+ for i, q in enumerate(args.env):
+ opts = q.split(',')
+ label_path = opts[0]
+ opts = opts[1:]
+
+ if ':' in label_path:
+ # path with colon inside is not supported
+ label, path = label_path.split(':')
+ bin_paths[label] = path
+ elif label_path in bin_paths:
+ label = label_path
+ path = bin_paths[label]
+ else:
+ path = label_path
+ label = f'q{i}'
+ bin_paths[label] = path
+
+ x_perf = {}
+ is_mirror = False
+ for opt in opts:
+ if opt == 'mirror':
+ is_mirror = True
+ elif opt == 'copy-range=on':
+ x_perf['use-copy-range'] = True
+ elif opt == 'copy-range=off':
+ x_perf['use-copy-range'] = False
+ elif opt.startswith('max-workers='):
+ x_perf['max-workers'] = int(opt.split('=')[1])
+
+ if is_mirror:
+ assert not x_perf
+ test_envs.append({
+ 'id': f'mirror({label})',
+ 'cmd': 'blockdev-mirror',
+ 'qemu-binary': path
+ })
+ else:
+ test_envs.append({
+ 'id': f'backup({label})\n' + '\n'.join(opts),
+ 'cmd': 'blockdev-backup',
+ 'cmd-options': {'x-perf': x_perf} if x_perf else {},
+ 'qemu-binary': path
+ })
+
+ result = simplebench.bench(bench_func, test_envs, test_cases, count=3)
+ with open('results.json', 'w') as f:
+ json.dump(result, f, indent=4)
+ print(results_to_text(result))
+
+
+class ExtendAction(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ items = getattr(namespace, self.dest) or []
+ items.extend(values)
+ setattr(namespace, self.dest, items)
+
+
+if __name__ == '__main__':
+ p = argparse.ArgumentParser('Backup benchmark', epilog='''
+ENV format
+
+ (LABEL:PATH|LABEL|PATH)[,max-workers=N][,use-copy-range=(on|off)][,mirror]
+
+ LABEL short name for the binary
+ PATH path to the binary
+ max-workers set x-perf.max-workers of backup job
+ use-copy-range set x-perf.use-copy-range of backup job
+ mirror use mirror job instead of backup''',
+ formatter_class=argparse.RawTextHelpFormatter)
+ p.add_argument('--env', nargs='+', help='''\
+Qemu binaries with labels and options, see below
+"ENV format" section''',
+ action=ExtendAction)
+ p.add_argument('--dir', nargs='+', help='''\
+Directories, each containing "test-source" and/or
+"test-target" files, raw images to used in
+benchmarking. File path with label, like
+label:/path/to/directory''',
+ action=ExtendAction)
+ p.add_argument('--nbd', help='''\
+host:port for remote NBD image, (or just host, for
+default port 10809). Use it in tests, label is "nbd"
+(but you cannot create test nbd:nbd).''')
+ p.add_argument('--test', nargs='+', help='''\
+Tests, in form source-dir-label:target-dir-label''',
+ action=ExtendAction)
+
+ bench(p.parse_args())
diff --git a/scripts/simplebench/bench-example.py b/scripts/simplebench/bench-example.py
index d9c7f7bc17..4864435f39 100644
--- a/scripts/simplebench/bench-example.py
+++ b/scripts/simplebench/bench-example.py
@@ -25,7 +25,7 @@ from bench_block_job import bench_block_copy, drv_file, drv_nbd
def bench_func(env, case):
""" Handle one "cell" of benchmarking table. """
- return bench_block_copy(env['qemu_binary'], env['cmd'],
+ return bench_block_copy(env['qemu_binary'], env['cmd'], {}
case['source'], case['target'])
diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py
index 9808d696cf..7332845c1c 100755
--- a/scripts/simplebench/bench_block_job.py
+++ b/scripts/simplebench/bench_block_job.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Benchmark block jobs
#
@@ -78,16 +78,19 @@ def bench_block_job(cmd, cmd_args, qemu_args):
# Bench backup or mirror
-def bench_block_copy(qemu_binary, cmd, source, target):
+def bench_block_copy(qemu_binary, cmd, cmd_options, source, target):
"""Helper to run bench_block_job() for mirror or backup"""
assert cmd in ('blockdev-backup', 'blockdev-mirror')
source['node-name'] = 'source'
target['node-name'] = 'target'
- return bench_block_job(cmd,
- {'job-id': 'job0', 'device': 'source',
- 'target': 'target', 'sync': 'full'},
+ cmd_options['job-id'] = 'job0'
+ cmd_options['device'] = 'source'
+ cmd_options['target'] = 'target'
+ cmd_options['sync'] = 'full'
+
+ return bench_block_job(cmd, cmd_options,
[qemu_binary,
'-blockdev', json.dumps(source),
'-blockdev', json.dumps(target)])
diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c
index 32b9287e68..2098d7e759 100644
--- a/scsi/pr-manager.c
+++ b/scsi/pr-manager.c
@@ -116,8 +116,7 @@ pr_manager_register_types(void)
static int query_one_pr_manager(Object *object, void *opaque)
{
- PRManagerInfoList ***prev = opaque;
- PRManagerInfoList *elem;
+ PRManagerInfoList ***tail = opaque;
PRManagerInfo *info;
PRManager *pr_mgr;
@@ -126,15 +125,10 @@ static int query_one_pr_manager(Object *object, void *opaque)
return 0;
}
- elem = g_new0(PRManagerInfoList, 1);
info = g_new0(PRManagerInfo, 1);
info->id = g_strdup(object_get_canonical_path_component(object));
info->connected = pr_manager_is_connected(pr_mgr);
- elem->value = info;
- elem->next = NULL;
-
- **prev = elem;
- *prev = &elem->next;
+ QAPI_LIST_APPEND(*tail, info);
return 0;
}
diff --git a/softmmu/tpm.c b/softmmu/tpm.c
index cab206355a..578563f05a 100644
--- a/softmmu/tpm.c
+++ b/softmmu/tpm.c
@@ -196,22 +196,14 @@ int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
TPMInfoList *qmp_query_tpm(Error **errp)
{
TPMBackend *drv;
- TPMInfoList *info, *head = NULL, *cur_item = NULL;
+ TPMInfoList *head = NULL, **tail = &head;
QLIST_FOREACH(drv, &tpm_backends, list) {
if (!drv->tpmif) {
continue;
}
- info = g_new0(TPMInfoList, 1);
- info->value = tpm_backend_query_tpm(drv);
-
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
+ QAPI_LIST_APPEND(tail, tpm_backend_query_tpm(drv));
}
return head;
@@ -220,44 +212,26 @@ TPMInfoList *qmp_query_tpm(Error **errp)
TpmTypeList *qmp_query_tpm_types(Error **errp)
{
unsigned int i = 0;
- TpmTypeList *head = NULL, *prev = NULL, *cur_item;
+ TpmTypeList *head = NULL, **tail = &head;
for (i = 0; i < TPM_TYPE__MAX; i++) {
if (!tpm_be_find_by_type(i)) {
continue;
}
- cur_item = g_new0(TpmTypeList, 1);
- cur_item->value = i;
-
- if (prev) {
- prev->next = cur_item;
- }
- if (!head) {
- head = cur_item;
- }
- prev = cur_item;
+ QAPI_LIST_APPEND(tail, i);
}
return head;
}
TpmModelList *qmp_query_tpm_models(Error **errp)
{
- TpmModelList *head = NULL, *prev = NULL, *cur_item;
+ TpmModelList *head = NULL, **tail = &head;
GSList *e, *l = object_class_get_list(TYPE_TPM_IF, false);
for (e = l; e; e = e->next) {
TPMIfClass *c = TPM_IF_CLASS(e->data);
- cur_item = g_new0(TpmModelList, 1);
- cur_item->value = c->model;
-
- if (prev) {
- prev->next = cur_item;
- }
- if (!head) {
- head = cur_item;
- }
- prev = cur_item;
+ QAPI_LIST_APPEND(tail, c->model);
}
g_slist_free(l);
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 72a79e6019..ae89024d36 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -4818,20 +4818,17 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose);
/* Build a list with the name of all features on a feature word array */
static void x86_cpu_list_feature_names(FeatureWordArray features,
- strList **feat_names)
+ strList **list)
{
+ strList **tail = list;
FeatureWord w;
- strList **next = feat_names;
for (w = 0; w < FEATURE_WORDS; w++) {
uint64_t filtered = features[w];
int i;
for (i = 0; i < 64; i++) {
if (filtered & (1ULL << i)) {
- strList *new = g_new0(strList, 1);
- new->value = g_strdup(x86_cpu_feature_name(w, i));
- *next = new;
- next = &new->next;
+ QAPI_LIST_APPEND(tail, g_strdup(x86_cpu_feature_name(w, i)));
}
}
}
@@ -4852,16 +4849,14 @@ static void x86_cpu_get_unavailable_features(Object *obj, Visitor *v,
* running using the current machine and accelerator.
*/
static void x86_cpu_class_check_missing_features(X86CPUClass *xcc,
- strList **missing_feats)
+ strList **list)
{
+ strList **tail = list;
X86CPU *xc;
Error *err = NULL;
- strList **next = missing_feats;
if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) {
- strList *new = g_new0(strList, 1);
- new->value = g_strdup("kvm");
- *missing_feats = new;
+ QAPI_LIST_APPEND(tail, g_strdup("kvm"));
return;
}
@@ -4873,16 +4868,13 @@ static void x86_cpu_class_check_missing_features(X86CPUClass *xcc,
* but in case it does, just report the model as not
* runnable at all using the "type" property.
*/
- strList *new = g_new0(strList, 1);
- new->value = g_strdup("type");
- *next = new;
- next = &new->next;
+ QAPI_LIST_APPEND(tail, g_strdup("type"));
error_free(err);
}
x86_cpu_filter_features(xc, false);
- x86_cpu_list_feature_names(xc->filtered_features, next);
+ x86_cpu_list_feature_names(xc->filtered_features, tail);
object_unref(OBJECT(xc));
}
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
index c8e754cfb1..d5e8bfe11f 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -98,6 +98,38 @@ static bool mb_cpu_has_work(CPUState *cs)
}
#ifndef CONFIG_USER_ONLY
+static void mb_cpu_ns_axi_dp(void *opaque, int irq, int level)
+{
+ MicroBlazeCPU *cpu = opaque;
+ bool en = cpu->cfg.use_non_secure & USE_NON_SECURE_M_AXI_DP_MASK;
+
+ cpu->ns_axi_dp = level & en;
+}
+
+static void mb_cpu_ns_axi_ip(void *opaque, int irq, int level)
+{
+ MicroBlazeCPU *cpu = opaque;
+ bool en = cpu->cfg.use_non_secure & USE_NON_SECURE_M_AXI_IP_MASK;
+
+ cpu->ns_axi_ip = level & en;
+}
+
+static void mb_cpu_ns_axi_dc(void *opaque, int irq, int level)
+{
+ MicroBlazeCPU *cpu = opaque;
+ bool en = cpu->cfg.use_non_secure & USE_NON_SECURE_M_AXI_DC_MASK;
+
+ cpu->ns_axi_dc = level & en;
+}
+
+static void mb_cpu_ns_axi_ic(void *opaque, int irq, int level)
+{
+ MicroBlazeCPU *cpu = opaque;
+ bool en = cpu->cfg.use_non_secure & USE_NON_SECURE_M_AXI_IC_MASK;
+
+ cpu->ns_axi_ic = level & en;
+}
+
static void microblaze_cpu_set_irq(void *opaque, int irq, int level)
{
MicroBlazeCPU *cpu = opaque;
@@ -248,6 +280,10 @@ static void mb_cpu_initfn(Object *obj)
#ifndef CONFIG_USER_ONLY
/* Inbound IRQ and FIR lines */
qdev_init_gpio_in(DEVICE(cpu), microblaze_cpu_set_irq, 2);
+ qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_dp, "ns_axi_dp", 1);
+ qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_ip, "ns_axi_ip", 1);
+ qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_dc, "ns_axi_dc", 1);
+ qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_ic, "ns_axi_ic", 1);
#endif
}
@@ -277,6 +313,16 @@ static Property mb_properties[] = {
DEFINE_PROP_BOOL("use-msr-instr", MicroBlazeCPU, cfg.use_msr_instr, true),
DEFINE_PROP_BOOL("use-pcmp-instr", MicroBlazeCPU, cfg.use_pcmp_instr, true),
DEFINE_PROP_BOOL("use-mmu", MicroBlazeCPU, cfg.use_mmu, true),
+ /*
+ * use-non-secure enables/disables the use of the non_secure[3:0] signals.
+ * It is a bitfield where 1 = non-secure for the following bits and their
+ * corresponding interfaces:
+ * 0x1 - M_AXI_DP
+ * 0x2 - M_AXI_IP
+ * 0x4 - M_AXI_DC
+ * 0x8 - M_AXI_IC
+ */
+ DEFINE_PROP_UINT8("use-non-secure", MicroBlazeCPU, cfg.use_non_secure, 0),
DEFINE_PROP_BOOL("dcache-writeback", MicroBlazeCPU, cfg.dcache_writeback,
false),
DEFINE_PROP_BOOL("endianness", MicroBlazeCPU, cfg.endi, false),
@@ -329,7 +375,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data)
cc->tlb_fill = mb_cpu_tlb_fill;
#ifndef CONFIG_USER_ONLY
cc->do_transaction_failed = mb_cpu_transaction_failed;
- cc->get_phys_page_debug = mb_cpu_get_phys_page_debug;
+ cc->get_phys_page_attrs_debug = mb_cpu_get_phys_page_attrs_debug;
dc->vmsd = &vmstate_mb_cpu;
#endif
device_class_set_props(dc, mb_properties);
diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h
index c1c264199f..e4bba8a755 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -233,6 +233,12 @@ typedef struct CPUMBState CPUMBState;
#define TARGET_INSN_START_EXTRA_WORDS 1
+/* use-non-secure property masks */
+#define USE_NON_SECURE_M_AXI_DP_MASK 0x1
+#define USE_NON_SECURE_M_AXI_IP_MASK 0x2
+#define USE_NON_SECURE_M_AXI_DC_MASK 0x4
+#define USE_NON_SECURE_M_AXI_IC_MASK 0x8
+
struct CPUMBState {
uint32_t bvalue; /* TCG temporary, only valid during a TB */
uint32_t btarget; /* Full resolved branch destination */
@@ -316,6 +322,7 @@ typedef struct {
bool use_msr_instr;
bool use_pcmp_instr;
bool use_mmu;
+ uint8_t use_non_secure;
bool dcache_writeback;
bool endi;
bool dopb_bus_exception;
@@ -337,6 +344,10 @@ struct MicroBlazeCPU {
CPUState parent_obj;
/*< public >*/
+ bool ns_axi_dp;
+ bool ns_axi_ip;
+ bool ns_axi_dc;
+ bool ns_axi_ic;
CPUNegativeOffsetState neg;
CPUMBState env;
@@ -350,7 +361,8 @@ void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr);
void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
-hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr,
+ MemTxAttrs *attrs);
int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c
index cda14a14be..20dbd67313 100644
--- a/target/microblaze/helper.c
+++ b/target/microblaze/helper.c
@@ -46,6 +46,16 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
#else /* !CONFIG_USER_ONLY */
+static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu,
+ MMUAccessType access_type)
+{
+ if (access_type == MMU_INST_FETCH) {
+ return !cpu->ns_axi_ip;
+ } else {
+ return !cpu->ns_axi_dp;
+ }
+}
+
bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr)
@@ -55,12 +65,16 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MicroBlazeMMULookup lu;
unsigned int hit;
int prot;
+ MemTxAttrs attrs = {};
+
+ attrs.secure = mb_cpu_access_is_secure(cpu, access_type);
if (mmu_idx == MMU_NOMMU_IDX) {
/* MMU disabled or not available. */
address &= TARGET_PAGE_MASK;
prot = PAGE_BITS;
- tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
+ tlb_set_page_with_attrs(cs, address, address, attrs, prot, mmu_idx,
+ TARGET_PAGE_SIZE);
return true;
}
@@ -71,7 +85,8 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n",
mmu_idx, vaddr, paddr, lu.prot);
- tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE);
+ tlb_set_page_with_attrs(cs, vaddr, paddr, attrs, lu.prot, mmu_idx,
+ TARGET_PAGE_SIZE);
return true;
}
@@ -230,7 +245,8 @@ void mb_cpu_do_interrupt(CPUState *cs)
}
}
-hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
+ MemTxAttrs *attrs)
{
MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
CPUMBState *env = &cpu->env;
@@ -239,6 +255,10 @@ hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
int mmu_idx = cpu_mmu_index(env, false);
unsigned int hit;
+ /* Caller doesn't initialize */
+ *attrs = (MemTxAttrs) {};
+ attrs->secure = mb_cpu_access_is_secure(cpu, MMU_DATA_LOAD);
+
if (mmu_idx != MMU_NOMMU_IDX) {
hit = mmu_translate(cpu, &lu, addr, 0, 0);
if (hit) {
diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c
index 1e426963ba..cc40f275ea 100644
--- a/target/microblaze/mmu.c
+++ b/target/microblaze/mmu.c
@@ -74,7 +74,7 @@ static void mmu_change_pid(CPUMBState *env, unsigned int newpid)
/* rw - 0 = read, 1 = write, 2 = fetch. */
unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu,
- target_ulong vaddr, int rw, int mmu_idx)
+ target_ulong vaddr, MMUAccessType rw, int mmu_idx)
{
MicroBlazeMMU *mmu = &cpu->env.mmu;
unsigned int i, hit = 0;
diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h
index 09e4075739..b6b4b9ad60 100644
--- a/target/microblaze/mmu.h
+++ b/target/microblaze/mmu.h
@@ -84,7 +84,7 @@ typedef struct {
} MicroBlazeMMULookup;
unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu,
- target_ulong vaddr, int rw, int mmu_idx);
+ target_ulong vaddr, MMUAccessType rw, int mmu_idx);
uint32_t mmu_read(CPUMBState *env, bool ea, uint32_t rn);
void mmu_write(CPUMBState *env, bool ea, uint32_t rn, uint32_t v);
void mmu_init(MicroBlazeMMU *mmu);
diff --git a/tests/check-block.sh b/tests/check-block.sh
index e4f37905be..f86cb863de 100755
--- a/tests/check-block.sh
+++ b/tests/check-block.sh
@@ -73,10 +73,11 @@ cd tests/qemu-iotests
# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests
export QEMU_CHECK_BLOCK_AUTO=1
+export PYTHONUTF8=1
ret=0
for fmt in $format_list ; do
- ./check -makecheck -$fmt $group || ret=1
+ ${PYTHON} ./check -makecheck -$fmt $group || ret=1
done
exit $ret
diff --git a/tests/docker/dockerfiles/centos7.docker b/tests/docker/dockerfiles/centos7.docker
index 6f11af1989..75fdb53c7c 100644
--- a/tests/docker/dockerfiles/centos7.docker
+++ b/tests/docker/dockerfiles/centos7.docker
@@ -15,6 +15,7 @@ ENV PACKAGES \
gettext \
git \
glib2-devel \
+ glibc-static \
gnutls-devel \
libaio-devel \
libepoxy-devel \
diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker
index 64cb7a6eda..a763d55730 100644
--- a/tests/docker/dockerfiles/centos8.docker
+++ b/tests/docker/dockerfiles/centos8.docker
@@ -15,6 +15,7 @@ ENV PACKAGES \
glib2-devel \
libaio-devel \
libepoxy-devel \
+ libfdt-devel \
libgcrypt-devel \
lzo-devel \
make \
diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker
index a98314757d..ed546edcd6 100644
--- a/tests/docker/dockerfiles/debian-amd64.docker
+++ b/tests/docker/dockerfiles/debian-amd64.docker
@@ -21,6 +21,7 @@ RUN apt update && \
libbz2-dev \
liblzo2-dev \
libgcrypt20-dev \
+ libfdt-dev \
librdmacm-dev \
libsasl2-dev \
libsnappy-dev \
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 0b5053f2d0..0d7602abbe 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -6,6 +6,7 @@ ENV PACKAGES \
brlapi-devel \
bzip2 \
bzip2-devel \
+ capstone-devel \
ccache \
clang \
cyrus-sasl-devel \
@@ -37,6 +38,7 @@ ENV PACKAGES \
libpng-devel \
librbd-devel \
libseccomp-devel \
+ libslirp-devel \
libssh-devel \
libubsan \
libudev-devel \
@@ -46,6 +48,7 @@ ENV PACKAGES \
llvm \
lzo-devel \
make \
+ meson \
mingw32-bzip2 \
mingw32-curl \
mingw32-glib2 \
diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker
index ae889d8482..8519584d2b 100644
--- a/tests/docker/dockerfiles/ubuntu2004.docker
+++ b/tests/docker/dockerfiles/ubuntu2004.docker
@@ -37,6 +37,7 @@ ENV PACKAGES flex bison \
libsasl2-dev \
libsdl2-dev \
libseccomp-dev \
+ libslirp-dev \
libsnappy-dev \
libspice-protocol-dev \
libspice-server-dev \
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 890784b116..832fe4a1e2 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -246,7 +246,9 @@ class TestParallelOps(iotests.QMPTestCase):
node_name = 'node%d' % i
job_id = 'stream-%s' % node_name
pending_jobs.append(job_id)
- result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=1024)
+ result = self.vm.qmp('block-stream', device=node_name,
+ job_id=job_id, bottom=f'node{i-1}',
+ speed=1024)
self.assert_qmp(result, 'return', {})
for job in pending_jobs:
@@ -277,12 +279,14 @@ class TestParallelOps(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
# Set a speed limit to make sure that this job blocks the rest
- result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4', base=self.imgs[1], speed=1024*1024)
+ result = self.vm.qmp('block-stream', device='node4',
+ job_id='stream-node4', base=self.imgs[1],
+ filter_node_name='stream-filter', speed=1024*1024)
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2])
self.assert_qmp(result, 'error/desc',
- "Node 'node4' is busy: block device is in use by block job: stream")
+ "Node 'stream-filter' is busy: block device is in use by block job: stream")
result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3', base=self.imgs[2])
self.assert_qmp(result, 'error/desc',
@@ -295,7 +299,7 @@ class TestParallelOps(iotests.QMPTestCase):
# block-commit should also fail if it touches nodes used by the stream job
result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[4], job_id='commit-node4')
self.assert_qmp(result, 'error/desc',
- "Node 'node4' is busy: block device is in use by block job: stream")
+ "Node 'stream-filter' is busy: block device is in use by block job: stream")
result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[1], top=self.imgs[3], job_id='commit-node1')
self.assert_qmp(result, 'error/desc',
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
index 0e6b8591e7..b459a3f1e8 100755
--- a/tests/qemu-iotests/056
+++ b/tests/qemu-iotests/056
@@ -308,8 +308,13 @@ class BackupTest(iotests.QMPTestCase):
event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
match={'data': {'device': 'drive0'}})
self.assertNotEqual(event, None)
- # OK, job should be wedged
- res = self.vm.qmp('query-block-jobs')
+ # OK, job should pause, but it can't do it immediately, as it can't
+ # cancel other parallel requests (which didn't fail)
+ with iotests.Timeout(60, "Timeout waiting for backup actually paused"):
+ while True:
+ res = self.vm.qmp('query-block-jobs')
+ if res['return'][0]['status'] == 'paused':
+ break
self.assert_qmp(res, 'return[0]/status', 'paused')
res = self.vm.qmp('block-job-dismiss', id='drive0')
self.assert_qmp(res, 'error/desc',
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
index 6e73406cdb..8f839b4b7f 100644
--- a/tests/qemu-iotests/109.out
+++ b/tests/qemu-iotests/109.out
@@ -42,6 +42,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
@@ -91,6 +93,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
@@ -140,6 +144,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
@@ -189,6 +195,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
@@ -238,6 +246,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
@@ -287,6 +297,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
@@ -335,6 +347,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
@@ -383,6 +397,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
@@ -431,6 +447,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
@@ -479,6 +497,8 @@ read 512/512 bytes at offset 0
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
@@ -507,6 +527,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
@@ -528,6 +550,8 @@ Images are identical.
{"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"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
index 1a2e219057..cae52ffa5e 100755
--- a/tests/qemu-iotests/118
+++ b/tests/qemu-iotests/118
@@ -1,8 +1,7 @@
#!/usr/bin/env python3
# group: rw
#
-# Test case for the QMP 'change' command and all other associated
-# commands
+# Test case for media change monitor commands
#
# Copyright (C) 2015 Red Hat, Inc.
#
@@ -74,23 +73,6 @@ class ChangeBaseClass(iotests.QMPTestCase):
class GeneralChangeTestsBaseClass(ChangeBaseClass):
- def test_change(self):
- # 'change' requires a drive name, so skip the test for blockdev
- if not self.use_drive:
- return
-
- result = self.vm.qmp('change', device='drive0', target=new_img,
- arg=iotests.imgfmt)
- self.assert_qmp(result, 'return', {})
-
- self.wait_for_open()
- self.wait_for_close()
-
- result = self.vm.qmp('query-block')
- if self.has_real_tray:
- self.assert_qmp(result, 'return[0]/tray_open', False)
- self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
-
def test_blockdev_change_medium(self):
result = self.vm.qmp('blockdev-change-medium',
id=self.device_name, filename=new_img,
diff --git a/tests/qemu-iotests/118.out b/tests/qemu-iotests/118.out
index bf5bfd5aca..0a70391105 100644
--- a/tests/qemu-iotests/118.out
+++ b/tests/qemu-iotests/118.out
@@ -1,5 +1,5 @@
-.......................................................................................................................................................................
+...........................................................................................................................................................
----------------------------------------------------------------------
-Ran 167 tests
+Ran 155 tests
OK
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 3b21bc497f..90cdbd8e24 100755
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -23,6 +23,7 @@
import os
import iotests
+from iotests import try_remove
def io_write_patterns(img, patterns):
@@ -30,13 +31,6 @@ def io_write_patterns(img, patterns):
iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
-def try_remove(img):
- try:
- os.remove(img)
- except OSError:
- pass
-
-
def transaction_action(action, **kwargs):
return {
'type': action,
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
index f57a2e19f6..5251e2669e 100755
--- a/tests/qemu-iotests/129
+++ b/tests/qemu-iotests/129
@@ -21,68 +21,87 @@
import os
import iotests
-import time
class TestStopWithBlockJob(iotests.QMPTestCase):
test_img = os.path.join(iotests.test_dir, 'test.img')
target_img = os.path.join(iotests.test_dir, 'target.img')
base_img = os.path.join(iotests.test_dir, 'base.img')
+ overlay_img = os.path.join(iotests.test_dir, 'overlay.img')
def setUp(self):
iotests.qemu_img('create', '-f', iotests.imgfmt, self.base_img, "1G")
iotests.qemu_img('create', '-f', iotests.imgfmt, self.test_img,
"-b", self.base_img, '-F', iotests.imgfmt)
- iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M', self.test_img)
- self.vm = iotests.VM().add_drive(self.test_img)
+ iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M',
+ self.test_img)
+ self.vm = iotests.VM()
+ self.vm.add_object('throttle-group,id=tg0,x-bps-total=1024')
+
+ source_drive = 'driver=throttle,' \
+ 'node-name=source,' \
+ 'throttle-group=tg0,' \
+ f'file.driver={iotests.imgfmt},' \
+ f'file.file.filename={self.test_img}'
+
+ self.vm.add_drive(None, source_drive)
self.vm.launch()
def tearDown(self):
- params = {"device": "drive0",
- "bps": 0,
- "bps_rd": 0,
- "bps_wr": 0,
- "iops": 0,
- "iops_rd": 0,
- "iops_wr": 0,
- }
- result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
- **params)
self.vm.shutdown()
+ for img in (self.test_img, self.target_img, self.base_img,
+ self.overlay_img):
+ iotests.try_remove(img)
def do_test_stop(self, cmd, **args):
"""Test 'stop' while block job is running on a throttled drive.
The 'stop' command shouldn't drain the job"""
- params = {"device": "drive0",
- "bps": 1024,
- "bps_rd": 0,
- "bps_wr": 0,
- "iops": 0,
- "iops_rd": 0,
- "iops_wr": 0,
- }
- result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
- **params)
- self.assert_qmp(result, 'return', {})
result = self.vm.qmp(cmd, **args)
self.assert_qmp(result, 'return', {})
+
result = self.vm.qmp("stop")
self.assert_qmp(result, 'return', {})
result = self.vm.qmp("query-block-jobs")
- self.assert_qmp(result, 'return[0]/busy', True)
+
+ self.assert_qmp(result, 'return[0]/status', 'running')
self.assert_qmp(result, 'return[0]/ready', False)
def test_drive_mirror(self):
self.do_test_stop("drive-mirror", device="drive0",
- target=self.target_img,
- sync="full")
+ target=self.target_img, format=iotests.imgfmt,
+ sync="full", buf_size=65536)
def test_drive_backup(self):
+ # Limit max-chunk and max-workers so that block-copy will not
+ # launch so many workers working on so much data each that
+ # stop's bdrv_drain_all() would finish the job
self.do_test_stop("drive-backup", device="drive0",
- target=self.target_img,
- sync="full")
+ target=self.target_img, format=iotests.imgfmt,
+ sync="full",
+ x_perf={ 'max-chunk': 65536,
+ 'max-workers': 8 })
def test_block_commit(self):
- self.do_test_stop("block-commit", device="drive0")
+ # Add overlay above the source node so that we actually use a
+ # commit job instead of a mirror job
+
+ iotests.qemu_img('create', '-f', iotests.imgfmt, self.overlay_img,
+ '1G')
+
+ result = self.vm.qmp('blockdev-add', **{
+ 'node-name': 'overlay',
+ 'driver': iotests.imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': self.overlay_img
+ }
+ })
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-snapshot',
+ node='source', overlay='overlay')
+ self.assert_qmp(result, 'return', {})
+
+ self.do_test_stop('block-commit', device='drive0', top_node='source')
if __name__ == '__main__':
iotests.main(supported_fmts=["qcow2"],
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
index 6d8652e22b..c4c15fb275 100644
--- a/tests/qemu-iotests/141.out
+++ b/tests/qemu-iotests/141.out
@@ -165,7 +165,7 @@ wrote 1048576/1048576 bytes at offset 0
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
{'execute': 'blockdev-del',
'arguments': {'node-name': 'drv0'}}
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}}
{'execute': 'block-job-cancel',
'arguments': {'device': 'job0'}}
{"return": {}}
diff --git a/tests/qemu-iotests/146.out b/tests/qemu-iotests/146.out
index 80513cdd06..c67ba4ba7c 100644
--- a/tests/qemu-iotests/146.out
+++ b/tests/qemu-iotests/146.out
@@ -2,414 +2,414 @@ QA output created by 146
=== Testing VPC Autodetect ===
-[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false}]
=== Testing VPC with current_size force ===
-[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false}]
=== Testing VPC with chs force ===
-[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false}]
=== Testing Hyper-V Autodetect ===
-[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false}]
=== Testing Hyper-V with current_size force ===
-[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 136365211648, "depth": 0, "zero": true, "data": false}]
=== Testing Hyper-V with chs force ===
-[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 136363130880, "depth": 0, "zero": true, "data": false}]
=== Testing d2v Autodetect ===
-[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET }]
+[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
=== Testing d2v with current_size force ===
-[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET }]
+[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
=== Testing d2v with chs force ===
-[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET },
-{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET }]
+[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 4194304, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 6291456, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 8388608, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 10485760, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 12582912, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 14680064, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 16777216, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 18874368, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 20971520, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 23068672, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 25165824, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 27262976, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 29360128, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 31457280, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 33554432, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 35651584, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 37748736, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 39845888, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 41943040, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 44040192, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 46137344, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 48234496, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 50331648, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 52428800, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 54525952, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 56623104, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 58720256, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 60817408, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 62914560, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 65011712, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 67108864, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 69206016, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 71303168, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 73400320, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 75497472, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 77594624, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 79691776, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 81788928, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 83886080, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 85983232, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 88080384, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 90177536, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 92274688, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 94371840, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 96468992, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 98566144, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 100663296, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 102760448, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 104857600, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 106954752, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 109051904, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 111149056, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 113246208, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 115343360, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 117440512, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 119537664, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 121634816, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 123731968, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 125829120, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 127926272, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 130023424, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 132120576, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 134217728, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 136314880, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 138412032, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 140509184, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 142606336, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 144703488, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 146800640, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 148897792, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 150994944, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 153092096, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 155189248, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 157286400, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 159383552, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 161480704, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 163577856, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 165675008, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 167772160, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 169869312, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 171966464, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 174063616, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 176160768, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 178257920, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 180355072, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 182452224, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 184549376, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 186646528, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 188743680, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 190840832, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 192937984, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 195035136, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 197132288, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 199229440, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 201326592, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 203423744, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 205520896, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 207618048, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 209715200, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 211812352, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 213909504, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 216006656, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 218103808, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 220200960, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 222298112, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 224395264, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 226492416, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 228589568, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 230686720, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 232783872, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 234881024, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 236978176, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 239075328, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 241172480, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 243269632, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 245366784, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 247463936, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 249561088, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 251658240, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 253755392, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 255852544, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 257949696, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 260046848, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
+{ "start": 262144000, "length": 1310720, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
=== Testing Image create, default ===
@@ -417,15 +417,15 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296
=== Read created image, default opts ====
-[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false}]
=== Read created image, force_size_calc=chs ====
-[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false}]
=== Read created image, force_size_calc=current_size ====
-[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 4295467008, "depth": 0, "zero": true, "data": false}]
=== Testing Image create, force_size ===
@@ -433,13 +433,13 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296
=== Read created image, default opts ====
-[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false}]
=== Read created image, force_size_calc=chs ====
-[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false}]
=== Read created image, force_size_calc=current_size ====
-[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false }]
+[{ "start": 0, "length": 4294967296, "depth": 0, "zero": true, "data": false}]
*** done
diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178
index 3b1a7adce4..8df241ead8 100755
--- a/tests/qemu-iotests/178
+++ b/tests/qemu-iotests/178
@@ -57,7 +57,7 @@ $QEMU_IMG measure --image-opts # missing filename
$QEMU_IMG measure -f qcow2 # missing filename
$QEMU_IMG measure -l snap1 # missing filename
$QEMU_IMG measure -o , # invalid option list
-$QEMU_IMG measure -l snapshot.foo # invalid snapshot option
+$QEMU_IMG measure -l snapshot.foo=bar # invalid snapshot option
$QEMU_IMG measure --output foo # invalid output format
$QEMU_IMG measure --size -1 # invalid image size
$QEMU_IMG measure -O foo "$TEST_IMG" # unknown image file format
diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2
index c7997760fd..fe193fd5f4 100644
--- a/tests/qemu-iotests/178.out.qcow2
+++ b/tests/qemu-iotests/178.out.qcow2
@@ -11,7 +11,7 @@ qemu-img: --image-opts, -f, and -l require a filename argument.
qemu-img: --image-opts, -f, and -l require a filename argument.
qemu-img: Invalid option list: ,
qemu-img: Invalid parameter 'snapshot.foo'
-qemu-img: Failed in parsing snapshot param 'snapshot.foo'
+qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
qemu-img: --output must be used with human or json as argument.
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
qemu-img: Unknown file format 'foo'
diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw
index 20e17da115..445e460fad 100644
--- a/tests/qemu-iotests/178.out.raw
+++ b/tests/qemu-iotests/178.out.raw
@@ -11,7 +11,7 @@ qemu-img: --image-opts, -f, and -l require a filename argument.
qemu-img: --image-opts, -f, and -l require a filename argument.
qemu-img: Invalid option list: ,
qemu-img: Invalid parameter 'snapshot.foo'
-qemu-img: Failed in parsing snapshot param 'snapshot.foo'
+qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
qemu-img: --output must be used with human or json as argument.
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
qemu-img: Unknown file format 'foo'
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
index 7bc8fe5767..f2ec5c5ceb 100755
--- a/tests/qemu-iotests/185
+++ b/tests/qemu-iotests/185
@@ -183,7 +183,8 @@ _send_qemu_cmd $h \
'target': '$TEST_IMG.copy',
'format': '$IMGFMT',
'sync': 'full',
- 'speed': 65536 } }" \
+ 'speed': 65536,
+ 'x-perf': {'max-chunk': 65536} } }" \
"return"
# If we don't sleep here 'quit' command races with disk I/O
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
index eab55d22bf..9dedc8eacb 100644
--- a/tests/qemu-iotests/185.out
+++ b/tests/qemu-iotests/185.out
@@ -88,7 +88,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
'target': 'TEST_DIR/t.IMGFMT.copy',
'format': 'IMGFMT',
'sync': 'full',
- 'speed': 65536 } }
+ 'speed': 65536,
+ 'x-perf': { 'max-chunk': 65536 } } }
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
index 16c3ca7fff..d1757e9e6f 100755
--- a/tests/qemu-iotests/219
+++ b/tests/qemu-iotests/219
@@ -204,13 +204,13 @@ with iotests.FilePath('disk.img') as disk_path, \
# but related to this also automatic state transitions like job
# completion), but still get pause points often enough to avoid making this
# test very slow, it's important to have the right ratio between speed and
- # buf_size.
+ # copy-chunk-size.
#
- # For backup, buf_size is hard-coded to the source image cluster size (64k),
- # so we'll pick the same for mirror. The slice time, i.e. the granularity
- # of the rate limiting is 100ms. With a speed of 256k per second, we can
- # get four pause points per second. This gives us 250ms per iteration,
- # which should be enough to stay deterministic.
+ # Chose 64k copy-chunk-size both for mirror (by buf_size) and backup (by
+ # x-max-chunk). The slice time, i.e. the granularity of the rate limiting
+ # is 100ms. With a speed of 256k per second, we can get four pause points
+ # per second. This gives us 250ms per iteration, which should be enough to
+ # stay deterministic.
test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
'device': 'drive0-node',
@@ -227,6 +227,7 @@ with iotests.FilePath('disk.img') as disk_path, \
'target': copy_path,
'sync': 'full',
'speed': 262144,
+ 'x-perf': {'max-chunk': 65536},
'auto-finalize': auto_finalize,
'auto-dismiss': auto_dismiss,
})
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
index 86f00f290f..cfdeb902be 100755
--- a/tests/qemu-iotests/245
+++ b/tests/qemu-iotests/245
@@ -893,20 +893,24 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# hd1 <- hd0
result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
- device = 'hd1', auto_finalize = False)
+ device = 'hd1', filter_node_name='cor',
+ auto_finalize = False)
self.assert_qmp(result, 'return', {})
- # We can't reopen with the original options because that would
- # make hd1 read-only and block-stream requires it to be read-write
- # (Which error message appears depends on whether the stream job is
- # already done with copying at this point.)
+ # We can't reopen with the original options because there is a filter
+ # inserted by stream job above hd1.
self.reopen(opts, {},
- ["Can't set node 'hd1' to r/o with copy-on-read enabled",
- "Cannot make block node read-only, there is a writer on it"])
+ "Cannot change the option 'backing.backing.file.node-name'")
+
+ # We can't reopen hd1 to read-only, as block-stream requires it to be
+ # read-write
+ self.reopen(opts['backing'], {'read-only': True},
+ "Cannot make block node read-only, there is a writer on it")
# We can't remove hd2 while the stream job is ongoing
opts['backing']['backing'] = None
- self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
+ self.reopen(opts['backing'], {'read-only': False},
+ "Cannot change 'backing' link from 'hd1' to 'hd2'")
# We can detach hd1 from hd0 because it doesn't affect the stream job
opts['backing'] = None
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index a2f4b5afe6..7cd2520829 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -192,6 +192,7 @@ def blockdev_backup(vm, device, target, sync, **kwargs):
target=target,
sync=sync,
filter_node_name='backup-top',
+ x_perf={'max-workers': 1},
**kwargs)
return result
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
index 64dd460055..a7ba512f4c 100644
--- a/tests/qemu-iotests/257.out
+++ b/tests/qemu-iotests/257.out
@@ -30,7 +30,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -78,7 +78,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -92,7 +92,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -205,7 +205,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -219,7 +219,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -290,7 +290,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -338,7 +338,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -354,7 +354,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -416,7 +416,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -430,7 +430,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -501,7 +501,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -549,7 +549,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -563,7 +563,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -676,7 +676,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -690,7 +690,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -761,7 +761,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -809,7 +809,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -823,7 +823,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -936,7 +936,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -950,7 +950,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1021,7 +1021,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1069,7 +1069,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1085,7 +1085,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1147,7 +1147,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1161,7 +1161,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1232,7 +1232,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1280,7 +1280,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1294,7 +1294,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -1407,7 +1407,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1421,7 +1421,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1492,7 +1492,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1540,7 +1540,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1554,7 +1554,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -1667,7 +1667,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1681,7 +1681,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1752,7 +1752,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1800,7 +1800,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1816,7 +1816,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1878,7 +1878,7 @@ expecting 13 dirty sectors; have 13. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1892,7 +1892,7 @@ expecting 13 dirty sectors; have 13. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1963,7 +1963,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2011,7 +2011,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2025,7 +2025,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -2138,7 +2138,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2152,7 +2152,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2223,7 +2223,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2271,7 +2271,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2285,7 +2285,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -2398,7 +2398,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2412,7 +2412,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2483,7 +2483,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2531,7 +2531,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2547,7 +2547,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2609,7 +2609,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2623,7 +2623,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2694,7 +2694,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2742,7 +2742,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2756,7 +2756,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -2869,7 +2869,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2883,7 +2883,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2954,7 +2954,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3002,7 +3002,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3016,7 +3016,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -3129,7 +3129,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3143,7 +3143,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3214,7 +3214,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3262,7 +3262,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3278,7 +3278,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3340,7 +3340,7 @@ expecting 1014 dirty sectors; have 1014. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3354,7 +3354,7 @@ expecting 1014 dirty sectors; have 1014. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3425,7 +3425,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3473,7 +3473,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3487,7 +3487,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -3600,7 +3600,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3614,7 +3614,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3685,7 +3685,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3733,7 +3733,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3747,7 +3747,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -3860,7 +3860,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3874,7 +3874,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3945,7 +3945,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3993,7 +3993,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4009,7 +4009,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 458752, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4071,7 +4071,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4085,7 +4085,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4156,7 +4156,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4204,7 +4204,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4218,7 +4218,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -4331,7 +4331,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4345,7 +4345,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4416,7 +4416,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4464,7 +4464,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4478,7 +4478,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -4591,7 +4591,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4605,7 +4605,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4676,7 +4676,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4724,7 +4724,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4740,7 +4740,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 458752, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4802,7 +4802,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4816,7 +4816,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4887,7 +4887,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4935,7 +4935,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4949,7 +4949,7 @@ expecting 6 dirty sectors; have 6. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
{"return": {}}
--- Write #2 ---
@@ -5062,7 +5062,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5076,7 +5076,7 @@ expecting 12 dirty sectors; have 12. OK!
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -5139,155 +5139,155 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
-- Sync mode incremental tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-- Sync mode bitmap tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
-- Sync mode full tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'full'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
-- Sync mode top tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'top'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
-- Sync mode none tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297
index 85bc1c0c85..a37910b42d 100755
--- a/tests/qemu-iotests/297
+++ b/tests/qemu-iotests/297
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env python3
# group: meta
#
# Copyright (C) 2020 Red Hat, Inc.
@@ -16,30 +16,98 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-seq=$(basename $0)
-echo "QA output created by $seq"
+import os
+import re
+import shutil
+import subprocess
+import sys
-status=1 # failure is the default!
+import iotests
-# get standard environment
-. ./common.rc
-if ! type -p "pylint-3" > /dev/null; then
- _notrun "pylint-3 not found"
-fi
-if ! type -p "mypy" > /dev/null; then
- _notrun "mypy not found"
-fi
+# TODO: Empty this list!
+SKIP_FILES = (
+ '030', '040', '041', '044', '045', '055', '056', '057', '065', '093',
+ '096', '118', '124', '132', '136', '139', '147', '148', '149',
+ '151', '152', '155', '163', '165', '169', '194', '196', '199', '202',
+ '203', '205', '206', '207', '208', '210', '211', '212', '213', '216',
+ '218', '219', '222', '224', '228', '234', '235', '236', '237', '238',
+ '240', '242', '245', '246', '248', '255', '256', '257', '258', '260',
+ '262', '264', '266', '274', '277', '280', '281', '295', '296', '298',
+ '299', '302', '303', '304', '307',
+ 'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py'
+)
-pylint-3 --score=n iotests.py
-MYPYPATH=../../python/ mypy --warn-unused-configs --disallow-subclassing-any \
- --disallow-any-generics --disallow-incomplete-defs \
- --disallow-untyped-decorators --no-implicit-optional \
- --warn-redundant-casts --warn-unused-ignores \
- --no-implicit-reexport iotests.py
+def is_python_file(filename):
+ if not os.path.isfile(filename):
+ return False
-# success, all done
-echo "*** done"
-rm -f $seq.full
-status=0
+ if filename.endswith('.py'):
+ return True
+
+ with open(filename) as f:
+ try:
+ first_line = f.readline()
+ return re.match('^#!.*python', first_line) is not None
+ except UnicodeDecodeError: # Ignore binary files
+ return False
+
+
+def run_linters():
+ files = [filename for filename in (set(os.listdir('.')) - set(SKIP_FILES))
+ if is_python_file(filename)]
+
+ iotests.logger.debug('Files to be checked:')
+ iotests.logger.debug(', '.join(sorted(files)))
+
+ print('=== pylint ===')
+ sys.stdout.flush()
+
+ # Todo notes are fine, but fixme's or xxx's should probably just be
+ # fixed (in tests, at least)
+ env = os.environ.copy()
+ qemu_module_path = os.path.join(os.path.dirname(__file__),
+ '..', '..', 'python')
+ try:
+ env['PYTHONPATH'] += os.pathsep + qemu_module_path
+ except KeyError:
+ env['PYTHONPATH'] = qemu_module_path
+ subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files),
+ env=env, check=False)
+
+ print('=== mypy ===')
+ sys.stdout.flush()
+
+ # We have to call mypy separately for each file. Otherwise, it
+ # will interpret all given files as belonging together (i.e., they
+ # may not both define the same classes, etc.; most notably, they
+ # must not both define the __main__ module).
+ env['MYPYPATH'] = env['PYTHONPATH']
+ for filename in files:
+ p = subprocess.run(('mypy',
+ '--warn-unused-configs',
+ '--disallow-subclassing-any',
+ '--disallow-any-generics',
+ '--disallow-incomplete-defs',
+ '--disallow-untyped-decorators',
+ '--no-implicit-optional',
+ '--warn-redundant-casts',
+ '--warn-unused-ignores',
+ '--no-implicit-reexport',
+ filename),
+ env=env,
+ check=False,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True)
+
+ if p.returncode != 0:
+ print(p.stdout)
+
+
+for linter in ('pylint-3', 'mypy'):
+ if shutil.which(linter) is None:
+ iotests.notrun(f'{linter} not found')
+
+iotests.script_main(run_linters)
diff --git a/tests/qemu-iotests/297.out b/tests/qemu-iotests/297.out
index 6acc843649..f2e1314d10 100644
--- a/tests/qemu-iotests/297.out
+++ b/tests/qemu-iotests/297.out
@@ -1,3 +1,2 @@
-QA output created by 297
-Success: no issues found in 1 source file
-*** done
+=== pylint ===
+=== mypy ===
diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
index 23aca59d9c..43264d883d 100755
--- a/tests/qemu-iotests/300
+++ b/tests/qemu-iotests/300
@@ -23,12 +23,15 @@ import os
import random
import re
from typing import Dict, List, Optional, Union
+
import iotests
+
+# Import qemu after iotests.py has amended sys.path
+# pylint: disable=wrong-import-order
import qemu
BlockBitmapMapping = List[Dict[str, Union[str, List[Dict[str, str]]]]]
-assert iotests.sock_dir is not None
mig_sock = os.path.join(iotests.sock_dir, 'mig_sock')
@@ -112,10 +115,14 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
If @msg is None, check that there has not been any error.
"""
self.vm_b.shutdown()
+
+ log = self.vm_b.get_log()
+ assert log is not None # Loaded after shutdown
+
if msg is None:
- self.assertNotIn('qemu-system-', self.vm_b.get_log())
+ self.assertNotIn('qemu-system-', log)
else:
- self.assertIn(msg, self.vm_b.get_log())
+ self.assertIn(msg, log)
@staticmethod
def mapping(node_name: str, node_alias: str,
@@ -447,9 +454,13 @@ class TestBlockBitmapMappingErrors(TestDirtyBitmapMigration):
# Check for the error in the source's log
self.vm_a.shutdown()
+
+ log = self.vm_a.get_log()
+ assert log is not None # Loaded after shutdown
+
self.assertIn(f"Cannot migrate bitmap '{name}' on node "
f"'{self.src_node_name}': Name is longer than 255 bytes",
- self.vm_a.get_log())
+ log)
# Expect abnormal shutdown of the destination VM because of
# the failed migration
diff --git a/tests/qemu-iotests/310 b/tests/qemu-iotests/310
new file mode 100755
index 0000000000..9d9c928c4b
--- /dev/null
+++ b/tests/qemu-iotests/310
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+# group: rw quick
+#
+# Copy-on-read tests using a COR filter with a bottom node
+#
+# Copyright (C) 2018 Red Hat, Inc.
+# Copyright (c) 2020 Virtuozzo International GmbH
+#
+# 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/>.
+#
+
+import iotests
+from iotests import log, qemu_img, qemu_io_silent
+
+# Need backing file support
+iotests.script_initialize(supported_fmts=['qcow2'],
+ supported_platforms=['linux'])
+
+log('')
+log('=== Copy-on-read across nodes ===')
+log('')
+
+# This test is similar to the 216 one by Max Reitz <mreitz@redhat.com>
+# The difference is that this test case involves a bottom node to the
+# COR filter driver.
+
+with iotests.FilePath('base.img') as base_img_path, \
+ iotests.FilePath('mid.img') as mid_img_path, \
+ iotests.FilePath('top.img') as top_img_path, \
+ iotests.VM() as vm:
+
+ log('--- Setting up images ---')
+ log('')
+
+ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
+ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
+ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 3M 1M') == 0
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
+ '-F', iotests.imgfmt, mid_img_path) == 0
+ assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 2M 1M') == 0
+ assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 4M 1M') == 0
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path,
+ '-F', iotests.imgfmt, top_img_path) == 0
+ assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
+
+# 0 1 2 3 4
+# top 2
+# mid 3 3
+# base 1 1
+
+ log('Done')
+
+ log('')
+ log('--- Doing COR ---')
+ log('')
+
+ vm.launch()
+
+ log(vm.qmp('blockdev-add',
+ node_name='node0',
+ driver='copy-on-read',
+ bottom='node2',
+ file={
+ 'driver': iotests.imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ 'backing': {
+ 'node-name': 'node2',
+ 'driver': iotests.imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': mid_img_path
+ },
+ 'backing': {
+ 'driver': iotests.imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': base_img_path
+ }
+ },
+ }
+ }))
+
+ # Trigger COR
+ log(vm.qmp('human-monitor-command',
+ command_line='qemu-io node0 "read 0 5M"'))
+
+ vm.shutdown()
+
+ log('')
+ log('--- Checking COR result ---')
+ log('')
+
+ # Detach backing to check that we can read the data from the top level now
+ assert qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt,
+ top_img_path) == 0
+
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0
+
+ log('Done')
diff --git a/tests/qemu-iotests/310.out b/tests/qemu-iotests/310.out
new file mode 100644
index 0000000000..a70aa5cdae
--- /dev/null
+++ b/tests/qemu-iotests/310.out
@@ -0,0 +1,15 @@
+
+=== Copy-on-read across nodes ===
+
+--- Setting up images ---
+
+Done
+
+--- Doing COR ---
+
+{"return": {}}
+{"return": ""}
+
+--- Checking COR result ---
+
+Done
diff --git a/tests/qemu-iotests/313 b/tests/qemu-iotests/313
new file mode 100755
index 0000000000..a75655b7ef
--- /dev/null
+++ b/tests/qemu-iotests/313
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+# group: rw auto quick
+#
+# Test for the regression fixed in commit c8bf9a9169
+#
+# Copyright (C) 2020 Igalia, S.L.
+# Author: Alberto Garcia <berto@igalia.com>
+# Based on a test case by Maxim Levitsky <mlevitsk@redhat.com>
+#
+# 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=berto@igalia.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
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+_unsupported_imgopts cluster_size refcount_bits extended_l2 compat=0.10 data_file
+
+# The cluster size must be at least the granularity of the mirror job (4KB)
+# Note that larger cluster sizes will produce very large images (several GBs)
+cluster_size=4096
+refcount_bits=64 # Make it equal to the L2 entry size for convenience
+options="cluster_size=${cluster_size},refcount_bits=${refcount_bits}"
+
+# Number of refcount entries per refcount blocks
+ref_entries=$(( ${cluster_size} * 8 / ${refcount_bits} ))
+
+# Number of data clusters needed to fill a refcount block
+# Equals ${ref_entries} minus two (one L2 table and one refcount block)
+data_clusters_per_refblock=$(( ${ref_entries} - 2 ))
+
+# Number of entries in the refcount cache
+ref_blocks=4
+
+# Write enough data clusters to fill the refcount cache and allocate
+# one more refcount block.
+# Subtract 3 clusters from the total: qcow2 header, refcount table, L1 table
+total_data_clusters=$(( ${data_clusters_per_refblock} * ${ref_blocks} + 1 - 3 ))
+
+# Total size to write in bytes
+total_size=$(( ${total_data_clusters} * ${cluster_size} ))
+
+echo
+echo '### Create the image'
+echo
+TEST_IMG_FILE=$TEST_IMG.base _make_test_img -o $options $total_size | _filter_img_create_size
+
+echo
+echo '### Write data to allocate more refcount blocks than the cache can hold'
+echo
+$QEMU_IO -c "write -P 1 0 $total_size" $TEST_IMG.base | _filter_qemu_io
+
+echo
+echo '### Create an overlay'
+echo
+_make_test_img -F $IMGFMT -b $TEST_IMG.base -o $options | _filter_img_create_size
+
+echo
+echo '### Fill the overlay with zeroes'
+echo
+$QEMU_IO -c "write -z 0 $total_size" $TEST_IMG | _filter_qemu_io
+
+echo
+echo '### Commit changes to the base image'
+echo
+$QEMU_IMG commit $TEST_IMG
+
+echo
+echo '### Check the base image'
+echo
+$QEMU_IMG check $TEST_IMG.base
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/313.out b/tests/qemu-iotests/313.out
new file mode 100644
index 0000000000..adb9f7bd95
--- /dev/null
+++ b/tests/qemu-iotests/313.out
@@ -0,0 +1,29 @@
+QA output created by 313
+
+### Create the image
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=SIZE
+
+### Write data to allocate more refcount blocks than the cache can hold
+
+wrote 8347648/8347648 bytes at offset 0
+7.961 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+### Create an overlay
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+
+### Fill the overlay with zeroes
+
+wrote 8347648/8347648 bytes at offset 0
+7.961 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+### Commit changes to the base image
+
+Image committed.
+
+### Check the base image
+
+No errors were found on the image.
+Image end offset: 8396800
+*** done
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index 952762d5ed..5190dee82e 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -1,7 +1,8 @@
-#!/usr/bin/env bash
+#!/usr/bin/env python3
#
-# Copyright (C) 2009 Red Hat, Inc.
-# Copyright (c) 2000-2002,2006 Silicon Graphics, Inc. All Rights Reserved.
+# Configure environment and run group of tests in it.
+#
+# Copyright (c) 2020-2021 Virtuozzo International GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -14,967 +15,129 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-# Control script for QA
-#
-
-status=0
-needwrap=true
-try=0
-n_bad=0
-bad=""
-notrun=""
-casenotrun=""
-interrupt=true
-makecheck=false
-
-_init_error()
-{
- echo "check: $1" >&2
- exit 1
-}
-
-if [ -L "$0" ]
-then
- # called from the build tree
- source_iotests=$(dirname "$(readlink "$0")")
- if [ -z "$source_iotests" ]
- then
- _init_error "failed to obtain source tree name from check symlink"
- fi
- source_iotests=$(cd "$source_iotests"; pwd) || _init_error "failed to enter source tree"
- build_iotests=$(cd "$(dirname "$0")"; pwd)
-else
- # called from the source tree
- source_iotests=$PWD
- # this may be an in-tree build (note that in the following code we may not
- # assume that it truly is and have to test whether the build results
- # actually exist)
- build_iotests=$PWD
-fi
-
-build_root="$build_iotests/../.."
-
-# we need common.env
-if ! . "$build_iotests/common.env"
-then
- _init_error "failed to source common.env (make sure the qemu-iotests are run from tests/qemu-iotests in the build tree)"
-fi
-
-# we need common.config
-if ! . "$source_iotests/common.config"
-then
- _init_error "failed to source common.config"
-fi
-
-_full_imgfmt_details()
-{
- if [ -n "$IMGOPTS" ]; then
- echo "$IMGFMT ($IMGOPTS)"
- else
- echo "$IMGFMT"
- fi
-}
-
-_full_platform_details()
-{
- os=$(uname -s)
- host=$(hostname -s)
- kernel=$(uname -r)
- platform=$(uname -m)
- echo "$os/$platform $host $kernel"
-}
-
-_full_env_details()
-{
- cat <<EOF
-QEMU -- "$QEMU_PROG" $QEMU_OPTIONS
-QEMU_IMG -- "$QEMU_IMG_PROG" $QEMU_IMG_OPTIONS
-QEMU_IO -- "$QEMU_IO_PROG" $QEMU_IO_OPTIONS
-QEMU_NBD -- "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS
-IMGFMT -- $FULL_IMGFMT_DETAILS
-IMGPROTO -- $IMGPROTO
-PLATFORM -- $FULL_HOST_DETAILS
-TEST_DIR -- $TEST_DIR
-SOCK_DIR -- $SOCK_DIR
-SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER
-
-EOF
-}
-
-# $1 = prog to look for
-set_prog_path()
-{
- p=$(command -v $1 2> /dev/null)
- if [ -n "$p" -a -x "$p" ]; then
- type -p "$p"
- else
- return 1
- fi
-}
-
-if [ -z "$TEST_DIR" ]; then
- TEST_DIR=$PWD/scratch
-fi
-mkdir -p "$TEST_DIR" || _init_error 'Failed to create TEST_DIR'
-
-tmp_sock_dir=false
-if [ -z "$SOCK_DIR" ]; then
- SOCK_DIR=$(mktemp -d)
- tmp_sock_dir=true
-fi
-mkdir -p "$SOCK_DIR" || _init_error 'Failed to create SOCK_DIR'
-
-diff="diff -u"
-verbose=false
-debug=false
-group=false
-xgroup=false
-imgopts=false
-showme=false
-sortme=false
-expunge=true
-have_test_arg=false
-cachemode=false
-aiomode=false
-
-tmp="${TEST_DIR}"/$$
-rm -f $tmp.list $tmp.tmp $tmp.sed
-
-export IMGFMT=raw
-export IMGFMT_GENERIC=true
-export IMGPROTO=file
-export IMGOPTS=""
-export CACHEMODE="writeback"
-export AIOMODE="threads"
-export QEMU_IO_OPTIONS=""
-export QEMU_IO_OPTIONS_NO_FMT=""
-export CACHEMODE_IS_DEFAULT=true
-export VALGRIND_QEMU=
-export IMGKEYSECRET=
-export IMGOPTSSYNTAX=false
-
-# Save current tty settings, since an aborting qemu call may leave things
-# screwed up
-STTY_RESTORE=
-if test -t 0; then
- STTY_RESTORE=$(stty -g)
-fi
-
-for r
-do
-
- if $group
- then
- # arg after -g
- group_list=$(sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
-s/ .*//p
-}')
- if [ -z "$group_list" ]
- then
- echo "Group \"$r\" is empty or not defined?"
- exit 1
- fi
- [ ! -s $tmp.list ] && touch $tmp.list
- for t in $group_list
- do
- if grep -s "^$t\$" $tmp.list >/dev/null
- then
- :
- else
- echo "$t" >>$tmp.list
- fi
- done
- group=false
- continue
-
- elif $xgroup
- then
- # arg after -x
- # Populate $tmp.list with all tests
- awk '/^[0-9]{3,}/ {print $1}' "${source_iotests}/group" > $tmp.list 2>/dev/null
- group_list=$(sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
-s/ .*//p
-}')
- if [ -z "$group_list" ]
- then
- echo "Group \"$r\" is empty or not defined?"
- exit 1
- fi
- numsed=0
- rm -f $tmp.sed
- for t in $group_list
- do
- if [ $numsed -gt 100 ]
- then
- sed -f $tmp.sed <$tmp.list >$tmp.tmp
- mv $tmp.tmp $tmp.list
- numsed=0
- rm -f $tmp.sed
- fi
- echo "/^$t\$/d" >>$tmp.sed
- numsed=$(expr $numsed + 1)
- done
- sed -f $tmp.sed <$tmp.list >$tmp.tmp
- mv $tmp.tmp $tmp.list
- xgroup=false
- continue
-
- elif $imgopts
- then
- IMGOPTS="$r"
- imgopts=false
- continue
- elif $cachemode
- then
- CACHEMODE="$r"
- CACHEMODE_IS_DEFAULT=false
- cachemode=false
- continue
- elif $aiomode
- then
- AIOMODE="$r"
- aiomode=false
- continue
- fi
-
- xpand=true
- case "$r"
- in
-
- -\? | -h | --help) # usage
- echo "Usage: $0 [options] [testlist]"'
-
-common options
- -v verbose
- -d debug
-
-image format options
- -raw test raw (default)
- -bochs test bochs
- -cloop test cloop
- -parallels test parallels
- -qcow test qcow
- -qcow2 test qcow2
- -qed test qed
- -vdi test vdi
- -vpc test vpc
- -vhdx test vhdx
- -vmdk test vmdk
- -luks test luks
- -dmg test dmg
-
-image protocol options
- -file test file (default)
- -rbd test rbd
- -sheepdog test sheepdog
- -nbd test nbd
- -fuse test fuse
- -ssh test ssh
- -nfs test nfs
-
-other options
- -xdiff graphical mode diff
- -nocache use O_DIRECT on backing file
- -misalign misalign memory allocations
- -n show me, do not run tests
- -o options -o options to pass to qemu-img create/convert
- -c mode cache mode
- -i mode AIO mode
- -makecheck pretty print output for make check
-
-testlist options
- -g group[,group...] include tests from these groups
- -x group[,group...] exclude tests from these groups
- NNN include test NNN
- NNN-NNN include test range (eg. 012-021)
-'
- exit 0
- ;;
-
- -raw)
- IMGFMT=raw
- xpand=false
- ;;
-
- -bochs)
- IMGFMT=bochs
- IMGFMT_GENERIC=false
- xpand=false
- ;;
-
- -cloop)
- IMGFMT=cloop
- IMGFMT_GENERIC=false
- xpand=false
- ;;
-
- -parallels)
- IMGFMT=parallels
- xpand=false
- ;;
-
- -qcow)
- IMGFMT=qcow
- xpand=false
- ;;
-
- -qcow2)
- IMGFMT=qcow2
- xpand=false
- ;;
-
- -luks)
- IMGOPTSSYNTAX=true
- IMGFMT=luks
- IMGKEYSECRET=123456
- xpand=false
- ;;
-
- -dmg)
- IMGFMT=dmg
- IMGFMT_GENERIC=false
- xpand=false
- ;;
-
- -qed)
- IMGFMT=qed
- xpand=false
- ;;
-
- -vdi)
- IMGFMT=vdi
- xpand=false
- ;;
-
- -vmdk)
- IMGFMT=vmdk
- xpand=false
- ;;
-
- -vpc)
- IMGFMT=vpc
- xpand=false
- ;;
-
- -vhdx)
- IMGFMT=vhdx
- xpand=false
- ;;
-
- -file)
- IMGPROTO=file
- xpand=false
- ;;
-
- -rbd)
- IMGPROTO=rbd
- xpand=false
- ;;
-
- -sheepdog)
- IMGPROTO=sheepdog
- xpand=false
- ;;
-
- -nbd)
- IMGPROTO=nbd
- xpand=false
- ;;
-
- -fuse)
- IMGPROTO=fuse
- xpand=false
- ;;
-
- -ssh)
- IMGPROTO=ssh
- xpand=false
- ;;
-
- -nfs)
- IMGPROTO=nfs
- xpand=false
- ;;
-
- -nocache)
- CACHEMODE="none"
- CACHEMODE_IS_DEFAULT=false
- xpand=false
- ;;
-
- -misalign)
- QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign"
- xpand=false
- ;;
-
- -valgrind)
- VALGRIND_QEMU='y'
- xpand=false
- ;;
-
- -g) # -g group ... pick from group file
- group=true
- xpand=false
- ;;
-
- -xdiff) # graphical diff mode
- xpand=false
-
- if [ ! -z "$DISPLAY" ]
- then
- command -v xdiff >/dev/null 2>&1 && diff=xdiff
- command -v gdiff >/dev/null 2>&1 && diff=gdiff
- command -v tkdiff >/dev/null 2>&1 && diff=tkdiff
- command -v xxdiff >/dev/null 2>&1 && diff=xxdiff
- fi
- ;;
- -makecheck) # makecheck friendly output
- makecheck=true
- xpand=false
- ;;
- -n) # show me, don't do it
- showme=true
- xpand=false
- ;;
- -o)
- imgopts=true
- xpand=false
- ;;
- -c)
- cachemode=true
- xpand=false
- ;;
- -i)
- aiomode=true
- xpand=false
- ;;
- -T) # deprecated timestamp option
- xpand=false
- ;;
- -v)
- verbose=true
- xpand=false
- ;;
- -d)
- debug=true
- xpand=false
- ;;
- -x) # -x group ... exclude from group file
- xgroup=true
- xpand=false
- ;;
- '[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]')
- echo "No tests?"
- status=1
- exit $status
- ;;
-
- [0-9]*-[0-9]*)
- eval $(echo $r | sed -e 's/^/start=/' -e 's/-/ end=/')
- ;;
-
- [0-9]*-)
- eval $(echo $r | sed -e 's/^/start=/' -e 's/-//')
- end=$(echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //')
- if [ -z "$end" ]
- then
- echo "No tests in range \"$r\"?"
- status=1
- exit $status
- fi
- ;;
-
- *)
- start=$r
- end=$r
- ;;
-
- esac
-
- # get rid of leading 0s as can be interpreted as octal
- start=$(echo $start | sed 's/^0*//')
- end=$(echo $end | sed 's/^0*//')
-
- if $xpand
- then
- have_test_arg=true
- awk </dev/null '
-BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
- | while read id
- do
- if grep -s "^$id\( \|\$\)" "$source_iotests/group" >/dev/null
- then
- # in group file ... OK
- echo $id >>$tmp.list
- else
- if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null
- then
- # expunged ... will be reported, but not run, later
- echo $id >>$tmp.list
- else
- # oops
- if [ "$start" == "$end" -a "$id" == "$end" ]
- then
- echo "$id - unknown test"
- exit 1
- else
- echo "$id - unknown test, ignored"
- fi
- fi
- fi
- done || exit 1
- fi
-
-done
-
-# Set qemu-io cache mode with $CACHEMODE we have
-QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --cache $CACHEMODE"
-# Set qemu-io aio mode with $AIOMODE we have
-QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --aio $AIOMODE"
-
-QEMU_IO_OPTIONS_NO_FMT="$QEMU_IO_OPTIONS"
-if [ "$IMGOPTSSYNTAX" != "true" ]; then
- QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS -f $IMGFMT"
-fi
-
-# Set default options for qemu-img create -o if they were not specified
-if [ "$IMGFMT" == "qcow2" ] && ! (echo "$IMGOPTS" | grep "compat=" > /dev/null); then
- IMGOPTS=$(_optstr_add "$IMGOPTS" "compat=1.1")
-fi
-if [ "$IMGFMT" == "luks" ] && ! (echo "$IMGOPTS" | grep "iter-time=" > /dev/null); then
- IMGOPTS=$(_optstr_add "$IMGOPTS" "iter-time=10")
-fi
-if [ "$IMGFMT" == "vmdk" ] && ! (echo "$IMGOPTS" | grep "zeroed_grain=" > /dev/null); then
- IMGOPTS=$(_optstr_add "$IMGOPTS" "zeroed_grain=on")
-fi
-
-if [ -z "$SAMPLE_IMG_DIR" ]; then
- SAMPLE_IMG_DIR="$source_iotests/sample_images"
-fi
-
-export TEST_DIR
-export SOCK_DIR
-export SAMPLE_IMG_DIR
-
-if [ -s $tmp.list ]
-then
- # found some valid test numbers ... this is good
- :
-else
- if $have_test_arg
- then
- # had test numbers, but none in group file ... do nothing
- touch $tmp.list
- else
- # no test numbers, do everything from group file
- sed -n -e '/^[0-9][0-9][0-9]*/s/^\([0-9]*\).*/\1/p' <"$source_iotests/group" >$tmp.list
- fi
-fi
-
-# should be sort -n, but this did not work for Linux when this
-# was ported from IRIX
-#
-list=$(sort $tmp.list)
-rm -f $tmp.list $tmp.tmp $tmp.sed
-
-if [ -z "$QEMU_PROG" ]
-then
- if [ -x "$build_iotests/qemu" ]; then
- export QEMU_PROG="$build_iotests/qemu"
- elif [ -x "$build_root/qemu-system-${qemu_arch}" ]; then
- export QEMU_PROG="$build_root/qemu-system-${qemu_arch}"
- else
- pushd "$build_root" > /dev/null
- for binary in qemu-system-*
- do
- if [ -x "$binary" ]
- then
- export QEMU_PROG="$build_root/$binary"
- break
- fi
- done
- popd > /dev/null
- [ "$QEMU_PROG" = "" ] && _init_error "qemu not found"
- fi
-fi
-export QEMU_PROG="$(type -p "$QEMU_PROG")"
-
-export QEMU_OPTIONS="-nodefaults -display none -accel qtest"
-case "$QEMU_PROG" in
- *qemu-system-arm|*qemu-system-aarch64)
- export QEMU_OPTIONS="$QEMU_OPTIONS -machine virt"
- ;;
- *qemu-system-avr)
- export QEMU_OPTIONS="$QEMU_OPTIONS -machine mega2560"
- ;;
- *qemu-system-rx)
- export QEMU_OPTIONS="$QEMU_OPTIONS -machine gdbsim-r5f562n8"
- ;;
- *qemu-system-tricore)
- export QEMU_OPTIONS="-$QEMU_OPTIONS -machine tricore_testboard"
- ;;
-esac
-
-if [ -z "$QEMU_IMG_PROG" ]; then
- if [ -x "$build_iotests/qemu-img" ]; then
- export QEMU_IMG_PROG="$build_iotests/qemu-img"
- elif [ -x "$build_root/qemu-img" ]; then
- export QEMU_IMG_PROG="$build_root/qemu-img"
- else
- _init_error "qemu-img not found"
- fi
-fi
-export QEMU_IMG_PROG="$(type -p "$QEMU_IMG_PROG")"
-
-if [ -z "$QEMU_IO_PROG" ]; then
- if [ -x "$build_iotests/qemu-io" ]; then
- export QEMU_IO_PROG="$build_iotests/qemu-io"
- elif [ -x "$build_root/qemu-io" ]; then
- export QEMU_IO_PROG="$build_root/qemu-io"
- else
- _init_error "qemu-io not found"
- fi
-fi
-export QEMU_IO_PROG="$(type -p "$QEMU_IO_PROG")"
-
-if [ -z $QEMU_NBD_PROG ]; then
- if [ -x "$build_iotests/qemu-nbd" ]; then
- export QEMU_NBD_PROG="$build_iotests/qemu-nbd"
- elif [ -x "$build_root/qemu-nbd" ]; then
- export QEMU_NBD_PROG="$build_root/qemu-nbd"
- else
- _init_error "qemu-nbd not found"
- fi
-fi
-export QEMU_NBD_PROG="$(type -p "$QEMU_NBD_PROG")"
-
-if [ -z "$QSD_PROG" ]; then
- if [ -x "$build_iotests/qemu-storage-daemon" ]; then
- export QSD_PROG="$build_iotests/qemu-storage-daemon"
- elif [ -x "$build_root/storage-daemon/qemu-storage-daemon" ]; then
- export QSD_PROG="$build_root/storage-daemon/qemu-storage-daemon"
- else
- _init_error "qemu-storage-daemon not found"
- fi
-fi
-export QSD_PROG="$(type -p "$QSD_PROG")"
-
-if [ -x "$build_iotests/socket_scm_helper" ]
-then
- export SOCKET_SCM_HELPER="$build_iotests/socket_scm_helper"
-fi
-
-python_usable=false
-if $PYTHON -c 'import sys; sys.exit(0 if sys.version_info >= (3,6) else 1)'
-then
- # Our python framework also requires virtio-blk
- if "$QEMU_PROG" -M none -device help | grep -q virtio-blk >/dev/null 2>&1
- then
- python_usable=true
- else
- python_unusable_because="Missing virtio-blk in QEMU binary"
- fi
-else
- python_unusable_because="Unsupported Python version"
-fi
-
-default_machine=$($QEMU_PROG -machine help | sed -n '/(default)/ s/ .*//p')
-default_alias_machine=$($QEMU_PROG -machine help | \
- sed -n "/(alias of $default_machine)/ { s/ .*//p; q; }")
-if [[ "$default_alias_machine" ]]; then
- default_machine="$default_alias_machine"
-fi
-
-export QEMU_DEFAULT_MACHINE="$default_machine"
-
-TIMESTAMP_FILE=check.time-$IMGPROTO-$IMGFMT
-
-_wallclock()
-{
- date "+%H %M %S" | awk '{ print $1*3600 + $2*60 + $3 }'
-}
-
-_wrapup()
-{
- if $showme
- then
- :
- elif $needwrap
- then
- if [ -f $TIMESTAMP_FILE -a -f $tmp.time ]
- then
- cat $TIMESTAMP_FILE $tmp.time \
- | awk '
- { t[$1] = $2 }
-END { if (NR > 0) {
- for (i in t) print i " " t[i]
- }
- }' \
- | sort -n >$tmp.out
- mv $tmp.out $TIMESTAMP_FILE
- fi
-
- if [ -f $tmp.expunged ]
- then
- notrun=$(wc -l <$tmp.expunged | sed -e 's/ *//g')
- try=$(expr $try - $notrun)
- list=$(echo "$list" | sed -f $tmp.expunged)
- fi
-
- echo "" >>check.log
- date >>check.log
- echo $list | fmt | sed -e 's/^/ /' >>check.log
- $interrupt && echo "Interrupted!" >>check.log
-
- if [ ! -z "$notrun" ]
- then
- echo "Not run:$notrun"
- echo "Not run:$notrun" >>check.log
- fi
- if [ ! -z "$casenotrun" ]
- then
- echo "Some cases not run in:$casenotrun"
- echo "Some cases not run in:$casenotrun" >>check.log
- fi
- if [ ! -z "$n_bad" -a $n_bad != 0 ]
- then
- echo "Failures:$bad"
- echo "Failed $n_bad of $try iotests"
- echo "Failures:$bad" | fmt >>check.log
- echo "Failed $n_bad of $try iotests" >>check.log
- else
- echo "Passed all $try iotests"
- echo "Passed all $try iotests" >>check.log
- fi
- needwrap=false
- fi
-
- if test -n "$STTY_RESTORE"; then
- stty $STTY_RESTORE
- fi
- rm -f "${TEST_DIR}"/*.out "${TEST_DIR}"/*.err "${TEST_DIR}"/*.time
- rm -f "${TEST_DIR}"/check.pid "${TEST_DIR}"/check.sts
- rm -f $tmp.*
-
- if $tmp_sock_dir
- then
- rm -rf "$SOCK_DIR"
- fi
-}
-
-trap "_wrapup; exit \$status" 0 1 2 3 15
-
-# Report the test start and results. For makecheck we want to pretty
-# print the whole report at the end of the execution.
-# args: $seq, $starttime, $lasttime
-_report_test_start()
-{
- if ! $makecheck; then
- if [ -n "$3" ]; then
- local lasttime=" (last: $3s)"
- fi
- printf "%-8s %-10s [%s] %4s%-14s\r" "$1" "..." "$2" "..." "$lasttime"
- fi
-}
-# args:$seq $status $starttime $lasttime $thistime $details
-_report_test_result()
-{
- local status lasttime thistime
- if $makecheck; then
- if [ -n "$2" ] && [ "$2" != "pass" ]; then
- status=" [$2]"
- fi
- printf " TEST iotest-$IMGFMT: %s%s\n" "$1" "$status"
- return
- fi
-
- if [ -n "$4" ]; then
- lasttime=" (last: $4s)"
- fi
- if [ -n "$5" ]; then
- thistime=" $5s"
- fi
- case "$2" in
- "pass") status=$(printf "\e[32m%-10s\e[0m" "$2") ;;
- "fail") status=$(printf "\e[1m\e[31m%-10s\e[0m" "$2") ;;
- "not run") status=$(printf "\e[33m%-10s\e[0m" "$2") ;;
- *) status=$(printf "%-10s" "$2") ;;
- esac
-
- printf "%-8s %s [%s] [%s] %4s%-14s %s\n" "$1" "$status" "$3" "$(date '+%T')" "$thistime" "$lasttime" "$6"
-}
-
-[ -f $TIMESTAMP_FILE ] || touch $TIMESTAMP_FILE
-
-FULL_IMGFMT_DETAILS=$(_full_imgfmt_details)
-FULL_HOST_DETAILS=$(_full_platform_details)
-
-if ! $makecheck; then
- _full_env_details
-fi
-
-seq="check"
-
-[ -n "$TESTS_REMAINING_LOG" ] && echo $list > $TESTS_REMAINING_LOG
-
-for seq in $list
-do
- err=false # error flag
- printdiff=false # show diff to reference output?
- status="" # test result summary
- results="" # test result details
- thistime="" # time the test took
-
- if [ -n "$TESTS_REMAINING_LOG" ] ; then
- sed -e "s/$seq//" -e 's/ / /' -e 's/^ *//' $TESTS_REMAINING_LOG > $TESTS_REMAINING_LOG.tmp
- mv $TESTS_REMAINING_LOG.tmp $TESTS_REMAINING_LOG
- sync
- fi
-
- lasttime=$(sed -n -e "/^$seq /s/.* //p" <$TIMESTAMP_FILE)
- starttime=$(date "+%T")
- _report_test_start $seq $starttime $lasttime
-
- if $showme
- then
- status="not run"
- elif [ -f expunged ] && $expunge && egrep "^$seq([ ]|\$)" expunged >/dev/null
- then
- status="not run"
- results="expunged"
- rm -f $seq.out.bad
- echo "/^$seq\$/d" >>$tmp.expunged
- elif [ ! -f "$source_iotests/$seq" ]
- then
- status="not run"
- results="no such test?"
- echo "/^$seq\$/d" >>$tmp.expunged
- else
- # really going to try and run this one
- #
- rm -f $seq.out.bad
- rm -f core $seq.notrun
- rm -f $seq.casenotrun
-
- start=$(_wallclock)
-
- if [ "$(head -n 1 "$source_iotests/$seq")" == "#!/usr/bin/env python3" ]; then
- if $python_usable; then
- run_command="$PYTHON $seq"
- else
- run_command="false"
- echo "$python_unusable_because" > $seq.notrun
- fi
- else
- run_command="./$seq"
- fi
- export OUTPUT_DIR=$PWD
- if $debug; then
- (cd "$source_iotests";
- MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
- $run_command -d 2>&1 | tee $tmp.out)
- else
- (cd "$source_iotests";
- MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
- $run_command >$tmp.out 2>&1)
- fi
- sts=$?
- stop=$(_wallclock)
-
- if [ -f core ]
- then
- mv core $seq.core
- status="fail"
- results="[dumped core] $seq.core"
- err=true
- fi
-
- if [ -f $seq.notrun ]
- then
- # overwrites timestamp output
- status="not run"
- results="$(cat $seq.notrun)"
- else
- if [ $sts -ne 0 ]
- then
- status="fail"
- results=$(printf %s "[failed, exit status $sts]")
- err=true
- fi
-
- reference="$source_iotests/$seq.out"
- reference_machine="$source_iotests/$seq.$QEMU_DEFAULT_MACHINE.out"
- if [ -f "$reference_machine" ]; then
- reference="$reference_machine"
- fi
-
- reference_format="$source_iotests/$seq.out.$IMGFMT"
- if [ -f "$reference_format" ]; then
- reference="$reference_format"
- fi
-
- if [ "$CACHEMODE" = "none" ]; then
- [ -f "$source_iotests/$seq.out.nocache" ] && reference="$source_iotests/$seq.out.nocache"
- fi
-
- if [ ! -f "$reference" ]
- then
- status="fail"
- results="no qualified output"
- err=true
- else
- if diff -w "$reference" $tmp.out >/dev/null 2>&1
- then
- if ! $err; then
- status="pass"
- thistime=$(expr $stop - $start)
- echo "$seq $thistime" >>$tmp.time
- fi
- else
- mv $tmp.out $seq.out.bad
- status="fail"
- results="output mismatch (see $seq.out.bad)"
- printdiff=true
- err=true
- fi
- fi
- fi
- if [ -f $seq.casenotrun ]
- then
- cat $seq.casenotrun
- casenotrun="$casenotrun $seq"
- fi
- fi
-
- # come here for each test, except when $showme is true
- #
- _report_test_result $seq "$status" "$starttime" "$lasttime" "$thistime" "$results"
- case "$status" in
- "pass")
- try=$(expr $try + 1)
- ;;
- "fail")
- try=$(expr $try + 1)
- if $makecheck; then
- _full_env_details
- fi
- if $printdiff; then
- $diff -w "$reference" "$PWD"/$seq.out.bad
- fi
- bad="$bad $seq"
- n_bad=$(expr $n_bad + 1)
- quick=false
- ;;
- "not run")
- notrun="$notrun $seq"
- ;;
- esac
-
- seq="after_$seq"
-done
-interrupt=false
-status=$(expr $n_bad)
-exit
+import os
+import sys
+import argparse
+from findtests import TestFinder
+from testenv import TestEnv
+from testrunner import TestRunner
+
+
+def make_argparser() -> argparse.ArgumentParser:
+ p = argparse.ArgumentParser(description="Test run options")
+
+ p.add_argument('-n', '--dry-run', action='store_true',
+ help='show me, do not run tests')
+ p.add_argument('-makecheck', action='store_true',
+ help='pretty print output for make check')
+
+ p.add_argument('-d', dest='debug', action='store_true', help='debug')
+ p.add_argument('-misalign', action='store_true',
+ help='misalign memory allocations')
+ p.add_argument('--color', choices=['on', 'off', 'auto'],
+ default='auto', help="use terminal colors. The default "
+ "'auto' value means use colors if terminal stdout detected")
+
+ g_env = p.add_argument_group('test environment options')
+ mg = g_env.add_mutually_exclusive_group()
+ # We don't set default for cachemode, as we need to distinguish default
+ # from user input later.
+ mg.add_argument('-nocache', dest='cachemode', action='store_const',
+ const='none', help='set cache mode "none" (O_DIRECT), '
+ 'sets CACHEMODE environment variable')
+ mg.add_argument('-c', dest='cachemode',
+ help='sets CACHEMODE environment variable')
+
+ g_env.add_argument('-i', dest='aiomode', default='threads',
+ help='sets AIOMODE environment variable')
+
+ p.set_defaults(imgfmt='raw', imgproto='file')
+
+ format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2',
+ 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg']
+ g_fmt = p.add_argument_group(
+ ' image format options',
+ 'The following options set the IMGFMT environment variable. '
+ 'At most one choice is allowed, default is "raw"')
+ mg = g_fmt.add_mutually_exclusive_group()
+ for fmt in format_list:
+ mg.add_argument('-' + fmt, dest='imgfmt', action='store_const',
+ const=fmt, help=f'test {fmt}')
+
+ protocol_list = ['file', 'rbd', 'sheepdog', 'nbd', 'ssh', 'nfs',
+ 'fuse']
+ g_prt = p.add_argument_group(
+ ' image protocol options',
+ 'The following options set the IMGPROTO environment variable. '
+ 'At most one choice is allowed, default is "file"')
+ mg = g_prt.add_mutually_exclusive_group()
+ for prt in protocol_list:
+ mg.add_argument('-' + prt, dest='imgproto', action='store_const',
+ const=prt, help=f'test {prt}')
+
+ g_bash = p.add_argument_group('bash tests options',
+ 'The following options are ignored by '
+ 'python tests.')
+ # TODO: make support for the following options in iotests.py
+ g_bash.add_argument('-o', dest='imgopts',
+ help='options to pass to qemu-img create/convert, '
+ 'sets IMGOPTS environment variable')
+ g_bash.add_argument('-valgrind', action='store_true',
+ help='use valgrind, sets VALGRIND_QEMU environment '
+ 'variable')
+
+ g_sel = p.add_argument_group('test selecting options',
+ 'The following options specify test set '
+ 'to run.')
+ g_sel.add_argument('-g', '--groups', metavar='group1,...',
+ help='include tests from these groups')
+ g_sel.add_argument('-x', '--exclude-groups', metavar='group1,...',
+ help='exclude tests from these groups')
+ g_sel.add_argument('--start-from', metavar='TEST',
+ help='Start from specified test: make sorted sequence '
+ 'of tests as usual and then drop tests from the first '
+ 'one to TEST (not inclusive). This may be used to '
+ 'rerun failed ./check command, starting from the '
+ 'middle of the process.')
+ g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*',
+ help='tests to run')
+
+ return p
+
+
+if __name__ == '__main__':
+ args = make_argparser().parse_args()
+
+ env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto,
+ aiomode=args.aiomode, cachemode=args.cachemode,
+ imgopts=args.imgopts, misalign=args.misalign,
+ debug=args.debug, valgrind=args.valgrind)
+
+ testfinder = TestFinder(test_dir=env.source_iotests)
+
+ groups = args.groups.split(',') if args.groups else None
+ x_groups = args.exclude_groups.split(',') if args.exclude_groups else None
+
+ group_local = os.path.join(env.source_iotests, 'group.local')
+ if os.path.isfile(group_local):
+ try:
+ testfinder.add_group_file(group_local)
+ except ValueError as e:
+ sys.exit(f"Failed to parse group file '{group_local}': {e}")
+
+ try:
+ tests = testfinder.find_tests(groups=groups, exclude_groups=x_groups,
+ tests=args.tests,
+ start_from=args.start_from)
+ if not tests:
+ raise ValueError('No tests selected')
+ except ValueError as e:
+ sys.exit(e)
+
+ if args.dry_run:
+ print('\n'.join(tests))
+ else:
+ with TestRunner(env, makecheck=args.makecheck,
+ color=args.color) as tr:
+ tr.run_tests([os.path.join(env.source_iotests, t) for t in tests])
diff --git a/tests/qemu-iotests/common.env.in b/tests/qemu-iotests/common.env.in
deleted file mode 100644
index e565cdf40c..0000000000
--- a/tests/qemu-iotests/common.env.in
+++ /dev/null
@@ -1,3 +0,0 @@
-# Automatically generated by configure - do not modify
-
-export PYTHON='@PYTHON@'
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 29354654cc..297acf9b6a 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -821,9 +821,10 @@ _supported_cache_modes()
# Check whether the filesystem supports O_DIRECT
_check_o_direct()
{
- $QEMU_IMG create -f raw "$TEST_IMG".test_o_direct 1M > /dev/null
- out=$($QEMU_IO -f raw -t none -c quit "$TEST_IMG".test_o_direct 2>&1)
- rm -f "$TEST_IMG".test_o_direct
+ testfile="$TEST_DIR"/_check_o_direct
+ $QEMU_IMG create -f raw "$testfile" 1M > /dev/null
+ out=$($QEMU_IO -f raw -t none -c quit "$testfile" 2>&1)
+ rm -f "$testfile"
[[ "$out" != *"O_DIRECT"* ]]
}
diff --git a/tests/qemu-iotests/findtests.py b/tests/qemu-iotests/findtests.py
new file mode 100644
index 0000000000..dd77b453b8
--- /dev/null
+++ b/tests/qemu-iotests/findtests.py
@@ -0,0 +1,159 @@
+# TestFinder class, define set of tests to run.
+#
+# Copyright (c) 2020-2021 Virtuozzo International GmbH
+#
+# 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/>.
+#
+
+import os
+import glob
+import re
+from collections import defaultdict
+from contextlib import contextmanager
+from typing import Optional, List, Iterator, Set
+
+
+@contextmanager
+def chdir(path: Optional[str] = None) -> Iterator[None]:
+ if path is None:
+ yield
+ return
+
+ saved_dir = os.getcwd()
+ os.chdir(path)
+ try:
+ yield
+ finally:
+ os.chdir(saved_dir)
+
+
+class TestFinder:
+ def __init__(self, test_dir: Optional[str] = None) -> None:
+ self.groups = defaultdict(set)
+
+ with chdir(test_dir):
+ self.all_tests = glob.glob('[0-9][0-9][0-9]')
+ self.all_tests += [f for f in glob.iglob('tests/*')
+ if not f.endswith('.out') and
+ os.path.isfile(f + '.out')]
+
+ for t in self.all_tests:
+ with open(t, encoding="utf-8") as f:
+ for line in f:
+ if line.startswith('# group: '):
+ for g in line.split()[2:]:
+ self.groups[g].add(t)
+ break
+
+ def add_group_file(self, fname: str) -> None:
+ with open(fname, encoding="utf-8") as f:
+ for line in f:
+ line = line.strip()
+
+ if (not line) or line[0] == '#':
+ continue
+
+ words = line.split()
+ test_file = self.parse_test_name(words[0])
+ groups = words[1:]
+
+ for g in groups:
+ self.groups[g].add(test_file)
+
+ def parse_test_name(self, name: str) -> str:
+ if '/' in name:
+ raise ValueError('Paths are unsupported for test selection, '
+ f'requiring "{name}" is wrong')
+
+ if re.fullmatch(r'\d+', name):
+ # Numbered tests are old naming convention. We should convert them
+ # to three-digit-length, like 1 --> 001.
+ name = f'{int(name):03}'
+ else:
+ # Named tests all should be in tests/ subdirectory
+ name = os.path.join('tests', name)
+
+ if name not in self.all_tests:
+ raise ValueError(f'Test "{name}" is not found')
+
+ return name
+
+ def find_tests(self, groups: Optional[List[str]] = None,
+ exclude_groups: Optional[List[str]] = None,
+ tests: Optional[List[str]] = None,
+ start_from: Optional[str] = None) -> List[str]:
+ """Find tests
+
+ Algorithm:
+
+ 1. a. if some @groups specified
+ a.1 Take all tests from @groups
+ a.2 Drop tests, which are in at least one of @exclude_groups or in
+ 'disabled' group (if 'disabled' is not listed in @groups)
+ a.3 Add tests from @tests (don't exclude anything from them)
+
+ b. else, if some @tests specified:
+ b.1 exclude_groups must be not specified, so just take @tests
+
+ c. else (only @exclude_groups list is non-empty):
+ c.1 Take all tests
+ c.2 Drop tests, which are in at least one of @exclude_groups or in
+ 'disabled' group
+
+ 2. sort
+
+ 3. If start_from specified, drop tests from first one to @start_from
+ (not inclusive)
+ """
+ if groups is None:
+ groups = []
+ if exclude_groups is None:
+ exclude_groups = []
+ if tests is None:
+ tests = []
+
+ res: Set[str] = set()
+ if groups:
+ # Some groups specified. exclude_groups supported, additionally
+ # selecting some individual tests supported as well.
+ res.update(*(self.groups[g] for g in groups))
+ elif tests:
+ # Some individual tests specified, but no groups. In this case
+ # we don't support exclude_groups.
+ if exclude_groups:
+ raise ValueError("Can't exclude from individually specified "
+ "tests.")
+ else:
+ # No tests no groups: start from all tests, exclude_groups
+ # supported.
+ res.update(self.all_tests)
+
+ if 'disabled' not in groups and 'disabled' not in exclude_groups:
+ # Don't want to modify function argument, so create new list.
+ exclude_groups = exclude_groups + ['disabled']
+
+ res = res.difference(*(self.groups[g] for g in exclude_groups))
+
+ # We want to add @tests. But for compatibility with old test names,
+ # we should convert any number < 100 to number padded by
+ # leading zeroes, like 1 -> 001 and 23 -> 023.
+ for t in tests:
+ res.add(self.parse_test_name(t))
+
+ sequence = sorted(res)
+
+ if start_from is not None:
+ del sequence[:sequence.index(self.parse_test_name(start_from))]
+
+ return sequence
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
deleted file mode 100644
index bc5bc324fe..0000000000
--- a/tests/qemu-iotests/group
+++ /dev/null
@@ -1,321 +0,0 @@
-#
-# QA groups control file
-# Defines test groups
-#
-# Some notes about the groups:
-#
-# - do not start group names with a digit
-#
-# - quick : Tests in this group should finish within some few seconds.
-#
-# - img : Tests in this group can be used to excercise the qemu-img tool.
-#
-# - auto : Tests in this group are used during "make check" and should be
-# runnable in any case. That means they should run with every QEMU binary
-# (also non-x86), with every QEMU configuration (i.e. must not fail if
-# an optional feature is not compiled in - but reporting a "skip" is ok),
-# work at least with the qcow2 file format, work with all kind of host
-# filesystems and users (e.g. "nobody" or "root") and must not take too
-# much memory and disk space (since CI pipelines tend to fail otherwise).
-#
-
-#
-# test-group association ... one line per test
-#
-001 rw auto quick
-002 rw auto quick
-003 rw auto
-004 rw auto quick
-005 img auto quick
-# 006 was removed, do not reuse
-007 snapshot auto
-008 rw auto quick
-009 rw auto quick
-010 rw auto quick
-011 rw auto quick
-012 auto quick
-013 rw auto
-014 rw
-015 rw snapshot
-# 016 was removed, do not reuse
-017 rw backing auto quick
-018 rw backing auto quick
-019 rw backing auto quick
-020 rw backing auto quick
-021 io auto quick
-022 rw snapshot auto
-023 rw
-024 rw backing auto quick
-025 rw auto quick
-026 rw blkdbg
-027 rw auto quick
-028 rw backing quick
-029 rw auto quick
-030 rw auto backing
-031 rw auto quick
-032 rw auto quick
-033 rw auto quick
-034 rw auto backing quick
-035 rw auto quick
-036 rw auto quick
-037 rw auto backing quick
-038 rw auto backing quick
-039 rw auto quick
-040 rw auto
-041 rw auto backing
-042 rw auto quick
-043 rw auto backing
-044 rw
-045 rw quick
-046 rw auto aio quick
-047 rw auto quick
-048 img auto quick
-049 rw auto
-050 rw auto backing quick
-051 rw
-052 rw auto backing quick
-053 rw auto quick
-054 rw auto quick
-055 rw
-056 rw backing
-057 rw
-058 rw quick
-059 rw quick
-060 rw auto quick
-061 rw auto
-062 rw auto quick
-063 rw auto quick
-064 rw quick
-065 rw quick
-066 rw auto quick
-# 067 was removed, do not reuse
-068 rw quick
-069 rw auto quick
-070 rw quick
-071 rw auto quick
-072 rw auto quick
-073 rw auto quick
-074 rw auto quick
-075 rw quick
-076 io
-077 rw quick
-078 rw quick
-079 rw auto
-080 rw auto
-081 rw quick
-082 rw quick
-083 rw
-084 img quick
-085 rw
-086 rw auto quick
-087 rw quick
-088 rw quick
-089 rw auto quick
-090 rw auto quick
-091 rw migration quick
-092 rw quick
-093 throttle
-094 rw quick
-095 rw quick
-096 rw quick
-097 rw auto backing
-098 rw auto backing quick
-099 rw auto quick
-# 100 was removed, do not reuse
-101 rw quick
-102 rw quick
-103 rw auto quick
-104 rw auto
-105 rw auto quick
-106 rw quick
-107 rw auto quick
-108 rw auto quick
-109 rw
-110 rw auto backing quick
-111 rw auto quick
-112 rw
-113 rw quick
-114 rw auto quick
-115 rw
-116 rw quick
-117 rw auto
-118 rw
-119 rw quick
-120 rw auto quick
-121 rw
-122 rw
-123 rw quick
-124 rw backing
-125 rw
-126 rw auto backing
-127 rw auto backing quick
-128 rw quick
-129 rw quick
-130 rw quick
-131 rw quick
-132 rw quick
-133 auto quick
-134 rw auto quick
-135 rw
-136 rw
-137 rw auto
-138 rw auto quick
-139 rw quick
-140 rw auto quick
-141 rw auto quick
-142
-143 auto quick
-144 rw quick
-145 quick
-146 quick
-147 img
-148 rw quick
-149 rw sudo
-150 rw auto quick
-151 rw
-152 rw quick
-153 rw quick
-154 rw auto backing quick
-155 rw
-156 rw auto quick
-157 quick
-158 rw auto quick
-159 rw auto quick
-160 rw quick
-161 rw auto quick
-162 quick
-163 rw
-165 rw quick
-169 rw migration
-170 rw auto quick
-171 rw quick
-172 auto
-173 rw
-174 auto
-175 quick
-176 rw auto backing
-177 rw auto quick
-178 img
-179 rw auto quick
-181 rw auto migration quick
-182 rw quick
-183 rw migration quick
-184 rw auto quick
-185 rw
-186 rw auto
-187 rw auto
-188 rw quick
-189 rw
-190 rw auto quick
-191 rw auto
-192 rw auto quick
-194 rw migration quick
-195 rw auto quick
-196 rw quick migration
-197 rw quick
-198 rw
-199 rw migration
-200 rw
-201 rw migration quick
-202 rw quick
-203 rw auto migration quick
-204 rw quick
-205 rw quick
-206 rw
-207 rw
-208 rw quick
-209 rw quick
-210 rw
-211 rw quick
-212 rw quick
-213 rw quick
-214 rw auto
-215 rw quick
-216 rw quick
-217 rw auto quick
-218 rw quick
-219 rw
-220 rw auto
-221 rw quick
-222 rw quick
-223 rw quick
-224 rw quick
-225 rw quick
-226 auto quick
-227 quick
-228 rw quick
-229 auto quick
-231 quick
-232 quick
-233 quick
-234 quick migration
-235 quick
-236 quick
-237 rw quick
-238 quick
-239 rw quick
-240 quick
-241 rw quick
-242 rw quick
-243 rw quick
-244 rw auto quick
-245 rw
-246 rw quick
-247 rw quick
-248 rw quick
-249 rw auto quick
-250 rw quick
-251 rw auto quick
-252 rw auto backing quick
-253 rw quick
-254 rw backing quick
-255 rw quick
-256 rw auto quick
-257 rw
-258 rw quick
-259 rw auto quick
-260 rw quick
-261 rw
-262 rw quick migration
-263 rw quick
-264 rw
-265 rw auto quick
-266 rw quick
-267 rw auto quick snapshot
-268 rw auto quick
-270 rw backing quick
-271 rw auto
-272 rw
-273 backing quick
-274 rw backing
-277 rw quick
-279 rw backing quick
-280 rw migration quick
-281 rw quick
-282 rw img quick
-283 auto quick
-284 rw
-286 rw quick
-287 auto quick
-288 quick
-289 rw quick
-290 rw auto quick
-291 rw quick
-292 rw auto quick
-293 rw
-294 rw quick
-295 rw
-296 rw
-297 meta
-298
-299 auto quick
-300 migration
-301 backing quick
-302 quick
-303 rw quick
-304 rw quick
-305 rw quick
-307 rw quick export
-308 rw
-309 rw auto quick
-312 rw quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 2e89c0ab1a..00be68eca3 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -75,12 +75,20 @@ qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
imgfmt = os.environ.get('IMGFMT', 'raw')
imgproto = os.environ.get('IMGPROTO', 'file')
-test_dir = os.environ.get('TEST_DIR')
-sock_dir = os.environ.get('SOCK_DIR')
output_dir = os.environ.get('OUTPUT_DIR', '.')
-cachemode = os.environ.get('CACHEMODE')
-aiomode = os.environ.get('AIOMODE')
-qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE')
+
+try:
+ test_dir = os.environ['TEST_DIR']
+ sock_dir = os.environ['SOCK_DIR']
+ cachemode = os.environ['CACHEMODE']
+ aiomode = os.environ['AIOMODE']
+ qemu_default_machine = os.environ['QEMU_DEFAULT_MACHINE']
+except KeyError:
+ # We are using these variables as proxies to indicate that we're
+ # not being run via "check". There may be other things set up by
+ # "check" that individual test cases rely on.
+ sys.stderr.write('Please run this test via the "check" script\n')
+ sys.exit(os.EX_USAGE)
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
@@ -507,12 +515,15 @@ class FilePath:
return False
+def try_remove(img):
+ try:
+ os.remove(img)
+ except OSError:
+ pass
+
def file_path_remover():
for path in reversed(file_path_remover.paths):
- try:
- os.remove(path)
- except OSError:
- pass
+ try_remove(path)
def file_path(*names, base_dir=test_dir):
@@ -1120,6 +1131,13 @@ def _verify_formats(required_formats: Sequence[str] = ()) -> None:
if usf_list:
notrun(f'formats {usf_list} are not whitelisted')
+
+def _verify_virtio_blk() -> None:
+ out = qemu_pipe('-M', 'none', '-device', 'help')
+ if 'virtio-blk' not in out:
+ notrun('Missing virtio-blk in QEMU binary')
+
+
def supports_quorum():
return 'quorum' in qemu_img_pipe('--help')
@@ -1286,14 +1304,6 @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
"""
# Note: Python 3.6 and pylint do not like 'Collection' so use 'Sequence'.
- # We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
- # indicate that we're not being run via "check". There may be
- # other things set up by "check" that individual test cases rely
- # on.
- if test_dir is None or qemu_default_machine is None:
- sys.stderr.write('Please run this test via the "check" script\n')
- sys.exit(os.EX_USAGE)
-
debug = '-d' in sys.argv
if debug:
sys.argv.remove('-d')
@@ -1305,6 +1315,7 @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
_verify_cache_mode(supported_cache_modes)
_verify_aio_mode(supported_aio_modes)
_verify_formats(required_fmts)
+ _verify_virtio_blk()
return debug
diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build
index 26658ce25c..67aed1e492 100644
--- a/tests/qemu-iotests/meson.build
+++ b/tests/qemu-iotests/meson.build
@@ -3,6 +3,3 @@ if 'CONFIG_LINUX' in config_host
else
socket_scm_helper = []
endif
-configure_file(output: 'common.env',
- input: files('common.env.in'),
- configuration: {'PYTHON': python.full_path()})
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py
new file mode 100644
index 0000000000..b31275f518
--- /dev/null
+++ b/tests/qemu-iotests/testenv.py
@@ -0,0 +1,281 @@
+# TestEnv class to manage test environment variables.
+#
+# Copyright (c) 2020-2021 Virtuozzo International GmbH
+#
+# 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/>.
+#
+
+import os
+import sys
+import tempfile
+from pathlib import Path
+import shutil
+import collections
+import random
+import subprocess
+import glob
+from typing import Dict, Any, Optional, ContextManager
+
+
+def isxfile(path: str) -> bool:
+ return os.path.isfile(path) and os.access(path, os.X_OK)
+
+
+def get_default_machine(qemu_prog: str) -> str:
+ outp = subprocess.run([qemu_prog, '-machine', 'help'], check=True,
+ universal_newlines=True,
+ stdout=subprocess.PIPE).stdout
+
+ machines = outp.split('\n')
+ try:
+ default_machine = next(m for m in machines if m.endswith(' (default)'))
+ except StopIteration:
+ return ''
+ default_machine = default_machine.split(' ', 1)[0]
+
+ alias_suf = ' (alias of {})'.format(default_machine)
+ alias = next((m for m in machines if m.endswith(alias_suf)), None)
+ if alias is not None:
+ default_machine = alias.split(' ', 1)[0]
+
+ return default_machine
+
+
+class TestEnv(ContextManager['TestEnv']):
+ """
+ Manage system environment for running tests
+
+ The following variables are supported/provided. They are represented by
+ lower-cased TestEnv attributes.
+ """
+
+ # We store environment variables as instance attributes, and there are a
+ # lot of them. Silence pylint:
+ # pylint: disable=too-many-instance-attributes
+
+ env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR',
+ 'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG',
+ 'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG',
+ 'SOCKET_SCM_HELPER', 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS',
+ 'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT',
+ 'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
+ 'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
+ 'CACHEMODE_IS_DEFAULT', 'IMGFMT_GENERIC', 'IMGOPTSSYNTAX',
+ 'IMGKEYSECRET', 'QEMU_DEFAULT_MACHINE', 'MALLOC_PERTURB_']
+
+ def get_env(self) -> Dict[str, str]:
+ env = {}
+ for v in self.env_variables:
+ val = getattr(self, v.lower(), None)
+ if val is not None:
+ env[v] = val
+
+ return env
+
+ def init_directories(self) -> None:
+ """Init directory variables:
+ PYTHONPATH
+ TEST_DIR
+ SOCK_DIR
+ SAMPLE_IMG_DIR
+ OUTPUT_DIR
+ """
+ self.pythonpath = os.getenv('PYTHONPATH')
+ if self.pythonpath:
+ self.pythonpath = self.source_iotests + os.pathsep + \
+ self.pythonpath
+ else:
+ self.pythonpath = self.source_iotests
+
+ self.test_dir = os.getenv('TEST_DIR',
+ os.path.join(os.getcwd(), 'scratch'))
+ Path(self.test_dir).mkdir(parents=True, exist_ok=True)
+
+ try:
+ self.sock_dir = os.environ['SOCK_DIR']
+ self.tmp_sock_dir = False
+ Path(self.test_dir).mkdir(parents=True, exist_ok=True)
+ except KeyError:
+ self.sock_dir = tempfile.mkdtemp()
+ self.tmp_sock_dir = True
+
+ self.sample_img_dir = os.getenv('SAMPLE_IMG_DIR',
+ os.path.join(self.source_iotests,
+ 'sample_images'))
+
+ self.output_dir = os.getcwd() # OUTPUT_DIR
+
+ def init_binaries(self) -> None:
+ """Init binary path variables:
+ PYTHON (for bash tests)
+ QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG
+ SOCKET_SCM_HELPER
+ """
+ self.python = sys.executable
+
+ def root(*names: str) -> str:
+ return os.path.join(self.build_root, *names)
+
+ arch = os.uname().machine
+ if 'ppc64' in arch:
+ arch = 'ppc64'
+
+ self.qemu_prog = os.getenv('QEMU_PROG', root(f'qemu-system-{arch}'))
+ if not os.path.exists(self.qemu_prog):
+ pattern = root('qemu-system-*')
+ try:
+ progs = glob.iglob(pattern)
+ self.qemu_prog = next(p for p in progs if isxfile(p))
+ except StopIteration:
+ sys.exit("Not found any Qemu executable binary by pattern "
+ f"'{pattern}'")
+
+ self.qemu_img_prog = os.getenv('QEMU_IMG_PROG', root('qemu-img'))
+ self.qemu_io_prog = os.getenv('QEMU_IO_PROG', root('qemu-io'))
+ self.qemu_nbd_prog = os.getenv('QEMU_NBD_PROG', root('qemu-nbd'))
+ self.qsd_prog = os.getenv('QSD_PROG', root('storage-daemon',
+ 'qemu-storage-daemon'))
+
+ for b in [self.qemu_img_prog, self.qemu_io_prog, self.qemu_nbd_prog,
+ self.qemu_prog, self.qsd_prog]:
+ if not os.path.exists(b):
+ sys.exit('No such file: ' + b)
+ if not isxfile(b):
+ sys.exit('Not executable: ' + b)
+
+ helper_path = os.path.join(self.build_iotests, 'socket_scm_helper')
+ if isxfile(helper_path):
+ self.socket_scm_helper = helper_path # SOCKET_SCM_HELPER
+
+ def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
+ cachemode: Optional[str] = None,
+ imgopts: Optional[str] = None,
+ misalign: bool = False,
+ debug: bool = False,
+ valgrind: bool = False) -> None:
+ self.imgfmt = imgfmt
+ self.imgproto = imgproto
+ self.aiomode = aiomode
+ self.imgopts = imgopts
+ self.misalign = misalign
+ self.debug = debug
+
+ if valgrind:
+ self.valgrind_qemu = 'y'
+
+ if cachemode is None:
+ self.cachemode_is_default = 'true'
+ self.cachemode = 'writeback'
+ else:
+ self.cachemode_is_default = 'false'
+ self.cachemode = cachemode
+
+ # Initialize generic paths: build_root, build_iotests, source_iotests,
+ # which are needed to initialize some environment variables. They are
+ # used by init_*() functions as well.
+
+ if os.path.islink(sys.argv[0]):
+ # called from the build tree
+ self.source_iotests = os.path.dirname(os.readlink(sys.argv[0]))
+ self.build_iotests = os.path.dirname(os.path.abspath(sys.argv[0]))
+ else:
+ # called from the source tree
+ self.source_iotests = os.getcwd()
+ self.build_iotests = self.source_iotests
+
+ self.build_root = os.path.join(self.build_iotests, '..', '..')
+
+ self.init_directories()
+ self.init_binaries()
+
+ self.malloc_perturb_ = os.getenv('MALLOC_PERTURB_',
+ str(random.randrange(1, 255)))
+
+ # QEMU_OPTIONS
+ self.qemu_options = '-nodefaults -display none -accel qtest'
+ machine_map = (
+ ('arm', 'virt'),
+ ('aarch64', 'virt'),
+ ('avr', 'mega2560'),
+ ('rx', 'gdbsim-r5f562n8'),
+ ('tricore', 'tricore_testboard')
+ )
+ for suffix, machine in machine_map:
+ if self.qemu_prog.endswith(f'qemu-system-{suffix}'):
+ self.qemu_options += f' -machine {machine}'
+
+ # QEMU_DEFAULT_MACHINE
+ self.qemu_default_machine = get_default_machine(self.qemu_prog)
+
+ self.qemu_img_options = os.getenv('QEMU_IMG_OPTIONS')
+ self.qemu_nbd_options = os.getenv('QEMU_NBD_OPTIONS')
+
+ is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg']
+ self.imgfmt_generic = 'true' if is_generic else 'false'
+
+ self.qemu_io_options = f'--cache {self.cachemode} --aio {self.aiomode}'
+ if self.misalign:
+ self.qemu_io_options += ' --misalign'
+
+ self.qemu_io_options_no_fmt = self.qemu_io_options
+
+ if self.imgfmt == 'luks':
+ self.imgoptssyntax = 'true'
+ self.imgkeysecret = '123456'
+ if not self.imgopts:
+ self.imgopts = 'iter-time=10'
+ elif 'iter-time=' not in self.imgopts:
+ self.imgopts += ',iter-time=10'
+ else:
+ self.imgoptssyntax = 'false'
+ self.qemu_io_options += ' -f ' + self.imgfmt
+
+ if self.imgfmt == 'vmdk':
+ if not self.imgopts:
+ self.imgopts = 'zeroed_grain=on'
+ elif 'zeroed_grain=' not in self.imgopts:
+ self.imgopts += ',zeroed_grain=on'
+
+ def close(self) -> None:
+ if self.tmp_sock_dir:
+ shutil.rmtree(self.sock_dir)
+
+ def __enter__(self) -> 'TestEnv':
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
+ self.close()
+
+ def print_env(self) -> None:
+ template = """\
+QEMU -- "{QEMU_PROG}" {QEMU_OPTIONS}
+QEMU_IMG -- "{QEMU_IMG_PROG}" {QEMU_IMG_OPTIONS}
+QEMU_IO -- "{QEMU_IO_PROG}" {QEMU_IO_OPTIONS}
+QEMU_NBD -- "{QEMU_NBD_PROG}" {QEMU_NBD_OPTIONS}
+IMGFMT -- {IMGFMT}{imgopts}
+IMGPROTO -- {IMGPROTO}
+PLATFORM -- {platform}
+TEST_DIR -- {TEST_DIR}
+SOCK_DIR -- {SOCK_DIR}
+SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}"""
+
+ args = collections.defaultdict(str, self.get_env())
+
+ if 'IMGOPTS' in args:
+ args['imgopts'] = f" ({args['IMGOPTS']})"
+
+ u = os.uname()
+ args['platform'] = f'{u.sysname}/{u.machine} {u.nodename} {u.release}'
+
+ print(template.format_map(args))
diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py
new file mode 100644
index 0000000000..a581be6a29
--- /dev/null
+++ b/tests/qemu-iotests/testrunner.py
@@ -0,0 +1,367 @@
+# Class for actually running tests.
+#
+# Copyright (c) 2020-2021 Virtuozzo International GmbH
+#
+# 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/>.
+#
+
+import os
+from pathlib import Path
+import datetime
+import time
+import difflib
+import subprocess
+import contextlib
+import json
+import termios
+import sys
+from contextlib import contextmanager
+from typing import List, Optional, Iterator, Any, Sequence, Dict, \
+ ContextManager
+
+from testenv import TestEnv
+
+
+def silent_unlink(path: Path) -> None:
+ try:
+ path.unlink()
+ except OSError:
+ pass
+
+
+def file_diff(file1: str, file2: str) -> List[str]:
+ with open(file1, encoding="utf-8") as f1, \
+ open(file2, encoding="utf-8") as f2:
+ # We want to ignore spaces at line ends. There are a lot of mess about
+ # it in iotests.
+ # TODO: fix all tests to not produce extra spaces, fix all .out files
+ # and use strict diff here!
+ seq1 = [line.rstrip() for line in f1]
+ seq2 = [line.rstrip() for line in f2]
+ res = [line.rstrip()
+ for line in difflib.unified_diff(seq1, seq2, file1, file2)]
+ return res
+
+
+# We want to save current tty settings during test run,
+# since an aborting qemu call may leave things screwed up.
+@contextmanager
+def savetty() -> Iterator[None]:
+ isterm = sys.stdin.isatty()
+ if isterm:
+ fd = sys.stdin.fileno()
+ attr = termios.tcgetattr(fd)
+
+ try:
+ yield
+ finally:
+ if isterm:
+ termios.tcsetattr(fd, termios.TCSADRAIN, attr)
+
+
+class LastElapsedTime(ContextManager['LastElapsedTime']):
+ """ Cache for elapsed time for tests, to show it during new test run
+
+ It is safe to use get() at any time. To use update(), you must either
+ use it inside with-block or use save() after update().
+ """
+ def __init__(self, cache_file: str, env: TestEnv) -> None:
+ self.env = env
+ self.cache_file = cache_file
+ self.cache: Dict[str, Dict[str, Dict[str, float]]]
+
+ try:
+ with open(cache_file, encoding="utf-8") as f:
+ self.cache = json.load(f)
+ except (OSError, ValueError):
+ self.cache = {}
+
+ def get(self, test: str,
+ default: Optional[float] = None) -> Optional[float]:
+ if test not in self.cache:
+ return default
+
+ if self.env.imgproto not in self.cache[test]:
+ return default
+
+ return self.cache[test][self.env.imgproto].get(self.env.imgfmt,
+ default)
+
+ def update(self, test: str, elapsed: float) -> None:
+ d = self.cache.setdefault(test, {})
+ d.setdefault(self.env.imgproto, {})[self.env.imgfmt] = elapsed
+
+ def save(self) -> None:
+ with open(self.cache_file, 'w', encoding="utf-8") as f:
+ json.dump(self.cache, f)
+
+ def __enter__(self) -> 'LastElapsedTime':
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
+ self.save()
+
+
+class TestResult:
+ def __init__(self, status: str, description: str = '',
+ elapsed: Optional[float] = None, diff: Sequence[str] = (),
+ casenotrun: str = '', interrupted: bool = False) -> None:
+ self.status = status
+ self.description = description
+ self.elapsed = elapsed
+ self.diff = diff
+ self.casenotrun = casenotrun
+ self.interrupted = interrupted
+
+
+class TestRunner(ContextManager['TestRunner']):
+ def __init__(self, env: TestEnv, makecheck: bool = False,
+ color: str = 'auto') -> None:
+ self.env = env
+ self.test_run_env = self.env.get_env()
+ self.makecheck = makecheck
+ self.last_elapsed = LastElapsedTime('.last-elapsed-cache', env)
+
+ assert color in ('auto', 'on', 'off')
+ self.color = (color == 'on') or (color == 'auto' and
+ sys.stdout.isatty())
+
+ self._stack: contextlib.ExitStack
+
+ def __enter__(self) -> 'TestRunner':
+ self._stack = contextlib.ExitStack()
+ self._stack.enter_context(self.env)
+ self._stack.enter_context(self.last_elapsed)
+ self._stack.enter_context(savetty())
+ return self
+
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
+ self._stack.close()
+
+ def test_print_one_line(self, test: str, starttime: str,
+ endtime: Optional[str] = None, status: str = '...',
+ lasttime: Optional[float] = None,
+ thistime: Optional[float] = None,
+ description: str = '',
+ test_field_width: Optional[int] = None,
+ end: str = '\n') -> None:
+ """ Print short test info before/after test run """
+ test = os.path.basename(test)
+
+ if test_field_width is None:
+ test_field_width = 8
+
+ if self.makecheck and status != '...':
+ if status and status != 'pass':
+ status = f' [{status}]'
+ else:
+ status = ''
+
+ print(f' TEST iotest-{self.env.imgfmt}: {test}{status}')
+ return
+
+ if lasttime:
+ lasttime_s = f' (last: {lasttime:.1f}s)'
+ else:
+ lasttime_s = ''
+ if thistime:
+ thistime_s = f'{thistime:.1f}s'
+ else:
+ thistime_s = '...'
+
+ if endtime:
+ endtime = f'[{endtime}]'
+ else:
+ endtime = ''
+
+ if self.color:
+ if status == 'pass':
+ col = '\033[32m'
+ elif status == 'fail':
+ col = '\033[1m\033[31m'
+ elif status == 'not run':
+ col = '\033[33m'
+ else:
+ col = ''
+
+ col_end = '\033[0m'
+ else:
+ col = ''
+ col_end = ''
+
+ print(f'{test:{test_field_width}} {col}{status:10}{col_end} '
+ f'[{starttime}] {endtime:13}{thistime_s:5} {lasttime_s:14} '
+ f'{description}', end=end)
+
+ def find_reference(self, test: str) -> str:
+ if self.env.cachemode == 'none':
+ ref = f'{test}.out.nocache'
+ if os.path.isfile(ref):
+ return ref
+
+ ref = f'{test}.out.{self.env.imgfmt}'
+ if os.path.isfile(ref):
+ return ref
+
+ ref = f'{test}.{self.env.qemu_default_machine}.out'
+ if os.path.isfile(ref):
+ return ref
+
+ return f'{test}.out'
+
+ def do_run_test(self, test: str) -> TestResult:
+ f_test = Path(test)
+ f_bad = Path(f_test.name + '.out.bad')
+ f_notrun = Path(f_test.name + '.notrun')
+ f_casenotrun = Path(f_test.name + '.casenotrun')
+ f_reference = Path(self.find_reference(test))
+
+ if not f_test.exists():
+ return TestResult(status='fail',
+ description=f'No such test file: {f_test}')
+
+ if not os.access(str(f_test), os.X_OK):
+ sys.exit(f'Not executable: {f_test}')
+
+ if not f_reference.exists():
+ return TestResult(status='not run',
+ description='No qualified output '
+ f'(expected {f_reference})')
+
+ for p in (f_bad, f_notrun, f_casenotrun):
+ silent_unlink(p)
+
+ args = [str(f_test.resolve())]
+ if self.env.debug:
+ args.append('-d')
+
+ with f_test.open(encoding="utf-8") as f:
+ try:
+ if f.readline() == '#!/usr/bin/env python3':
+ args.insert(0, self.env.python)
+ except UnicodeDecodeError: # binary test? for future.
+ pass
+
+ env = os.environ.copy()
+ env.update(self.test_run_env)
+
+ t0 = time.time()
+ with f_bad.open('w', encoding="utf-8") as f:
+ proc = subprocess.Popen(args, cwd=str(f_test.parent), env=env,
+ stdout=f, stderr=subprocess.STDOUT)
+ try:
+ proc.wait()
+ except KeyboardInterrupt:
+ proc.terminate()
+ proc.wait()
+ return TestResult(status='not run',
+ description='Interrupted by user',
+ interrupted=True)
+ ret = proc.returncode
+
+ elapsed = round(time.time() - t0, 1)
+
+ if ret != 0:
+ return TestResult(status='fail', elapsed=elapsed,
+ description=f'failed, exit status {ret}',
+ diff=file_diff(str(f_reference), str(f_bad)))
+
+ if f_notrun.exists():
+ return TestResult(status='not run',
+ description=f_notrun.read_text().strip())
+
+ casenotrun = ''
+ if f_casenotrun.exists():
+ casenotrun = f_casenotrun.read_text()
+
+ diff = file_diff(str(f_reference), str(f_bad))
+ if diff:
+ return TestResult(status='fail', elapsed=elapsed,
+ description=f'output mismatch (see {f_bad})',
+ diff=diff, casenotrun=casenotrun)
+ else:
+ f_bad.unlink()
+ self.last_elapsed.update(test, elapsed)
+ return TestResult(status='pass', elapsed=elapsed,
+ casenotrun=casenotrun)
+
+ def run_test(self, test: str,
+ test_field_width: Optional[int] = None) -> TestResult:
+ last_el = self.last_elapsed.get(test)
+ start = datetime.datetime.now().strftime('%H:%M:%S')
+
+ self.test_print_one_line(test=test, starttime=start, lasttime=last_el,
+ end='\r', test_field_width=test_field_width)
+
+ res = self.do_run_test(test)
+
+ end = datetime.datetime.now().strftime('%H:%M:%S')
+ self.test_print_one_line(test=test, status=res.status,
+ starttime=start, endtime=end,
+ lasttime=last_el, thistime=res.elapsed,
+ description=res.description,
+ test_field_width=test_field_width)
+
+ if res.casenotrun:
+ print(res.casenotrun)
+
+ return res
+
+ def run_tests(self, tests: List[str]) -> None:
+ n_run = 0
+ failed = []
+ notrun = []
+ casenotrun = []
+
+ if not self.makecheck:
+ self.env.print_env()
+ print()
+
+ test_field_width = max(len(os.path.basename(t)) for t in tests) + 2
+
+ for t in tests:
+ name = os.path.basename(t)
+ res = self.run_test(t, test_field_width=test_field_width)
+
+ assert res.status in ('pass', 'fail', 'not run')
+
+ if res.casenotrun:
+ casenotrun.append(t)
+
+ if res.status != 'not run':
+ n_run += 1
+
+ if res.status == 'fail':
+ failed.append(name)
+ if self.makecheck:
+ self.env.print_env()
+ if res.diff:
+ print('\n'.join(res.diff))
+ elif res.status == 'not run':
+ notrun.append(name)
+
+ if res.interrupted:
+ break
+
+ if notrun:
+ print('Not run:', ' '.join(notrun))
+
+ if casenotrun:
+ print('Some cases not run in:', ' '.join(casenotrun))
+
+ if failed:
+ print('Failures:', ' '.join(failed))
+ print(f'Failed {len(failed)} of {n_run} iotests')
+ else:
+ print(f'Passed all {n_run} iotests')
diff --git a/tests/qemu-iotests/199 b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
index dbf10e58d3..dbf10e58d3 100755
--- a/tests/qemu-iotests/199
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test
diff --git a/tests/qemu-iotests/199.out b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test.out
index 8d7e996700..8d7e996700 100644
--- a/tests/qemu-iotests/199.out
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test.out
diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/tests/migrate-bitmaps-test
index a5c7bc83e0..a5c7bc83e0 100755
--- a/tests/qemu-iotests/169
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-test
diff --git a/tests/qemu-iotests/169.out b/tests/qemu-iotests/tests/migrate-bitmaps-test.out
index cafb8161f7..cafb8161f7 100644
--- a/tests/qemu-iotests/169.out
+++ b/tests/qemu-iotests/tests/migrate-bitmaps-test.out
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 5249a628cc..fd043b0570 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -503,7 +503,7 @@ static GString *qtest_client_socket_recv_line(QTestState *s)
return line;
}
-static gchar **qtest_rsp(QTestState *s, int expected_args)
+static gchar **qtest_rsp_args(QTestState *s, int expected_args)
{
GString *line;
gchar **words;
@@ -539,25 +539,27 @@ redo:
g_assert(words[0] != NULL);
g_assert_cmpstr(words[0], ==, "OK");
- if (expected_args) {
- for (i = 0; i < expected_args; i++) {
- g_assert(words[i] != NULL);
- }
- } else {
- g_strfreev(words);
- words = NULL;
+ for (i = 0; i < expected_args; i++) {
+ g_assert(words[i] != NULL);
}
return words;
}
+static void qtest_rsp(QTestState *s)
+{
+ gchar **words = qtest_rsp_args(s, 0);
+
+ g_strfreev(words);
+}
+
static int qtest_query_target_endianness(QTestState *s)
{
gchar **args;
int big_endian;
qtest_sendf(s, "endianness\n");
- args = qtest_rsp(s, 1);
+ args = qtest_rsp_args(s, 1);
g_assert(strcmp(args[1], "big") == 0 || strcmp(args[1], "little") == 0);
big_endian = strcmp(args[1], "big") == 0;
g_strfreev(args);
@@ -892,14 +894,14 @@ bool qtest_get_irq(QTestState *s, int num)
void qtest_module_load(QTestState *s, const char *prefix, const char *libname)
{
qtest_sendf(s, "module_load %s %s\n", prefix, libname);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
static int64_t qtest_clock_rsp(QTestState *s)
{
gchar **words;
int64_t clock;
- words = qtest_rsp(s, 2);
+ words = qtest_rsp_args(s, 2);
clock = g_ascii_strtoll(words[1], NULL, 0);
g_strfreev(words);
return clock;
@@ -926,13 +928,13 @@ int64_t qtest_clock_set(QTestState *s, int64_t val)
void qtest_irq_intercept_out(QTestState *s, const char *qom_path)
{
qtest_sendf(s, "irq_intercept_out %s\n", qom_path);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
void qtest_irq_intercept_in(QTestState *s, const char *qom_path)
{
qtest_sendf(s, "irq_intercept_in %s\n", qom_path);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
void qtest_set_irq_in(QTestState *s, const char *qom_path, const char *name,
@@ -942,13 +944,13 @@ void qtest_set_irq_in(QTestState *s, const char *qom_path, const char *name,
name = "unnamed-gpio-in";
}
qtest_sendf(s, "set_irq_in %s %s %d %d\n", qom_path, name, num, level);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value)
{
qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
void qtest_outb(QTestState *s, uint16_t addr, uint8_t value)
@@ -973,7 +975,7 @@ static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr)
unsigned long value;
qtest_sendf(s, "%s 0x%x\n", cmd, addr);
- args = qtest_rsp(s, 2);
+ args = qtest_rsp_args(s, 2);
ret = qemu_strtoul(args[1], NULL, 0, &value);
g_assert(!ret && value <= UINT32_MAX);
g_strfreev(args);
@@ -1000,7 +1002,7 @@ static void qtest_write(QTestState *s, const char *cmd, uint64_t addr,
uint64_t value)
{
qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n", cmd, addr, value);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value)
@@ -1030,7 +1032,7 @@ static uint64_t qtest_read(QTestState *s, const char *cmd, uint64_t addr)
uint64_t value;
qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr);
- args = qtest_rsp(s, 2);
+ args = qtest_rsp_args(s, 2);
ret = qemu_strtou64(args[1], NULL, 0, &value);
g_assert(!ret);
g_strfreev(args);
@@ -1082,7 +1084,7 @@ void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size)
}
qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size);
- args = qtest_rsp(s, 2);
+ args = qtest_rsp_args(s, 2);
for (i = 0; i < size; i++) {
ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4;
@@ -1098,7 +1100,7 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name,
{
qtest_sendf(s, "rtas %s %u 0x%"PRIx64" %u 0x%"PRIx64"\n",
name, nargs, args, nret, ret);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
return 0;
}
@@ -1134,7 +1136,7 @@ void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
s->ops.send(s, bdata);
s->ops.send(s, "\n");
- qtest_rsp(s, 0);
+ qtest_rsp(s);
g_free(bdata);
}
@@ -1144,7 +1146,7 @@ void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size)
size_t len;
qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size);
- args = qtest_rsp(s, 2);
+ args = qtest_rsp_args(s, 2);
g_base64_decode_inplace(args[1], &len);
if (size != len) {
@@ -1174,14 +1176,14 @@ void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
}
qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
g_free(enc);
}
void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
{
qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n", addr, size, pattern);
- qtest_rsp(s, 0);
+ qtest_rsp(s);
}
void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...)
diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c
index 0415e75876..1b7ecc1c8f 100644
--- a/tests/qtest/virtio-scsi-test.c
+++ b/tests/qtest/virtio-scsi-test.c
@@ -200,6 +200,32 @@ static void test_unaligned_write_same(void *obj, void *data,
qvirtio_scsi_pci_free(vs);
}
+static void test_write_to_cdrom(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ QVirtioSCSI *scsi = obj;
+ QVirtioSCSIQueues *vs;
+ uint8_t buf[2048] = { 0 };
+ const uint8_t write_cdb[VIRTIO_SCSI_CDB_SIZE] = {
+ /* WRITE(10) to LBA 0, transfer length 1 */
+ 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
+ };
+ struct virtio_scsi_cmd_resp resp;
+
+ alloc = t_alloc;
+ vs = qvirtio_scsi_init(scsi->vdev);
+
+ virtio_scsi_do_command(vs, write_cdb, NULL, 0, buf, 2048, &resp);
+ g_assert_cmphex(resp.response, ==, 0);
+ g_assert_cmphex(resp.status, ==, CHECK_CONDITION);
+ g_assert_cmphex(resp.sense[0], ==, 0x70);
+ g_assert_cmphex(resp.sense[2], ==, DATA_PROTECT);
+ g_assert_cmphex(resp.sense[12], ==, 0x27); /* WRITE PROTECTED */
+ g_assert_cmphex(resp.sense[13], ==, 0x00); /* WRITE PROTECTED */
+
+ qvirtio_scsi_pci_free(vs);
+}
+
static void test_iothread_attach_node(void *obj, void *data,
QGuestAllocator *t_alloc)
{
@@ -267,6 +293,16 @@ static void *virtio_scsi_setup(GString *cmd_line, void *arg)
return arg;
}
+static void *virtio_scsi_setup_cd(GString *cmd_line, void *arg)
+{
+ g_string_append(cmd_line,
+ " -drive file=null-co://,"
+ "file.read-zeroes=on,"
+ "if=none,id=dr1,format=raw "
+ "-device scsi-cd,drive=dr1,lun=0,scsi-id=1");
+ return arg;
+}
+
static void *virtio_scsi_setup_iothread(GString *cmd_line, void *arg)
{
g_string_append(cmd_line,
@@ -287,6 +323,9 @@ static void register_virtio_scsi_test(void)
qos_add_test("unaligned-write-same", "virtio-scsi",
test_unaligned_write_same, &opts);
+ opts.before = virtio_scsi_setup_cd;
+ qos_add_test("write-to-cdrom", "virtio-scsi", test_write_to_cdrom, &opts);
+
opts.before = virtio_scsi_setup_iothread;
opts.edge = (QOSGraphEdgeOptions) {
.extra_device_opts = "iothread=thread0",
diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c
index b20ab8b29b..9dc1e075e7 100644
--- a/tests/test-qobject-output-visitor.c
+++ b/tests/test-qobject-output-visitor.c
@@ -442,122 +442,86 @@ static void init_list_union(UserDefListUnion *cvalue)
int i;
switch (cvalue->type) {
case USER_DEF_LIST_UNION_KIND_INTEGER: {
- intList **list = &cvalue->u.integer.data;
+ intList **tail = &cvalue->u.integer.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(intList, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_S8: {
- int8List **list = &cvalue->u.s8.data;
+ int8List **tail = &cvalue->u.s8.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(int8List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_S16: {
- int16List **list = &cvalue->u.s16.data;
+ int16List **tail = &cvalue->u.s16.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(int16List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_S32: {
- int32List **list = &cvalue->u.s32.data;
+ int32List **tail = &cvalue->u.s32.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(int32List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_S64: {
- int64List **list = &cvalue->u.s64.data;
+ int64List **tail = &cvalue->u.s64.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(int64List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_U8: {
- uint8List **list = &cvalue->u.u8.data;
+ uint8List **tail = &cvalue->u.u8.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(uint8List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_U16: {
- uint16List **list = &cvalue->u.u16.data;
+ uint16List **tail = &cvalue->u.u16.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(uint16List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_U32: {
- uint32List **list = &cvalue->u.u32.data;
+ uint32List **tail = &cvalue->u.u32.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(uint32List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_U64: {
- uint64List **list = &cvalue->u.u64.data;
+ uint64List **tail = &cvalue->u.u64.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(uint64List, 1);
- (*list)->value = i;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, i);
}
break;
}
case USER_DEF_LIST_UNION_KIND_BOOLEAN: {
- boolList **list = &cvalue->u.boolean.data;
+ boolList **tail = &cvalue->u.boolean.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(boolList, 1);
- (*list)->value = QEMU_IS_ALIGNED(i, 3);
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, QEMU_IS_ALIGNED(i, 3));
}
break;
}
case USER_DEF_LIST_UNION_KIND_STRING: {
- strList **list = &cvalue->u.string.data;
+ strList **tail = &cvalue->u.string.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(strList, 1);
- (*list)->value = g_strdup_printf("%d", i);
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, g_strdup_printf("%d", i));
}
break;
}
case USER_DEF_LIST_UNION_KIND_NUMBER: {
- numberList **list = &cvalue->u.number.data;
+ numberList **tail = &cvalue->u.number.data;
for (i = 0; i < 32; i++) {
- *list = g_new0(numberList, 1);
- (*list)->value = (double)i / 3;
- (*list)->next = NULL;
- list = &(*list)->next;
+ QAPI_LIST_APPEND(tail, (double)i / 3);
}
break;
}
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index 0dae04b960..e2bedc5c7c 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -88,15 +88,13 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
{
int64_t value[] = {0, 1, 9, 10, 16, 15, 14,
3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX};
- intList *list = NULL, **tmp = &list;
+ intList *list = NULL, **tail = &list;
int i;
Error *err = NULL;
char *str;
for (i = 0; i < ARRAY_SIZE(value); i++) {
- *tmp = g_malloc0(sizeof(**tmp));
- (*tmp)->value = value[i];
- tmp = &(*tmp)->next;
+ QAPI_LIST_APPEND(tail, value[i]);
}
visit_type_intList(data->ov, NULL, &list, &err);
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 5746d0aae7..514c0f9754 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -354,11 +354,11 @@ static const char *wan_compression_names[] = {
static SpiceChannelList *qmp_query_spice_channels(void)
{
- SpiceChannelList *cur_item = NULL, *head = NULL;
+ SpiceChannelList *head = NULL, **tail = &head;
ChannelList *item;
QTAILQ_FOREACH(item, &channel_list, link) {
- SpiceChannelList *chan;
+ SpiceChannel *chan;
char host[NI_MAXHOST], port[NI_MAXSERV];
struct sockaddr *paddr;
socklen_t plen;
@@ -366,29 +366,22 @@ static SpiceChannelList *qmp_query_spice_channels(void)
assert(item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT);
chan = g_malloc0(sizeof(*chan));
- chan->value = g_malloc0(sizeof(*chan->value));
paddr = (struct sockaddr *)&item->info->paddr_ext;
plen = item->info->plen_ext;
getnameinfo(paddr, plen,
host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
- chan->value->host = g_strdup(host);
- chan->value->port = g_strdup(port);
- chan->value->family = inet_netfamily(paddr->sa_family);
-
- chan->value->connection_id = item->info->connection_id;
- chan->value->channel_type = item->info->type;
- chan->value->channel_id = item->info->id;
- chan->value->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
-
- /* XXX: waiting for the qapi to support GSList */
- if (!cur_item) {
- head = cur_item = chan;
- } else {
- cur_item->next = chan;
- cur_item = chan;
- }
+ chan->host = g_strdup(host);
+ chan->port = g_strdup(port);
+ chan->family = inet_netfamily(paddr->sa_family);
+
+ chan->connection_id = item->info->connection_id;
+ chan->channel_type = item->info->type;
+ chan->channel_id = item->info->id;
+ chan->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
+
+ QAPI_LIST_APPEND(tail, chan);
}
return head;
diff --git a/ui/vnc.c b/ui/vnc.c
index 66f7c1b936..16bb3be770 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -687,6 +687,10 @@ static void vnc_desktop_resize(VncState *vs)
!vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT))) {
return;
}
+ if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
+ vs->client_height == pixman_image_get_height(vs->vd->server)) {
+ return;
+ }
assert(pixman_image_get_width(vs->vd->server) < 65536 &&
pixman_image_get_width(vs->vd->server) >= 0);
@@ -2042,10 +2046,9 @@ static void framebuffer_update_request(VncState *vs, int incremental,
} else {
vs->update = VNC_STATE_UPDATE_FORCE;
vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h);
- vnc_colordepth(vs);
- vnc_desktop_resize(vs);
- vnc_led_state_change(vs);
- vnc_cursor_define(vs);
+ if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) {
+ vnc_desktop_resize_ext(vs, 0);
+ }
}
}
@@ -2189,7 +2192,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
break;
}
}
+ vnc_desktop_resize(vs);
check_pointer_type_change(&vs->mouse_mode_notifier, NULL);
+ vnc_led_state_change(vs);
+ vnc_cursor_define(vs);
}
static void set_pixel_conversion(VncState *vs)
diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c
index aade82afb8..e99b8a4f9c 100644
--- a/util/coroutine-sigaltstack.c
+++ b/util/coroutine-sigaltstack.c
@@ -157,6 +157,7 @@ Coroutine *qemu_coroutine_new(void)
sigset_t sigs;
sigset_t osigs;
sigjmp_buf old_env;
+ static pthread_mutex_t sigusr2_mutex = PTHREAD_MUTEX_INITIALIZER;
/* The way to manipulate stack is with the sigaltstack function. We
* prepare a stack, with it delivering a signal to ourselves and then
@@ -186,6 +187,12 @@ Coroutine *qemu_coroutine_new(void)
sa.sa_handler = coroutine_trampoline;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK;
+
+ /*
+ * sigaction() is a process-global operation. We must not run
+ * this code in multiple threads at once.
+ */
+ pthread_mutex_lock(&sigusr2_mutex);
if (sigaction(SIGUSR2, &sa, &osa) != 0) {
abort();
}
@@ -234,6 +241,8 @@ Coroutine *qemu_coroutine_new(void)
* Restore the old SIGUSR2 signal handler and mask
*/
sigaction(SIGUSR2, &osa, NULL);
+ pthread_mutex_unlock(&sigusr2_mutex);
+
pthread_sigmask(SIG_SETMASK, &osigs, NULL);
/*