aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--block.c43
-rw-r--r--block/Makefile.objs4
-rw-r--r--block/accounting.c6
-rw-r--r--block/aio_task.c124
-rw-r--r--block/backup-top.c276
-rw-r--r--block/backup-top.h41
-rw-r--r--block/backup.c443
-rw-r--r--block/block-copy.c345
-rw-r--r--block/file-posix.c54
-rw-r--r--block/nbd.c15
-rw-r--r--block/qapi.c11
-rw-r--r--block/qcow2.c466
-rw-r--r--block/qcow2.h3
-rw-r--r--block/replication.c2
-rw-r--r--block/trace-events15
-rw-r--r--blockdev.c1
-rw-r--r--docs/devel/rcu.txt16
-rw-r--r--exec.c116
-rw-r--r--fsdev/9p-marshal.h6
-rw-r--r--fsdev/file-op-9p.h5
-rw-r--r--fsdev/qemu-fsdev-opts.c7
-rw-r--r--fsdev/qemu-fsdev-throttle.c4
-rw-r--r--fsdev/qemu-fsdev-throttle.h2
-rw-r--r--fsdev/qemu-fsdev.c1
-rw-r--r--hw/9pfs/9p-local.c28
-rw-r--r--hw/9pfs/9p-proxy.c4
-rw-r--r--hw/9pfs/9p.c534
-rw-r--r--hw/9pfs/9p.h60
-rw-r--r--hw/9pfs/trace-events14
-rw-r--r--hw/ide/core.c12
-rw-r--r--hw/s390x/s390-pci-inst.c58
-rw-r--r--hw/s390x/s390-virtio-ccw.c2
-rw-r--r--hw/scsi/scsi-disk.c34
-rw-r--r--hw/vfio/pci.c1
-rw-r--r--include/block/accounting.h2
-rw-r--r--include/block/aio_task.h54
-rw-r--r--include/block/block-copy.h93
-rw-r--r--include/block/block.h1
-rw-r--r--include/block/block_int.h2
-rw-r--r--include/exec/exec-all.h17
-rw-r--r--include/exec/ram_addr.h138
-rw-r--r--include/migration/misc.h1
-rw-r--r--include/migration/vmstate.h40
-rw-r--r--include/qemu/rcu.h25
-rw-r--r--memory.c15
-rw-r--r--migration/migration.c17
-rw-r--r--migration/postcopy-ram.c88
-rw-r--r--migration/postcopy-ram.h9
-rw-r--r--migration/ram.c298
-rw-r--r--migration/rdma.c57
-rw-r--r--migration/savevm.c14
-rw-r--r--migration/trace-events5
-rw-r--r--migration/vmstate-types.c152
-rw-r--r--qapi/block-core.json89
-rw-r--r--qemu-options.hx26
-rw-r--r--target/s390x/cc_helper.c4
-rw-r--r--target/s390x/cpu.h86
-rw-r--r--target/s390x/crypto_helper.c7
-rw-r--r--target/s390x/diag.c14
-rw-r--r--target/s390x/excp_helper.c58
-rw-r--r--target/s390x/fpu_helper.c6
-rw-r--r--target/s390x/gen-features.c11
-rw-r--r--target/s390x/helper.c7
-rw-r--r--target/s390x/int_helper.c15
-rw-r--r--target/s390x/internal.h6
-rw-r--r--target/s390x/interrupt.c9
-rw-r--r--target/s390x/ioinst.c40
-rw-r--r--target/s390x/mem_helper.c84
-rw-r--r--target/s390x/misc_helper.c27
-rw-r--r--target/s390x/mmu_helper.c429
-rw-r--r--target/s390x/tcg-stub.c4
-rw-r--r--target/s390x/tcg_s390x.h4
-rw-r--r--target/s390x/translate.c20
-rwxr-xr-xtests/qemu-iotests/0266
-rw-r--r--tests/qemu-iotests/026.out80
-rw-r--r--tests/qemu-iotests/026.out.nocache80
-rwxr-xr-xtests/qemu-iotests/0568
-rwxr-xr-xtests/qemu-iotests/12483
-rwxr-xr-xtests/qemu-iotests/12545
-rw-r--r--tests/qemu-iotests/141.out2
-rwxr-xr-xtests/qemu-iotests/1492
-rw-r--r--tests/qemu-iotests/149.out44
-rwxr-xr-xtests/qemu-iotests/1622
-rw-r--r--tests/qemu-iotests/162.out2
-rw-r--r--tests/qemu-iotests/227.out18
-rwxr-xr-xtests/qemu-iotests/25791
-rw-r--r--tests/qemu-iotests/257.out714
-rw-r--r--tests/qemu-iotests/common.rc17
-rw-r--r--tests/qemu-iotests/iotests.py27
-rw-r--r--tests/test-vmstate.c421
-rw-r--r--vl.c7
92 files changed, 4203 insertions, 2175 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 3ca814850e..fe4dc51b08 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1517,7 +1517,7 @@ F: tests/virtio-balloon-test.c
virtio-9p
M: Greg Kurz <groug@kaod.org>
-S: Supported
+S: Odd Fixes
F: hw/9pfs/
X: hw/9pfs/xen-9p*
F: fsdev/
diff --git a/block.c b/block.c
index 5944124845..1946fc6f57 100644
--- a/block.c
+++ b/block.c
@@ -5155,6 +5155,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
return NULL;
}
+BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv || !drv->bdrv_get_specific_stats) {
+ return NULL;
+ }
+ return drv->bdrv_get_specific_stats(bs);
+}
+
void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
{
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
@@ -5164,14 +5173,35 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
bs->drv->bdrv_debug_event(bs, event);
}
-int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
- const char *tag)
+static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs)
{
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
- bs = bs->file ? bs->file->bs : NULL;
+ if (bs->file) {
+ bs = bs->file->bs;
+ continue;
+ }
+
+ if (bs->drv->is_filter && bs->backing) {
+ bs = bs->backing->bs;
+ continue;
+ }
+
+ break;
}
if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) {
+ assert(bs->drv->bdrv_debug_remove_breakpoint);
+ return bs;
+ }
+
+ return NULL;
+}
+
+int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
+ const char *tag)
+{
+ bs = bdrv_find_debug_node(bs);
+ if (bs) {
return bs->drv->bdrv_debug_breakpoint(bs, event, tag);
}
@@ -5180,11 +5210,8 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag)
{
- while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) {
- bs = bs->file ? bs->file->bs : NULL;
- }
-
- if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) {
+ bs = bdrv_find_debug_node(bs);
+ if (bs) {
return bs->drv->bdrv_debug_remove_breakpoint(bs, tag);
}
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 35f3bca4d9..e394fe0b6c 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -37,9 +37,13 @@ block-obj-y += write-threshold.o
block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o copy-on-read.o
+block-obj-y += block-copy.o
block-obj-y += crypto.o
+block-obj-y += aio_task.o
+block-obj-y += backup-top.o
+
common-obj-y += stream.o
nfs.o-libs := $(LIBNFS_LIBS)
diff --git a/block/accounting.c b/block/accounting.c
index 70a3d9a426..8d41c8a83a 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -195,6 +195,10 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
assert(cookie->type < BLOCK_MAX_IOTYPE);
+ if (cookie->type == BLOCK_ACCT_NONE) {
+ return;
+ }
+
qemu_mutex_lock(&stats->lock);
if (failed) {
@@ -217,6 +221,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
}
qemu_mutex_unlock(&stats->lock);
+
+ cookie->type = BLOCK_ACCT_NONE;
}
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
diff --git a/block/aio_task.c b/block/aio_task.c
new file mode 100644
index 0000000000..88989fa248
--- /dev/null
+++ b/block/aio_task.c
@@ -0,0 +1,124 @@
+/*
+ * Aio tasks loops
+ *
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/aio.h"
+#include "block/aio_task.h"
+
+struct AioTaskPool {
+ Coroutine *main_co;
+ int status;
+ int max_busy_tasks;
+ int busy_tasks;
+ bool waiting;
+};
+
+static void coroutine_fn aio_task_co(void *opaque)
+{
+ AioTask *task = opaque;
+ AioTaskPool *pool = task->pool;
+
+ assert(pool->busy_tasks < pool->max_busy_tasks);
+ pool->busy_tasks++;
+
+ task->ret = task->func(task);
+
+ pool->busy_tasks--;
+
+ if (task->ret < 0 && pool->status == 0) {
+ pool->status = task->ret;
+ }
+
+ g_free(task);
+
+ if (pool->waiting) {
+ pool->waiting = false;
+ aio_co_wake(pool->main_co);
+ }
+}
+
+void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool)
+{
+ assert(pool->busy_tasks > 0);
+ assert(qemu_coroutine_self() == pool->main_co);
+
+ pool->waiting = true;
+ qemu_coroutine_yield();
+
+ assert(!pool->waiting);
+ assert(pool->busy_tasks < pool->max_busy_tasks);
+}
+
+void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool)
+{
+ if (pool->busy_tasks < pool->max_busy_tasks) {
+ return;
+ }
+
+ aio_task_pool_wait_one(pool);
+}
+
+void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool)
+{
+ while (pool->busy_tasks > 0) {
+ aio_task_pool_wait_one(pool);
+ }
+}
+
+void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task)
+{
+ aio_task_pool_wait_slot(pool);
+
+ task->pool = pool;
+ qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task));
+}
+
+AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks)
+{
+ AioTaskPool *pool = g_new0(AioTaskPool, 1);
+
+ pool->main_co = qemu_coroutine_self();
+ pool->max_busy_tasks = max_busy_tasks;
+
+ return pool;
+}
+
+void aio_task_pool_free(AioTaskPool *pool)
+{
+ g_free(pool);
+}
+
+int aio_task_pool_status(AioTaskPool *pool)
+{
+ if (!pool) {
+ return 0; /* Sugar for lazy allocation of aio pool */
+ }
+
+ return pool->status;
+}
+
+bool aio_task_pool_empty(AioTaskPool *pool)
+{
+ return pool->busy_tasks == 0;
+}
diff --git a/block/backup-top.c b/block/backup-top.c
new file mode 100644
index 0000000000..7cdb1f8eba
--- /dev/null
+++ b/block/backup-top.c
@@ -0,0 +1,276 @@
+/*
+ * backup-top filter driver
+ *
+ * The driver performs Copy-Before-Write (CBW) operation: it is injected above
+ * some node, and before each write it copies _old_ data to the target node.
+ *
+ * Copyright (c) 2018-2019 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Sementsov-Ogievskiy Vladimir <vsementsov@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/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "sysemu/block-backend.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "block/block_int.h"
+#include "block/qdict.h"
+#include "block/block-copy.h"
+
+#include "block/backup-top.h"
+
+typedef struct BDRVBackupTopState {
+ BlockCopyState *bcs;
+ BdrvChild *target;
+ bool active;
+} BDRVBackupTopState;
+
+static coroutine_fn int backup_top_co_preadv(
+ BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
+}
+
+static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes)
+{
+ BDRVBackupTopState *s = bs->opaque;
+ uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size);
+ uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size);
+
+ return block_copy(s->bcs, off, end - off, NULL);
+}
+
+static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int bytes)
+{
+ int ret = backup_top_cbw(bs, offset, bytes);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return bdrv_co_pdiscard(bs->backing, offset, bytes);
+}
+
+static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
+ int64_t offset, int bytes, BdrvRequestFlags flags)
+{
+ int ret = backup_top_cbw(bs, offset, bytes);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
+}
+
+static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) {
+ int ret = backup_top_cbw(bs, offset, bytes);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
+}
+
+static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
+{
+ if (!bs->backing) {
+ return 0;
+ }
+
+ return bdrv_co_flush(bs->backing->bs);
+}
+
+static void backup_top_refresh_filename(BlockDriverState *bs)
+{
+ if (bs->backing == NULL) {
+ /*
+ * we can be here after failed bdrv_attach_child in
+ * bdrv_set_backing_hd
+ */
+ return;
+ }
+ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
+ bs->backing->bs->filename);
+}
+
+static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
+ const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ BDRVBackupTopState *s = bs->opaque;
+
+ if (!s->active) {
+ /*
+ * The filter node may be in process of bdrv_append(), which firstly do
+ * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
+ * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
+ * let's require nothing during bdrv_append() and refresh permissions
+ * after it (see bdrv_backup_top_append()).
+ */
+ *nperm = 0;
+ *nshared = BLK_PERM_ALL;
+ return;
+ }
+
+ if (role == &child_file) {
+ /*
+ * Target child
+ *
+ * Share write to target (child_file), to not interfere
+ * with guest writes to its disk which may be in target backing chain.
+ */
+ *nshared = BLK_PERM_ALL;
+ *nperm = BLK_PERM_WRITE;
+ } else {
+ /* Source child */
+ bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
+ nperm, nshared);
+
+ if (perm & BLK_PERM_WRITE) {
+ *nperm = *nperm | BLK_PERM_CONSISTENT_READ;
+ }
+ *nshared &= ~BLK_PERM_WRITE;
+ }
+}
+
+BlockDriver bdrv_backup_top_filter = {
+ .format_name = "backup-top",
+ .instance_size = sizeof(BDRVBackupTopState),
+
+ .bdrv_co_preadv = backup_top_co_preadv,
+ .bdrv_co_pwritev = backup_top_co_pwritev,
+ .bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
+ .bdrv_co_pdiscard = backup_top_co_pdiscard,
+ .bdrv_co_flush = backup_top_co_flush,
+
+ .bdrv_co_block_status = bdrv_co_block_status_from_backing,
+
+ .bdrv_refresh_filename = backup_top_refresh_filename,
+
+ .bdrv_child_perm = backup_top_child_perm,
+
+ .is_filter = true,
+};
+
+BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
+ BlockDriverState *target,
+ const char *filter_node_name,
+ uint64_t cluster_size,
+ BdrvRequestFlags write_flags,
+ BlockCopyState **bcs,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ BDRVBackupTopState *state;
+ BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
+ filter_node_name,
+ BDRV_O_RDWR, errp);
+
+ if (!top) {
+ return NULL;
+ }
+
+ top->total_sectors = source->total_sectors;
+ top->opaque = state = g_new0(BDRVBackupTopState, 1);
+
+ bdrv_ref(target);
+ state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
+ if (!state->target) {
+ bdrv_unref(target);
+ bdrv_unref(top);
+ return NULL;
+ }
+
+ bdrv_drained_begin(source);
+
+ bdrv_ref(top);
+ bdrv_append(top, source, &local_err);
+ if (local_err) {
+ error_prepend(&local_err, "Cannot append backup-top filter: ");
+ goto append_failed;
+ }
+
+ /*
+ * bdrv_append() finished successfully, now we can require permissions
+ * we want.
+ */
+ state->active = true;
+ bdrv_child_refresh_perms(top, top->backing, &local_err);
+ if (local_err) {
+ error_prepend(&local_err,
+ "Cannot set permissions for backup-top filter: ");
+ goto failed_after_append;
+ }
+
+ state->bcs = block_copy_state_new(top->backing, state->target,
+ cluster_size, write_flags, &local_err);
+ if (local_err) {
+ error_prepend(&local_err, "Cannot create block-copy-state: ");
+ goto failed_after_append;
+ }
+ *bcs = state->bcs;
+
+ bdrv_drained_end(source);
+
+ return top;
+
+failed_after_append:
+ state->active = false;
+ bdrv_backup_top_drop(top);
+
+append_failed:
+ bdrv_drained_end(source);
+ bdrv_unref_child(top, state->target);
+ bdrv_unref(top);
+ error_propagate(errp, local_err);
+
+ return NULL;
+}
+
+void bdrv_backup_top_drop(BlockDriverState *bs)
+{
+ BDRVBackupTopState *s = bs->opaque;
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ block_copy_state_free(s->bcs);
+
+ aio_context_acquire(aio_context);
+
+ bdrv_drained_begin(bs);
+
+ s->active = false;
+ bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
+ bdrv_replace_node(bs, backing_bs(bs), &error_abort);
+ bdrv_set_backing_hd(bs, NULL, &error_abort);
+
+ bdrv_drained_end(bs);
+
+ bdrv_unref(bs);
+
+ aio_context_release(aio_context);
+}
diff --git a/block/backup-top.h b/block/backup-top.h
new file mode 100644
index 0000000000..e5cabfa197
--- /dev/null
+++ b/block/backup-top.h
@@ -0,0 +1,41 @@
+/*
+ * backup-top filter driver
+ *
+ * The driver performs Copy-Before-Write (CBW) operation: it is injected above
+ * some node, and before each write it copies _old_ data to the target node.
+ *
+ * Copyright (c) 2018-2019 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Sementsov-Ogievskiy Vladimir <vsementsov@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 BACKUP_TOP_H
+#define BACKUP_TOP_H
+
+#include "block/block_int.h"
+#include "block/block-copy.h"
+
+BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
+ BlockDriverState *target,
+ const char *filter_node_name,
+ uint64_t cluster_size,
+ BdrvRequestFlags write_flags,
+ BlockCopyState **bcs,
+ Error **errp);
+void bdrv_backup_top_drop(BlockDriverState *bs);
+
+#endif /* BACKUP_TOP_H */
diff --git a/block/backup.c b/block/backup.c
index 763f0d7ff6..46978c1785 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -2,6 +2,7 @@
* QEMU backup
*
* Copyright (C) 2013 Proxmox Server Solutions
+ * Copyright (c) 2019 Virtuozzo International GmbH.
*
* Authors:
* Dietmar Maurer (dietmar@proxmox.com)
@@ -18,6 +19,7 @@
#include "block/block_int.h"
#include "block/blockjob_int.h"
#include "block/block_backup.h"
+#include "block/block-copy.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h"
@@ -26,333 +28,68 @@
#include "qemu/bitmap.h"
#include "qemu/error-report.h"
-#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
+#include "block/backup-top.h"
-typedef struct CowRequest {
- int64_t start_byte;
- int64_t end_byte;
- QLIST_ENTRY(CowRequest) list;
- CoQueue wait_queue; /* coroutines blocked on this request */
-} CowRequest;
+#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
typedef struct BackupBlockJob {
BlockJob common;
- BlockBackend *target;
+ BlockDriverState *backup_top;
+ BlockDriverState *source_bs;
BdrvDirtyBitmap *sync_bitmap;
- BdrvDirtyBitmap *copy_bitmap;
MirrorSyncMode sync_mode;
BitmapSyncMode bitmap_mode;
BlockdevOnError on_source_error;
BlockdevOnError on_target_error;
- CoRwlock flush_rwlock;
uint64_t len;
uint64_t bytes_read;
int64_t cluster_size;
- NotifierWithReturn before_write;
- QLIST_HEAD(, CowRequest) inflight_reqs;
- bool use_copy_range;
- int64_t copy_range_size;
-
- BdrvRequestFlags write_flags;
- bool initializing_bitmap;
+ BlockCopyState *bcs;
} BackupBlockJob;
static const BlockJobDriver backup_job_driver;
-/* See if in-flight requests overlap and wait for them to complete */
-static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
- int64_t start,
- int64_t end)
+static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
{
- CowRequest *req;
- bool retry;
-
- do {
- retry = false;
- QLIST_FOREACH(req, &job->inflight_reqs, list) {
- if (end > req->start_byte && start < req->end_byte) {
- qemu_co_queue_wait(&req->wait_queue, NULL);
- retry = true;
- break;
- }
- }
- } while (retry);
-}
+ BackupBlockJob *s = opaque;
-/* Keep track of an in-flight request */
-static void cow_request_begin(CowRequest *req, BackupBlockJob *job,
- int64_t start, int64_t end)
-{
- req->start_byte = start;
- req->end_byte = end;
- qemu_co_queue_init(&req->wait_queue);
- QLIST_INSERT_HEAD(&job->inflight_reqs, req, list);
+ s->bytes_read += bytes;
+ job_progress_update(&s->common.job, bytes);
}
-/* Forget about a completed request */
-static void cow_request_end(CowRequest *req)
+static void backup_progress_reset_callback(void *opaque)
{
- QLIST_REMOVE(req, list);
- qemu_co_queue_restart_all(&req->wait_queue);
-}
-
-/* Copy range to target with a bounce buffer and return the bytes copied. If
- * error occurred, return a negative error number */
-static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
- int64_t start,
- int64_t end,
- bool is_write_notifier,
- bool *error_is_read,
- void **bounce_buffer)
-{
- int ret;
- BlockBackend *blk = job->common.blk;
- int nbytes;
- int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
-
- assert(QEMU_IS_ALIGNED(start, job->cluster_size));
- bdrv_reset_dirty_bitmap(job->copy_bitmap, start, job->cluster_size);
- nbytes = MIN(job->cluster_size, job->len - start);
- if (!*bounce_buffer) {
- *bounce_buffer = blk_blockalign(blk, job->cluster_size);
- }
-
- ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
- if (ret < 0) {
- trace_backup_do_cow_read_fail(job, start, ret);
- if (error_is_read) {
- *error_is_read = true;
- }
- goto fail;
- }
-
- ret = blk_co_pwrite(job->target, start, nbytes, *bounce_buffer,
- job->write_flags);
- if (ret < 0) {
- trace_backup_do_cow_write_fail(job, start, ret);
- if (error_is_read) {
- *error_is_read = false;
- }
- goto fail;
- }
-
- return nbytes;
-fail:
- bdrv_set_dirty_bitmap(job->copy_bitmap, start, job->cluster_size);
- return ret;
-
-}
-
-/* Copy range to target and return the bytes copied. If error occurred, return a
- * negative error number. */
-static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
- int64_t start,
- int64_t end,
- bool is_write_notifier)
-{
- int ret;
- int nr_clusters;
- BlockBackend *blk = job->common.blk;
- int nbytes;
- int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
-
- assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
- assert(QEMU_IS_ALIGNED(start, job->cluster_size));
- nbytes = MIN(job->copy_range_size, end - start);
- nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
- bdrv_reset_dirty_bitmap(job->copy_bitmap, start,
- job->cluster_size * nr_clusters);
- ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
- read_flags, job->write_flags);
- if (ret < 0) {
- trace_backup_do_cow_copy_range_fail(job, start, ret);
- bdrv_set_dirty_bitmap(job->copy_bitmap, start,
- job->cluster_size * nr_clusters);
- return ret;
- }
-
- return nbytes;
-}
-
-/*
- * Check if the cluster starting at offset is allocated or not.
- * return via pnum the number of contiguous clusters sharing this allocation.
- */
-static int backup_is_cluster_allocated(BackupBlockJob *s, int64_t offset,
- int64_t *pnum)
-{
- BlockDriverState *bs = blk_bs(s->common.blk);
- int64_t count, total_count = 0;
- int64_t bytes = s->len - offset;
- int ret;
-
- assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
-
- while (true) {
- ret = bdrv_is_allocated(bs, offset, bytes, &count);
- if (ret < 0) {
- return ret;
- }
-
- total_count += count;
-
- if (ret || count == 0) {
- /*
- * ret: partial segment(s) are considered allocated.
- * otherwise: unallocated tail is treated as an entire segment.
- */
- *pnum = DIV_ROUND_UP(total_count, s->cluster_size);
- return ret;
- }
-
- /* Unallocated segment(s) with uncertain following segment(s) */
- if (total_count >= s->cluster_size) {
- *pnum = total_count / s->cluster_size;
- return 0;
- }
-
- offset += count;
- bytes -= count;
- }
-}
-
-/**
- * Reset bits in copy_bitmap starting at offset if they represent unallocated
- * data in the image. May reset subsequent contiguous bits.
- * @return 0 when the cluster at @offset was unallocated,
- * 1 otherwise, and -ret on error.
- */
-static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
- int64_t offset, int64_t *count)
-{
- int ret;
- int64_t clusters, bytes, estimate;
-
- ret = backup_is_cluster_allocated(s, offset, &clusters);
- if (ret < 0) {
- return ret;
- }
-
- bytes = clusters * s->cluster_size;
-
- if (!ret) {
- bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
- estimate = bdrv_get_dirty_count(s->copy_bitmap);
- job_progress_set_remaining(&s->common.job, estimate);
- }
+ BackupBlockJob *s = opaque;
+ uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap);
- *count = bytes;
- return ret;
+ job_progress_set_remaining(&s->common.job, estimate);
}
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes,
- bool *error_is_read,
- bool is_write_notifier)
+ bool *error_is_read)
{
- CowRequest cow_request;
int ret = 0;
int64_t start, end; /* bytes */
- void *bounce_buffer = NULL;
- int64_t status_bytes;
-
- qemu_co_rwlock_rdlock(&job->flush_rwlock);
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);
- wait_for_overlapping_requests(job, start, end);
- cow_request_begin(&cow_request, job, start, end);
-
- while (start < end) {
- int64_t dirty_end;
-
- if (!bdrv_dirty_bitmap_get(job->copy_bitmap, start)) {
- trace_backup_do_cow_skip(job, start);
- start += job->cluster_size;
- continue; /* already copied */
- }
-
- dirty_end = bdrv_dirty_bitmap_next_zero(job->copy_bitmap, start,
- (end - start));
- if (dirty_end < 0) {
- dirty_end = end;
- }
-
- if (job->initializing_bitmap) {
- ret = backup_bitmap_reset_unallocated(job, start, &status_bytes);
- if (ret == 0) {
- trace_backup_do_cow_skip_range(job, start, status_bytes);
- start += status_bytes;
- continue;
- }
- /* Clamp to known allocated region */
- dirty_end = MIN(dirty_end, start + status_bytes);
- }
-
- trace_backup_do_cow_process(job, start);
-
- if (job->use_copy_range) {
- ret = backup_cow_with_offload(job, start, dirty_end,
- is_write_notifier);
- if (ret < 0) {
- job->use_copy_range = false;
- }
- }
- if (!job->use_copy_range) {
- ret = backup_cow_with_bounce_buffer(job, start, dirty_end,
- is_write_notifier,
- error_is_read, &bounce_buffer);
- }
- if (ret < 0) {
- break;
- }
-
- /* Publish progress, guest I/O counts as progress too. Note that the
- * offset field is an opaque progress value, it is not a disk offset.
- */
- start += ret;
- job->bytes_read += ret;
- job_progress_update(&job->common.job, ret);
- ret = 0;
- }
-
- if (bounce_buffer) {
- qemu_vfree(bounce_buffer);
- }
-
- cow_request_end(&cow_request);
+ ret = block_copy(job->bcs, start, end - start, error_is_read);
trace_backup_do_cow_return(job, offset, bytes, ret);
- qemu_co_rwlock_unlock(&job->flush_rwlock);
-
return ret;
}
-static int coroutine_fn backup_before_write_notify(
- NotifierWithReturn *notifier,
- void *opaque)
-{
- BackupBlockJob *job = container_of(notifier, BackupBlockJob, before_write);
- BdrvTrackedRequest *req = opaque;
-
- assert(req->bs == blk_bs(job->common.blk));
- assert(QEMU_IS_ALIGNED(req->offset, BDRV_SECTOR_SIZE));
- assert(QEMU_IS_ALIGNED(req->bytes, BDRV_SECTOR_SIZE));
-
- return backup_do_cow(job, req->offset, req->bytes, NULL, true);
-}
-
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{
BdrvDirtyBitmap *bm;
- BlockDriverState *bs = blk_bs(job->common.blk);
bool sync = (((ret == 0) || (job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS)) \
&& (job->bitmap_mode != BITMAP_SYNC_MODE_NEVER));
@@ -361,20 +98,20 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
* We succeeded, or we always intended to sync the bitmap.
* Delete this bitmap and install the child.
*/
- bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
+ bm = bdrv_dirty_bitmap_abdicate(job->source_bs, job->sync_bitmap, NULL);
} else {
/*
* We failed, or we never intended to sync the bitmap anyway.
* Merge the successor back into the parent, keeping all data.
*/
- bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
+ bm = bdrv_reclaim_dirty_bitmap(job->source_bs, job->sync_bitmap, NULL);
}
assert(bm);
if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) {
/* If we failed and synced, merge in the bits we didn't copy: */
- bdrv_dirty_bitmap_merge_internal(bm, job->copy_bitmap,
+ bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap,
NULL, true);
}
}
@@ -398,16 +135,8 @@ static void backup_abort(Job *job)
static void backup_clean(Job *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
- BlockDriverState *bs = blk_bs(s->common.blk);
-
- if (s->copy_bitmap) {
- bdrv_release_dirty_bitmap(bs, s->copy_bitmap);
- s->copy_bitmap = NULL;
- }
- assert(s->target);
- blk_unref(s->target);
- s->target = NULL;
+ bdrv_backup_top_drop(s->backup_top);
}
void backup_do_checkpoint(BlockJob *job, Error **errp)
@@ -422,7 +151,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
return;
}
- bdrv_set_dirty_bitmap(backup_job->copy_bitmap, 0, backup_job->len);
+ bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len);
}
static BlockErrorAction backup_error_action(BackupBlockJob *job,
@@ -445,8 +174,10 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *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. */
+ /*
+ * 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);
@@ -465,14 +196,13 @@ static int coroutine_fn backup_loop(BackupBlockJob *job)
BdrvDirtyBitmapIter *bdbi;
int ret = 0;
- bdbi = bdrv_dirty_iter_new(job->copy_bitmap);
+ bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap);
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, false);
+ 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)
{
@@ -492,7 +222,7 @@ static void backup_init_copy_bitmap(BackupBlockJob *job)
uint64_t estimate;
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
- ret = bdrv_dirty_bitmap_merge_internal(job->copy_bitmap,
+ ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap,
job->sync_bitmap,
NULL, true);
assert(ret);
@@ -502,29 +232,22 @@ static void backup_init_copy_bitmap(BackupBlockJob *job)
* We can't hog the coroutine to initialize this thoroughly.
* Set a flag and resume work when we are able to yield safely.
*/
- job->initializing_bitmap = true;
+ job->bcs->skip_unallocated = true;
}
- bdrv_set_dirty_bitmap(job->copy_bitmap, 0, job->len);
+ bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len);
}
- estimate = bdrv_get_dirty_count(job->copy_bitmap);
+ estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap);
job_progress_set_remaining(&job->common.job, estimate);
}
static int coroutine_fn backup_run(Job *job, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
- BlockDriverState *bs = blk_bs(s->common.blk);
int ret = 0;
- QLIST_INIT(&s->inflight_reqs);
- qemu_co_rwlock_init(&s->flush_rwlock);
-
backup_init_copy_bitmap(s);
- s->before_write.notify = backup_before_write_notify;
- bdrv_add_before_write_notifier(bs, &s->before_write);
-
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
int64_t offset = 0;
int64_t count;
@@ -535,22 +258,26 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
goto out;
}
- ret = backup_bitmap_reset_unallocated(s, offset, &count);
+ ret = block_copy_reset_unallocated(s->bcs, offset, &count);
if (ret < 0) {
goto out;
}
offset += count;
}
- s->initializing_bitmap = false;
+ s->bcs->skip_unallocated = false;
}
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
- /* All bits are set in copy_bitmap to allow any cluster to be copied.
- * This does not actually require them to be copied. */
+ /*
+ * All bits are set in copy_bitmap to allow any cluster to be copied.
+ * This does not actually require them to be copied.
+ */
while (!job_is_cancelled(job)) {
- /* Yield until the job is cancelled. We just let our before_write
- * notify callback service CoW requests. */
+ /*
+ * Yield until the job is cancelled. We just let our before_write
+ * notify callback service CoW requests.
+ */
job_yield(job);
}
} else {
@@ -558,12 +285,6 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
}
out:
- notifier_with_return_remove(&s->before_write);
-
- /* wait until pending backup_do_cow() calls have completed */
- qemu_co_rwlock_wrlock(&s->flush_rwlock);
- qemu_co_rwlock_unlock(&s->flush_rwlock);
-
return ret;
}
@@ -621,6 +342,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
BitmapSyncMode bitmap_mode,
bool compress,
+ const char *filter_node_name,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
@@ -629,9 +351,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
{
int64_t len;
BackupBlockJob *job = NULL;
- int ret;
int64_t cluster_size;
- BdrvDirtyBitmap *copy_bitmap = NULL;
+ BdrvRequestFlags write_flags;
+ BlockDriverState *backup_top = NULL;
+ BlockCopyState *bcs = NULL;
assert(bs);
assert(target);
@@ -696,76 +419,66 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
goto error;
}
- copy_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp);
- if (!copy_bitmap) {
+ /*
+ * 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
+ * source at backup-start point in time. And target is going to be read by
+ * somebody (for example, used as NBD export) during backup job.
+ *
+ * In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid
+ * intersection of backup writes and third party reads from target,
+ * otherwise reading from target we may occasionally read already updated by
+ * guest data.
+ *
+ * For more information see commit f8d59dfb40bb and test
+ * tests/qemu-iotests/222
+ */
+ write_flags = (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
+ (compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
+
+ backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
+ cluster_size, write_flags, &bcs, errp);
+ if (!backup_top) {
goto error;
}
- bdrv_disable_dirty_bitmap(copy_bitmap);
/* job->len is fixed, so we can't allow resize */
- job = block_job_create(job_id, &backup_job_driver, txn, bs,
- BLK_PERM_CONSISTENT_READ,
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
- BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
+ job = block_job_create(job_id, &backup_job_driver, txn, backup_top,
+ 0, BLK_PERM_ALL,
speed, creation_flags, cb, opaque, errp);
if (!job) {
goto error;
}
- /* The target must match the source in size, so no resize here either */
- job->target = blk_new(job->common.job.aio_context,
- BLK_PERM_WRITE,
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
- BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
- ret = blk_insert_bs(job->target, target, errp);
- if (ret < 0) {
- goto error;
- }
- blk_set_disable_request_queuing(job->target, true);
-
+ job->backup_top = backup_top;
+ job->source_bs = bs;
job->on_source_error = on_source_error;
job->on_target_error = on_target_error;
job->sync_mode = sync_mode;
job->sync_bitmap = sync_bitmap;
job->bitmap_mode = bitmap_mode;
+ job->bcs = bcs;
+ job->cluster_size = cluster_size;
+ job->len = len;
- /*
- * Set write flags:
- * 1. Detect image-fleecing (and similar) schemes
- * 2. Handle compression
- */
- job->write_flags =
- (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
- (compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
+ block_copy_set_callbacks(bcs, backup_progress_bytes_callback,
+ backup_progress_reset_callback, job);
- job->cluster_size = cluster_size;
- job->copy_bitmap = copy_bitmap;
- copy_bitmap = NULL;
- job->use_copy_range = !compress; /* compression isn't supported for it */
- job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
- blk_get_max_transfer(job->target));
- job->copy_range_size = MAX(job->cluster_size,
- QEMU_ALIGN_UP(job->copy_range_size,
- job->cluster_size));
-
- /* Required permissions are already taken with target's blk_new() */
+ /* Required permissions are already taken by backup-top target */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
- job->len = len;
return &job->common;
error:
- if (copy_bitmap) {
- assert(!job || !job->copy_bitmap);
- bdrv_release_dirty_bitmap(bs, copy_bitmap);
- }
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
}
if (job) {
backup_clean(&job->common.job);
job_early_fail(&job->common.job);
+ } else if (backup_top) {
+ bdrv_backup_top_drop(backup_top);
}
return NULL;
diff --git a/block/block-copy.c b/block/block-copy.c
new file mode 100644
index 0000000000..0f76ea1e63
--- /dev/null
+++ b/block/block-copy.c
@@ -0,0 +1,345 @@
+/*
+ * block_copy API
+ *
+ * Copyright (C) 2013 Proxmox Server Solutions
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Authors:
+ * Dietmar Maurer (dietmar@proxmox.com)
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "trace.h"
+#include "qapi/error.h"
+#include "block/block-copy.h"
+#include "sysemu/block-backend.h"
+
+static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s,
+ int64_t start,
+ int64_t end)
+{
+ BlockCopyInFlightReq *req;
+ bool waited;
+
+ do {
+ waited = false;
+ QLIST_FOREACH(req, &s->inflight_reqs, list) {
+ if (end > req->start_byte && start < req->end_byte) {
+ qemu_co_queue_wait(&req->wait_queue, NULL);
+ waited = true;
+ break;
+ }
+ }
+ } while (waited);
+}
+
+static void block_copy_inflight_req_begin(BlockCopyState *s,
+ BlockCopyInFlightReq *req,
+ int64_t start, int64_t end)
+{
+ req->start_byte = start;
+ req->end_byte = end;
+ qemu_co_queue_init(&req->wait_queue);
+ QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
+}
+
+static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req)
+{
+ QLIST_REMOVE(req, list);
+ qemu_co_queue_restart_all(&req->wait_queue);
+}
+
+void block_copy_state_free(BlockCopyState *s)
+{
+ if (!s) {
+ return;
+ }
+
+ bdrv_release_dirty_bitmap(s->source->bs, s->copy_bitmap);
+ g_free(s);
+}
+
+BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
+ int64_t cluster_size,
+ BdrvRequestFlags write_flags, Error **errp)
+{
+ BlockCopyState *s;
+ BdrvDirtyBitmap *copy_bitmap;
+ uint32_t max_transfer =
+ MIN_NON_ZERO(INT_MAX, MIN_NON_ZERO(source->bs->bl.max_transfer,
+ target->bs->bl.max_transfer));
+
+ copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
+ errp);
+ if (!copy_bitmap) {
+ return NULL;
+ }
+ bdrv_disable_dirty_bitmap(copy_bitmap);
+
+ s = g_new(BlockCopyState, 1);
+ *s = (BlockCopyState) {
+ .source = source,
+ .target = target,
+ .copy_bitmap = copy_bitmap,
+ .cluster_size = cluster_size,
+ .len = bdrv_dirty_bitmap_size(copy_bitmap),
+ .write_flags = write_flags,
+ };
+
+ s->copy_range_size = QEMU_ALIGN_DOWN(max_transfer, cluster_size),
+ /*
+ * Set use_copy_range, consider the following:
+ * 1. Compression is not supported for copy_range.
+ * 2. copy_range does not respect max_transfer (it's a TODO), so we factor
+ * that in here. If max_transfer is smaller than the job->cluster_size,
+ * we do not use copy_range (in that case it's zero after aligning down
+ * above).
+ */
+ s->use_copy_range =
+ !(write_flags & BDRV_REQ_WRITE_COMPRESSED) && s->copy_range_size > 0;
+
+ QLIST_INIT(&s->inflight_reqs);
+
+ return s;
+}
+
+void block_copy_set_callbacks(
+ BlockCopyState *s,
+ ProgressBytesCallbackFunc progress_bytes_callback,
+ ProgressResetCallbackFunc progress_reset_callback,
+ void *progress_opaque)
+{
+ s->progress_bytes_callback = progress_bytes_callback;
+ s->progress_reset_callback = progress_reset_callback;
+ s->progress_opaque = progress_opaque;
+}
+
+/*
+ * Copy range to target with a bounce buffer and return the bytes copied. If
+ * error occurred, return a negative error number
+ */
+static int coroutine_fn block_copy_with_bounce_buffer(BlockCopyState *s,
+ int64_t start,
+ int64_t end,
+ bool *error_is_read,
+ void **bounce_buffer)
+{
+ int ret;
+ int nbytes;
+
+ assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+ bdrv_reset_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
+ nbytes = MIN(s->cluster_size, s->len - start);
+ if (!*bounce_buffer) {
+ *bounce_buffer = qemu_blockalign(s->source->bs, s->cluster_size);
+ }
+
+ ret = bdrv_co_pread(s->source, start, nbytes, *bounce_buffer, 0);
+ if (ret < 0) {
+ trace_block_copy_with_bounce_buffer_read_fail(s, start, ret);
+ if (error_is_read) {
+ *error_is_read = true;
+ }
+ goto fail;
+ }
+
+ ret = bdrv_co_pwrite(s->target, start, nbytes, *bounce_buffer,
+ s->write_flags);
+ if (ret < 0) {
+ trace_block_copy_with_bounce_buffer_write_fail(s, start, ret);
+ if (error_is_read) {
+ *error_is_read = false;
+ }
+ goto fail;
+ }
+
+ return nbytes;
+fail:
+ bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
+ return ret;
+
+}
+
+/*
+ * Copy range to target and return the bytes copied. If error occurred, return a
+ * negative error number.
+ */
+static int coroutine_fn block_copy_with_offload(BlockCopyState *s,
+ int64_t start,
+ int64_t end)
+{
+ int ret;
+ int nr_clusters;
+ int nbytes;
+
+ assert(QEMU_IS_ALIGNED(s->copy_range_size, s->cluster_size));
+ assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+ nbytes = MIN(s->copy_range_size, MIN(end, s->len) - start);
+ nr_clusters = DIV_ROUND_UP(nbytes, s->cluster_size);
+ bdrv_reset_dirty_bitmap(s->copy_bitmap, start,
+ s->cluster_size * nr_clusters);
+ ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes,
+ 0, s->write_flags);
+ if (ret < 0) {
+ trace_block_copy_with_offload_fail(s, start, ret);
+ bdrv_set_dirty_bitmap(s->copy_bitmap, start,
+ s->cluster_size * nr_clusters);
+ return ret;
+ }
+
+ return nbytes;
+}
+
+/*
+ * Check if the cluster starting at offset is allocated or not.
+ * return via pnum the number of contiguous clusters sharing this allocation.
+ */
+static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
+ int64_t *pnum)
+{
+ BlockDriverState *bs = s->source->bs;
+ int64_t count, total_count = 0;
+ int64_t bytes = s->len - offset;
+ int ret;
+
+ assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
+
+ while (true) {
+ ret = bdrv_is_allocated(bs, offset, bytes, &count);
+ if (ret < 0) {
+ return ret;
+ }
+
+ total_count += count;
+
+ if (ret || count == 0) {
+ /*
+ * ret: partial segment(s) are considered allocated.
+ * otherwise: unallocated tail is treated as an entire segment.
+ */
+ *pnum = DIV_ROUND_UP(total_count, s->cluster_size);
+ return ret;
+ }
+
+ /* Unallocated segment(s) with uncertain following segment(s) */
+ if (total_count >= s->cluster_size) {
+ *pnum = total_count / s->cluster_size;
+ return 0;
+ }
+
+ offset += count;
+ bytes -= count;
+ }
+}
+
+/*
+ * Reset bits in copy_bitmap starting at offset if they represent unallocated
+ * data in the image. May reset subsequent contiguous bits.
+ * @return 0 when the cluster at @offset was unallocated,
+ * 1 otherwise, and -ret on error.
+ */
+int64_t block_copy_reset_unallocated(BlockCopyState *s,
+ int64_t offset, int64_t *count)
+{
+ int ret;
+ int64_t clusters, bytes;
+
+ ret = block_copy_is_cluster_allocated(s, offset, &clusters);
+ if (ret < 0) {
+ return ret;
+ }
+
+ bytes = clusters * s->cluster_size;
+
+ if (!ret) {
+ bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
+ s->progress_reset_callback(s->progress_opaque);
+ }
+
+ *count = bytes;
+ return ret;
+}
+
+int coroutine_fn block_copy(BlockCopyState *s,
+ int64_t start, uint64_t bytes,
+ bool *error_is_read)
+{
+ int ret = 0;
+ int64_t end = bytes + start; /* bytes */
+ void *bounce_buffer = NULL;
+ int64_t status_bytes;
+ BlockCopyInFlightReq req;
+
+ /*
+ * block_copy() user is responsible for keeping source and target in same
+ * aio context
+ */
+ assert(bdrv_get_aio_context(s->source->bs) ==
+ bdrv_get_aio_context(s->target->bs));
+
+ assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+ assert(QEMU_IS_ALIGNED(end, s->cluster_size));
+
+ block_copy_wait_inflight_reqs(s, start, bytes);
+ block_copy_inflight_req_begin(s, &req, start, end);
+
+ while (start < end) {
+ int64_t dirty_end;
+
+ if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) {
+ trace_block_copy_skip(s, start);
+ start += s->cluster_size;
+ continue; /* already copied */
+ }
+
+ dirty_end = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
+ (end - start));
+ if (dirty_end < 0) {
+ dirty_end = end;
+ }
+
+ if (s->skip_unallocated) {
+ ret = block_copy_reset_unallocated(s, start, &status_bytes);
+ if (ret == 0) {
+ trace_block_copy_skip_range(s, start, status_bytes);
+ start += status_bytes;
+ continue;
+ }
+ /* Clamp to known allocated region */
+ dirty_end = MIN(dirty_end, start + status_bytes);
+ }
+
+ trace_block_copy_process(s, start);
+
+ if (s->use_copy_range) {
+ ret = block_copy_with_offload(s, start, dirty_end);
+ if (ret < 0) {
+ s->use_copy_range = false;
+ }
+ }
+ if (!s->use_copy_range) {
+ ret = block_copy_with_bounce_buffer(s, start, dirty_end,
+ error_is_read, &bounce_buffer);
+ }
+ if (ret < 0) {
+ break;
+ }
+
+ start += ret;
+ s->progress_bytes_callback(ret, s->progress_opaque);
+ ret = 0;
+ }
+
+ if (bounce_buffer) {
+ qemu_vfree(bounce_buffer);
+ }
+
+ block_copy_inflight_req_end(&req);
+
+ return ret;
+}
diff --git a/block/file-posix.c b/block/file-posix.c
index f12c06de2d..695fcf740d 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -161,6 +161,11 @@ typedef struct BDRVRawState {
bool needs_alignment;
bool drop_cache;
bool check_cache_dropped;
+ struct {
+ uint64_t discard_nb_ok;
+ uint64_t discard_nb_failed;
+ uint64_t discard_bytes_ok;
+ } stats;
PRManager *pr_mgr;
} BDRVRawState;
@@ -2660,11 +2665,22 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs,
#endif /* !__linux__ */
}
+static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret)
+{
+ if (ret) {
+ s->stats.discard_nb_failed++;
+ } else {
+ s->stats.discard_nb_ok++;
+ s->stats.discard_bytes_ok += nbytes;
+ }
+}
+
static coroutine_fn int
raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev)
{
BDRVRawState *s = bs->opaque;
RawPosixAIOData acb;
+ int ret;
acb = (RawPosixAIOData) {
.bs = bs,
@@ -2678,7 +2694,9 @@ raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev)
acb.aio_type |= QEMU_AIO_BLKDEV;
}
- return raw_thread_pool_submit(bs, handle_aiocb_discard, &acb);
+ ret = raw_thread_pool_submit(bs, handle_aiocb_discard, &acb);
+ raw_account_discard(s, bytes, ret);
+ return ret;
}
static coroutine_fn int
@@ -2735,6 +2753,36 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0;
}
+static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ return (BlockStatsSpecificFile) {
+ .discard_nb_ok = s->stats.discard_nb_ok,
+ .discard_nb_failed = s->stats.discard_nb_failed,
+ .discard_bytes_ok = s->stats.discard_bytes_ok,
+ };
+}
+
+static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
+{
+ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
+
+ stats->driver = BLOCKDEV_DRIVER_FILE;
+ stats->u.file = get_blockstats_specific_file(bs);
+
+ return stats;
+}
+
+static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs)
+{
+ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
+
+ stats->driver = BLOCKDEV_DRIVER_HOST_DEVICE;
+ stats->u.host_device = get_blockstats_specific_file(bs);
+
+ return stats;
+}
+
static QemuOptsList raw_create_opts = {
.name = "raw-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
@@ -2942,6 +2990,7 @@ BlockDriver bdrv_file = {
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_get_specific_stats = raw_get_specific_stats,
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
@@ -3301,10 +3350,12 @@ static int fd_open(BlockDriverState *bs)
static coroutine_fn int
hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
{
+ BDRVRawState *s = bs->opaque;
int ret;
ret = fd_open(bs);
if (ret < 0) {
+ raw_account_discard(s, bytes, ret);
return ret;
}
return raw_do_pdiscard(bs, offset, bytes, true);
@@ -3418,6 +3469,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_get_specific_stats = hdev_get_specific_stats,
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
diff --git a/block/nbd.c b/block/nbd.c
index 813c40d8f0..fd78e5f330 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -1158,6 +1158,18 @@ static int coroutine_fn nbd_client_co_block_status(
BDRV_BLOCK_OFFSET_VALID;
}
+static int nbd_client_reopen_prepare(BDRVReopenState *state,
+ BlockReopenQueue *queue, Error **errp)
+{
+ BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque;
+
+ if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) {
+ error_setg(errp, "Can't reopen read-only NBD mount as read/write");
+ return -EACCES;
+ }
+ return 0;
+}
+
static void nbd_client_close(BlockDriverState *bs)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
@@ -1798,6 +1810,7 @@ static BlockDriver bdrv_nbd = {
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
+ .bdrv_reopen_prepare = nbd_client_reopen_prepare,
.bdrv_co_preadv = nbd_client_co_preadv,
.bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
@@ -1820,6 +1833,7 @@ static BlockDriver bdrv_nbd_tcp = {
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
+ .bdrv_reopen_prepare = nbd_client_reopen_prepare,
.bdrv_co_preadv = nbd_client_co_preadv,
.bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
@@ -1842,6 +1856,7 @@ static BlockDriver bdrv_nbd_unix = {
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
+ .bdrv_reopen_prepare = nbd_client_reopen_prepare,
.bdrv_co_preadv = nbd_client_co_preadv,
.bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
diff --git a/block/qapi.c b/block/qapi.c
index 7ee2ee065d..9a5d0c9b27 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -440,24 +440,30 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
+ ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP];
ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
+ ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP];
ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
+ ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP];
ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
ds->invalid_flush_operations =
stats->invalid_ops[BLOCK_ACCT_FLUSH];
+ ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP];
ds->rd_merged = stats->merged[BLOCK_ACCT_READ];
ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
+ ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP];
ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
+ ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP];
ds->has_idle_time_ns = stats->last_access_time_ns > 0;
if (ds->has_idle_time_ns) {
@@ -537,6 +543,11 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
+ s->driver_specific = bdrv_get_specific_stats(bs);
+ if (s->driver_specific) {
+ s->has_driver_specific = true;
+ }
+
if (bs->file) {
s->has_parent = true;
s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
diff --git a/block/qcow2.c b/block/qcow2.c
index 4d16393e61..7961c05783 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -41,6 +41,7 @@
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "crypto.h"
+#include "block/aio_task.h"
/*
Differences with QCOW:
@@ -1972,20 +1973,184 @@ out:
return ret;
}
+static coroutine_fn int
+qcow2_co_preadv_encrypted(BlockDriverState *bs,
+ uint64_t file_cluster_offset,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov,
+ uint64_t qiov_offset)
+{
+ int ret;
+ BDRVQcow2State *s = bs->opaque;
+ uint8_t *buf;
+
+ assert(bs->encrypted && s->crypto);
+ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+
+ /*
+ * For encrypted images, read everything into a temporary
+ * contiguous buffer on which the AES functions can work.
+ * Also, decryption in a separate buffer is better as it
+ * prevents the guest from learning information about the
+ * encrypted nature of the virtual disk.
+ */
+
+ buf = qemu_try_blockalign(s->data_file->bs, bytes);
+ if (buf == NULL) {
+ return -ENOMEM;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
+ ret = bdrv_co_pread(s->data_file,
+ file_cluster_offset + offset_into_cluster(s, offset),
+ bytes, buf, 0);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
+ assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
+ if (qcow2_co_decrypt(bs,
+ file_cluster_offset + offset_into_cluster(s, offset),
+ offset, buf, bytes) < 0)
+ {
+ ret = -EIO;
+ goto fail;
+ }
+ qemu_iovec_from_buf(qiov, qiov_offset, buf, bytes);
+
+fail:
+ qemu_vfree(buf);
+
+ return ret;
+}
+
+typedef struct Qcow2AioTask {
+ AioTask task;
+
+ BlockDriverState *bs;
+ QCow2ClusterType cluster_type; /* only for read */
+ uint64_t file_cluster_offset;
+ uint64_t offset;
+ uint64_t bytes;
+ QEMUIOVector *qiov;
+ uint64_t qiov_offset;
+ QCowL2Meta *l2meta; /* only for write */
+} Qcow2AioTask;
+
+static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task);
+static coroutine_fn int qcow2_add_task(BlockDriverState *bs,
+ AioTaskPool *pool,
+ AioTaskFunc func,
+ QCow2ClusterType cluster_type,
+ uint64_t file_cluster_offset,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov,
+ size_t qiov_offset,
+ QCowL2Meta *l2meta)
+{
+ Qcow2AioTask local_task;
+ Qcow2AioTask *task = pool ? g_new(Qcow2AioTask, 1) : &local_task;
+
+ *task = (Qcow2AioTask) {
+ .task.func = func,
+ .bs = bs,
+ .cluster_type = cluster_type,
+ .qiov = qiov,
+ .file_cluster_offset = file_cluster_offset,
+ .offset = offset,
+ .bytes = bytes,
+ .qiov_offset = qiov_offset,
+ .l2meta = l2meta,
+ };
+
+ trace_qcow2_add_task(qemu_coroutine_self(), bs, pool,
+ func == qcow2_co_preadv_task_entry ? "read" : "write",
+ cluster_type, file_cluster_offset, offset, bytes,
+ qiov, qiov_offset);
+
+ if (!pool) {
+ return func(&task->task);
+ }
+
+ aio_task_pool_start_task(pool, &task->task);
+
+ return 0;
+}
+
+static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs,
+ QCow2ClusterType cluster_type,
+ uint64_t file_cluster_offset,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov,
+ size_t qiov_offset)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int offset_in_cluster = offset_into_cluster(s, offset);
+
+ switch (cluster_type) {
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ case QCOW2_CLUSTER_ZERO_ALLOC:
+ /* Both zero types are handled in qcow2_co_preadv_part */
+ g_assert_not_reached();
+
+ case QCOW2_CLUSTER_UNALLOCATED:
+ assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
+ return bdrv_co_preadv_part(bs->backing, offset, bytes,
+ qiov, qiov_offset, 0);
+
+ case QCOW2_CLUSTER_COMPRESSED:
+ return qcow2_co_preadv_compressed(bs, file_cluster_offset,
+ offset, bytes, qiov, qiov_offset);
+
+ case QCOW2_CLUSTER_NORMAL:
+ if ((file_cluster_offset & 511) != 0) {
+ return -EIO;
+ }
+
+ if (bs->encrypted) {
+ return qcow2_co_preadv_encrypted(bs, file_cluster_offset,
+ offset, bytes, qiov, qiov_offset);
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
+ return bdrv_co_preadv_part(s->data_file,
+ file_cluster_offset + offset_in_cluster,
+ bytes, qiov, qiov_offset, 0);
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_assert_not_reached();
+}
+
+static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task)
+{
+ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task);
+
+ assert(!t->l2meta);
+
+ return qcow2_co_preadv_task(t->bs, t->cluster_type, t->file_cluster_offset,
+ t->offset, t->bytes, t->qiov, t->qiov_offset);
+}
+
static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov,
size_t qiov_offset, int flags)
{
BDRVQcow2State *s = bs->opaque;
- int offset_in_cluster;
- int ret;
+ int ret = 0;
unsigned int cur_bytes; /* number of bytes in current iteration */
uint64_t cluster_offset = 0;
- uint8_t *cluster_data = NULL;
-
- while (bytes != 0) {
+ AioTaskPool *aio = NULL;
+ while (bytes != 0 && aio_task_pool_status(aio) == 0) {
/* prepare next request */
cur_bytes = MIN(bytes, INT_MAX);
if (s->crypto) {
@@ -1997,110 +2162,39 @@ static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs,
ret = qcow2_get_cluster_offset(bs, offset, &cur_bytes, &cluster_offset);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
- goto fail;
+ goto out;
}
- offset_in_cluster = offset_into_cluster(s, offset);
-
- switch (ret) {
- case QCOW2_CLUSTER_UNALLOCATED:
-
- if (bs->backing) {
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
- ret = bdrv_co_preadv_part(bs->backing, offset, cur_bytes,
- qiov, qiov_offset, 0);
- if (ret < 0) {
- goto fail;
- }
- } else {
- /* Note: in this case, no need to wait */
- qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
- }
- break;
-
- case QCOW2_CLUSTER_ZERO_PLAIN:
- case QCOW2_CLUSTER_ZERO_ALLOC:
+ if (ret == QCOW2_CLUSTER_ZERO_PLAIN ||
+ ret == QCOW2_CLUSTER_ZERO_ALLOC ||
+ (ret == QCOW2_CLUSTER_UNALLOCATED && !bs->backing))
+ {
qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
- break;
-
- case QCOW2_CLUSTER_COMPRESSED:
- ret = qcow2_co_preadv_compressed(bs, cluster_offset,
- offset, cur_bytes,
- qiov, qiov_offset);
- if (ret < 0) {
- goto fail;
- }
-
- break;
-
- case QCOW2_CLUSTER_NORMAL:
- if ((cluster_offset & 511) != 0) {
- ret = -EIO;
- goto fail;
+ } else {
+ if (!aio && cur_bytes != bytes) {
+ aio = aio_task_pool_new(QCOW2_MAX_WORKERS);
}
-
- if (bs->encrypted) {
- assert(s->crypto);
-
- /*
- * For encrypted images, read everything into a temporary
- * contiguous buffer on which the AES functions can work.
- */
- if (!cluster_data) {
- cluster_data =
- qemu_try_blockalign(s->data_file->bs,
- QCOW_MAX_CRYPT_CLUSTERS
- * s->cluster_size);
- if (cluster_data == NULL) {
- ret = -ENOMEM;
- goto fail;
- }
- }
-
- assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
-
- BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
- ret = bdrv_co_pread(s->data_file,
- cluster_offset + offset_in_cluster,
- cur_bytes, cluster_data, 0);
- if (ret < 0) {
- goto fail;
- }
-
- assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
- assert(QEMU_IS_ALIGNED(cur_bytes, BDRV_SECTOR_SIZE));
- if (qcow2_co_decrypt(bs, cluster_offset + offset_in_cluster,
- offset,
- cluster_data, cur_bytes) < 0) {
- ret = -EIO;
- goto fail;
- }
- qemu_iovec_from_buf(qiov, qiov_offset, cluster_data, cur_bytes);
- } else {
- BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
- ret = bdrv_co_preadv_part(s->data_file,
- cluster_offset + offset_in_cluster,
- cur_bytes, qiov, qiov_offset, 0);
- if (ret < 0) {
- goto fail;
- }
+ ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, ret,
+ cluster_offset, offset, cur_bytes,
+ qiov, qiov_offset, NULL);
+ if (ret < 0) {
+ goto out;
}
- break;
-
- default:
- g_assert_not_reached();
- ret = -EIO;
- goto fail;
}
bytes -= cur_bytes;
offset += cur_bytes;
qiov_offset += cur_bytes;
}
- ret = 0;
-fail:
- qemu_vfree(cluster_data);
+out:
+ if (aio) {
+ aio_task_pool_wait_all(aio);
+ if (ret == 0) {
+ ret = aio_task_pool_status(aio);
+ }
+ g_free(aio);
+ }
return ret;
}
@@ -2225,6 +2319,99 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
return 0;
}
+/*
+ * qcow2_co_pwritev_task
+ * Called with s->lock unlocked
+ * l2meta - if not NULL, qcow2_co_pwritev_task() will consume it. Caller must
+ * not use it somehow after qcow2_co_pwritev_task() call
+ */
+static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs,
+ uint64_t file_cluster_offset,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov,
+ uint64_t qiov_offset,
+ QCowL2Meta *l2meta)
+{
+ int ret;
+ BDRVQcow2State *s = bs->opaque;
+ void *crypt_buf = NULL;
+ int offset_in_cluster = offset_into_cluster(s, offset);
+ QEMUIOVector encrypted_qiov;
+
+ if (bs->encrypted) {
+ assert(s->crypto);
+ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+ crypt_buf = qemu_try_blockalign(bs->file->bs, bytes);
+ if (crypt_buf == NULL) {
+ ret = -ENOMEM;
+ goto out_unlocked;
+ }
+ qemu_iovec_to_buf(qiov, qiov_offset, crypt_buf, bytes);
+
+ if (qcow2_co_encrypt(bs, file_cluster_offset + offset_in_cluster,
+ offset, crypt_buf, bytes) < 0)
+ {
+ ret = -EIO;
+ goto out_unlocked;
+ }
+
+ qemu_iovec_init_buf(&encrypted_qiov, crypt_buf, bytes);
+ qiov = &encrypted_qiov;
+ qiov_offset = 0;
+ }
+
+ /* Try to efficiently initialize the physical space with zeroes */
+ ret = handle_alloc_space(bs, l2meta);
+ if (ret < 0) {
+ goto out_unlocked;
+ }
+
+ /*
+ * If we need to do COW, check if it's possible to merge the
+ * writing of the guest data together with that of the COW regions.
+ * If it's not possible (or not necessary) then write the
+ * guest data now.
+ */
+ if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) {
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
+ trace_qcow2_writev_data(qemu_coroutine_self(),
+ file_cluster_offset + offset_in_cluster);
+ ret = bdrv_co_pwritev_part(s->data_file,
+ file_cluster_offset + offset_in_cluster,
+ bytes, qiov, qiov_offset, 0);
+ if (ret < 0) {
+ goto out_unlocked;
+ }
+ }
+
+ qemu_co_mutex_lock(&s->lock);
+
+ ret = qcow2_handle_l2meta(bs, &l2meta, true);
+ goto out_locked;
+
+out_unlocked:
+ qemu_co_mutex_lock(&s->lock);
+
+out_locked:
+ qcow2_handle_l2meta(bs, &l2meta, false);
+ qemu_co_mutex_unlock(&s->lock);
+
+ qemu_vfree(crypt_buf);
+
+ return ret;
+}
+
+static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task)
+{
+ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task);
+
+ assert(!t->cluster_type);
+
+ return qcow2_co_pwritev_task(t->bs, t->file_cluster_offset,
+ t->offset, t->bytes, t->qiov, t->qiov_offset,
+ t->l2meta);
+}
+
static coroutine_fn int qcow2_co_pwritev_part(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, size_t qiov_offset, int flags)
@@ -2234,16 +2421,12 @@ static coroutine_fn int qcow2_co_pwritev_part(
int ret;
unsigned int cur_bytes; /* number of sectors in current iteration */
uint64_t cluster_offset;
- QEMUIOVector encrypted_qiov;
- uint64_t bytes_done = 0;
- uint8_t *cluster_data = NULL;
QCowL2Meta *l2meta = NULL;
+ AioTaskPool *aio = NULL;
trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes);
- qemu_co_mutex_lock(&s->lock);
-
- while (bytes != 0) {
+ while (bytes != 0 && aio_task_pool_status(aio) == 0) {
l2meta = NULL;
@@ -2256,6 +2439,8 @@ static coroutine_fn int qcow2_co_pwritev_part(
- offset_in_cluster);
}
+ qemu_co_mutex_lock(&s->lock);
+
ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
&cluster_offset, &l2meta);
if (ret < 0) {
@@ -2273,73 +2458,24 @@ static coroutine_fn int qcow2_co_pwritev_part(
qemu_co_mutex_unlock(&s->lock);
- if (bs->encrypted) {
- assert(s->crypto);
- if (!cluster_data) {
- cluster_data = qemu_try_blockalign(bs->file->bs,
- QCOW_MAX_CRYPT_CLUSTERS
- * s->cluster_size);
- if (cluster_data == NULL) {
- ret = -ENOMEM;
- goto out_unlocked;
- }
- }
-
- assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
- qemu_iovec_to_buf(qiov, qiov_offset + bytes_done,
- cluster_data, cur_bytes);
-
- if (qcow2_co_encrypt(bs, cluster_offset + offset_in_cluster, offset,
- cluster_data, cur_bytes) < 0) {
- ret = -EIO;
- goto out_unlocked;
- }
-
- qemu_iovec_init_buf(&encrypted_qiov, cluster_data, cur_bytes);
+ if (!aio && cur_bytes != bytes) {
+ aio = aio_task_pool_new(QCOW2_MAX_WORKERS);
}
-
- /* Try to efficiently initialize the physical space with zeroes */
- ret = handle_alloc_space(bs, l2meta);
+ ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0,
+ cluster_offset, offset, cur_bytes,
+ qiov, qiov_offset, l2meta);
+ l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */
if (ret < 0) {
- goto out_unlocked;
- }
-
- /* If we need to do COW, check if it's possible to merge the
- * writing of the guest data together with that of the COW regions.
- * If it's not possible (or not necessary) then write the
- * guest data now. */
- if (!merge_cow(offset, cur_bytes,
- bs->encrypted ? &encrypted_qiov : qiov,
- bs->encrypted ? 0 : qiov_offset + bytes_done, l2meta))
- {
- BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
- trace_qcow2_writev_data(qemu_coroutine_self(),
- cluster_offset + offset_in_cluster);
- ret = bdrv_co_pwritev_part(
- s->data_file, cluster_offset + offset_in_cluster, cur_bytes,
- bs->encrypted ? &encrypted_qiov : qiov,
- bs->encrypted ? 0 : qiov_offset + bytes_done, 0);
- if (ret < 0) {
- goto out_unlocked;
- }
- }
-
- qemu_co_mutex_lock(&s->lock);
-
- ret = qcow2_handle_l2meta(bs, &l2meta, true);
- if (ret) {
- goto out_locked;
+ goto fail_nometa;
}
bytes -= cur_bytes;
offset += cur_bytes;
- bytes_done += cur_bytes;
+ qiov_offset += cur_bytes;
trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes);
}
ret = 0;
- goto out_locked;
-out_unlocked:
qemu_co_mutex_lock(&s->lock);
out_locked:
@@ -2347,7 +2483,15 @@ out_locked:
qemu_co_mutex_unlock(&s->lock);
- qemu_vfree(cluster_data);
+fail_nometa:
+ if (aio) {
+ aio_task_pool_wait_all(aio);
+ if (ret == 0) {
+ ret = aio_task_pool_status(aio);
+ }
+ g_free(aio);
+ }
+
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
return ret;
diff --git a/block/qcow2.h b/block/qcow2.h
index a488d761ff..f51f478e34 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -65,6 +65,9 @@
#define QCOW2_MAX_BITMAPS 65535
#define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS)
+/* Maximum of parallel sub-request per guest request */
+#define QCOW2_MAX_WORKERS 8
+
/* indicate that the refcount of the referenced cluster is exactly one. */
#define QCOW_OFLAG_COPIED (1ULL << 63)
/* indicate that the cluster is compressed (they never have the copied flag) */
diff --git a/block/replication.c b/block/replication.c
index 936b2f8b5a..99532ce521 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -543,7 +543,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,
+ 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
backup_job_completed, bs, NULL, &local_err);
diff --git a/block/trace-events b/block/trace-events
index 04209f058d..b8d70f5242 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -40,12 +40,14 @@ mirror_yield_in_flight(void *s, int64_t offset, int in_flight) "s %p offset %" P
# backup.c
backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64
backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d"
-backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
-backup_do_cow_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
-backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
-backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
-backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
-backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
+
+# block-copy.c
+block_copy_skip(void *bcs, int64_t start) "bcs %p start %"PRId64
+block_copy_skip_range(void *bcs, int64_t start, uint64_t bytes) "bcs %p start %"PRId64" bytes %"PRId64
+block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64
+block_copy_with_bounce_buffer_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
+block_copy_with_bounce_buffer_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
+block_copy_with_offload_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
# ../blockdev.c
qmp_block_job_cancel(void *job) "job %p"
@@ -62,6 +64,7 @@ file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "
file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64
# qcow2.c
+qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t file_cluster_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu"
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
qcow2_writev_done_req(void *co, int ret) "co %p ret %d"
qcow2_writev_start_part(void *co) "co %p"
diff --git a/blockdev.c b/blockdev.c
index fbef6845c8..f89e48fc79 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3601,6 +3601,7 @@ static BlockJob *do_backup_common(BackupCommon *backup,
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
backup->sync, bmap, backup->bitmap_mode,
backup->compress,
+ backup->filter_node_name,
backup->on_source_error,
backup->on_target_error,
job_flags, NULL, NULL, txn, errp);
diff --git a/docs/devel/rcu.txt b/docs/devel/rcu.txt
index c84e7f42b2..d83fed2f79 100644
--- a/docs/devel/rcu.txt
+++ b/docs/devel/rcu.txt
@@ -187,6 +187,22 @@ The following APIs must be used before RCU is used in a thread:
Note that these APIs are relatively heavyweight, and should _not_ be
nested.
+Convenience macros
+==================
+
+Two macros are provided that automatically release the read lock at the
+end of the scope.
+
+ RCU_READ_LOCK_GUARD()
+
+ Takes the lock and will release it at the end of the block it's
+ used in.
+
+ WITH_RCU_READ_LOCK_GUARD() { code }
+
+ Is used at the head of a block to protect the code within the block.
+
+Note that 'goto'ing out of the guarded block will also drop the lock.
DIFFERENCES WITH LINUX
======================
diff --git a/exec.c b/exec.c
index bdcfcdff3f..fb0943cfed 100644
--- a/exec.c
+++ b/exec.c
@@ -1037,16 +1037,14 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs)
return;
}
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
mr = address_space_translate(as, addr, &addr, &l, false, attrs);
if (!(memory_region_is_ram(mr)
|| memory_region_is_romd(mr))) {
- rcu_read_unlock();
return;
}
ram_addr = memory_region_get_ram_addr(mr) + addr;
tb_invalidate_phys_page_range(ram_addr, ram_addr + 1);
- rcu_read_unlock();
}
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
@@ -1332,14 +1330,13 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length)
end = TARGET_PAGE_ALIGN(start + length);
start &= TARGET_PAGE_MASK;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
block = qemu_get_ram_block(start);
assert(block == qemu_get_ram_block(end - 1));
start1 = (uintptr_t)ramblock_ptr(block, start - block->offset);
CPU_FOREACH(cpu) {
tlb_reset_dirty(cpu, start1, length);
}
- rcu_read_unlock();
}
/* Note: start and end must be within the same ram block. */
@@ -1360,30 +1357,29 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+ ramblock = qemu_get_ram_block(start);
+ /* Range sanity check on the ramblock */
+ assert(start >= ramblock->offset &&
+ start + length <= ramblock->offset + ramblock->used_length);
- blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
- ramblock = qemu_get_ram_block(start);
- /* Range sanity check on the ramblock */
- assert(start >= ramblock->offset &&
- start + length <= ramblock->offset + ramblock->used_length);
+ while (page < end) {
+ unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long num = MIN(end - page,
+ DIRTY_MEMORY_BLOCK_SIZE - offset);
- while (page < end) {
- unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);
+ dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx],
+ offset, num);
+ page += num;
+ }
- dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx],
- offset, num);
- page += num;
+ mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset;
+ mr_size = (end - page) << TARGET_PAGE_BITS;
+ memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size);
}
- mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset;
- mr_size = (end - page) << TARGET_PAGE_BITS;
- memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size);
-
- rcu_read_unlock();
-
if (dirty && tcg_enabled()) {
tlb_reset_dirty_range_all(start, length);
}
@@ -1411,28 +1407,27 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
end = last >> TARGET_PAGE_BITS;
dest = 0;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
- blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+ while (page < end) {
+ unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long num = MIN(end - page,
+ DIRTY_MEMORY_BLOCK_SIZE - offset);
- while (page < end) {
- unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);
-
- assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
- assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL)));
- offset >>= BITS_PER_LEVEL;
+ assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
+ assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL)));
+ offset >>= BITS_PER_LEVEL;
- bitmap_copy_and_clear_atomic(snap->dirty + dest,
- blocks->blocks[idx] + offset,
- num);
- page += num;
- dest += num >> BITS_PER_LEVEL;
+ bitmap_copy_and_clear_atomic(snap->dirty + dest,
+ blocks->blocks[idx] + offset,
+ num);
+ page += num;
+ dest += num >> BITS_PER_LEVEL;
+ }
}
- rcu_read_unlock();
-
if (tcg_enabled()) {
tlb_reset_dirty_range_all(start, length);
}
@@ -1643,7 +1638,7 @@ void ram_block_dump(Monitor *mon)
RAMBlock *block;
char *psize;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
monitor_printf(mon, "%24s %8s %18s %18s %18s\n",
"Block Name", "PSize", "Offset", "Used", "Total");
RAMBLOCK_FOREACH(block) {
@@ -1655,7 +1650,6 @@ void ram_block_dump(Monitor *mon)
(uint64_t)block->max_length);
g_free(psize);
}
- rcu_read_unlock();
}
#ifdef __linux__
@@ -2009,11 +2003,10 @@ static unsigned long last_ram_page(void)
RAMBlock *block;
ram_addr_t last = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBLOCK_FOREACH(block) {
last = MAX(last, block->offset + block->max_length);
}
- rcu_read_unlock();
return last >> TARGET_PAGE_BITS;
}
@@ -2100,7 +2093,7 @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev)
}
pstrcat(new_block->idstr, sizeof(new_block->idstr), name);
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBLOCK_FOREACH(block) {
if (block != new_block &&
!strcmp(block->idstr, new_block->idstr)) {
@@ -2109,7 +2102,6 @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev)
abort();
}
}
- rcu_read_unlock();
}
/* Called with iothread lock held. */
@@ -2651,17 +2643,16 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
if (xen_enabled()) {
ram_addr_t ram_addr;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
ram_addr = xen_ram_addr_from_mapcache(ptr);
block = qemu_get_ram_block(ram_addr);
if (block) {
*offset = ram_addr - block->offset;
}
- rcu_read_unlock();
return block;
}
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
block = atomic_rcu_read(&ram_list.mru_block);
if (block && block->host && host - block->host < block->max_length) {
goto found;
@@ -2677,7 +2668,6 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
}
}
- rcu_read_unlock();
return NULL;
found:
@@ -2685,7 +2675,6 @@ found:
if (round_offset) {
*offset &= TARGET_PAGE_MASK;
}
- rcu_read_unlock();
return block;
}
@@ -3281,10 +3270,9 @@ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
FlatView *fv;
if (len > 0) {
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
result = flatview_read(fv, addr, attrs, buf, len);
- rcu_read_unlock();
}
return result;
@@ -3298,10 +3286,9 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr,
FlatView *fv;
if (len > 0) {
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
result = flatview_write(fv, addr, attrs, buf, len);
- rcu_read_unlock();
}
return result;
@@ -3341,7 +3328,7 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as,
hwaddr addr1;
MemoryRegion *mr;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
while (len > 0) {
l = len;
mr = address_space_translate(as, addr, &addr1, &l, true, attrs);
@@ -3366,7 +3353,6 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as,
buf += l;
addr += l;
}
- rcu_read_unlock();
return MEMTX_OK;
}
@@ -3511,10 +3497,9 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr,
FlatView *fv;
bool result;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
result = flatview_access_valid(fv, addr, len, is_write, attrs);
- rcu_read_unlock();
return result;
}
@@ -3569,13 +3554,12 @@ void *address_space_map(AddressSpace *as,
}
l = len;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
if (!memory_access_is_direct(mr, is_write)) {
if (atomic_xchg(&bounce.in_use, true)) {
- rcu_read_unlock();
return NULL;
}
/* Avoid unbounded allocations */
@@ -3591,7 +3575,6 @@ void *address_space_map(AddressSpace *as,
bounce.buffer, l);
}
- rcu_read_unlock();
*plen = l;
return bounce.buffer;
}
@@ -3601,7 +3584,6 @@ void *address_space_map(AddressSpace *as,
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
l, is_write, attrs);
ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true);
- rcu_read_unlock();
return ptr;
}
@@ -3869,13 +3851,12 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr)
hwaddr l = 1;
bool res;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
mr = address_space_translate(&address_space_memory,
phys_addr, &phys_addr, &l, false,
MEMTXATTRS_UNSPECIFIED);
res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr));
- rcu_read_unlock();
return res;
}
@@ -3884,14 +3865,13 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
RAMBlock *block;
int ret = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBLOCK_FOREACH(block) {
ret = func(block, opaque);
if (ret) {
break;
}
}
- rcu_read_unlock();
return ret;
}
diff --git a/fsdev/9p-marshal.h b/fsdev/9p-marshal.h
index c8823d878f..8f3babb60a 100644
--- a/fsdev/9p-marshal.h
+++ b/fsdev/9p-marshal.h
@@ -9,9 +9,9 @@ typedef struct V9fsString
typedef struct V9fsQID
{
- int8_t type;
- int32_t version;
- int64_t path;
+ uint8_t type;
+ uint32_t version;
+ uint64_t path;
} V9fsQID;
typedef struct V9fsStat
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index c757c8099f..f2f7772c86 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -59,6 +59,11 @@ typedef struct ExtendedOps {
#define V9FS_RDONLY 0x00000040
#define V9FS_PROXY_SOCK_FD 0x00000080
#define V9FS_PROXY_SOCK_NAME 0x00000100
+/*
+ * multidevs option (either one of the two applies exclusively)
+ */
+#define V9FS_REMAP_INODES 0x00000200
+#define V9FS_FORBID_MULTIDEVS 0x00000400
#define V9FS_SEC_MASK 0x0000003C
diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c
index 7c31ffffaf..07a18c6e48 100644
--- a/fsdev/qemu-fsdev-opts.c
+++ b/fsdev/qemu-fsdev-opts.c
@@ -31,7 +31,9 @@ static QemuOptsList qemu_fsdev_opts = {
}, {
.name = "readonly",
.type = QEMU_OPT_BOOL,
-
+ }, {
+ .name = "multidevs",
+ .type = QEMU_OPT_STRING,
}, {
.name = "socket",
.type = QEMU_OPT_STRING,
@@ -76,6 +78,9 @@ static QemuOptsList qemu_virtfs_opts = {
.name = "readonly",
.type = QEMU_OPT_BOOL,
}, {
+ .name = "multidevs",
+ .type = QEMU_OPT_STRING,
+ }, {
.name = "socket",
.type = QEMU_OPT_STRING,
}, {
diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c
index fe1d76dc76..5c83a1cc09 100644
--- a/fsdev/qemu-fsdev-throttle.c
+++ b/fsdev/qemu-fsdev-throttle.c
@@ -31,7 +31,7 @@ static void fsdev_throttle_write_timer_cb(void *opaque)
qemu_co_enter_next(&fst->throttled_reqs[true], NULL);
}
-void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp)
+int fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp)
{
throttle_config_init(&fst->cfg);
fst->cfg.buckets[THROTTLE_BPS_TOTAL].avg =
@@ -75,7 +75,7 @@ void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp)
fst->cfg.op_size =
qemu_opt_get_number(opts, "throttling.iops-size", 0);
- throttle_is_valid(&fst->cfg, errp);
+ return throttle_is_valid(&fst->cfg, errp) ? 0 : -1;
}
void fsdev_throttle_init(FsThrottle *fst)
diff --git a/fsdev/qemu-fsdev-throttle.h b/fsdev/qemu-fsdev-throttle.h
index c98e2feab5..a21aecddc7 100644
--- a/fsdev/qemu-fsdev-throttle.h
+++ b/fsdev/qemu-fsdev-throttle.h
@@ -26,7 +26,7 @@ typedef struct FsThrottle {
CoQueue throttled_reqs[2];
} FsThrottle;
-void fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **);
+int fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **);
void fsdev_throttle_init(FsThrottle *);
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index 077a8c4e2b..a9e069c0c7 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -58,6 +58,7 @@ static FsDriverTable FsDrivers[] = {
"writeout",
"fmode",
"dmode",
+ "multidevs",
"throttling.bps-total",
"throttling.bps-read",
"throttling.bps-write",
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index 08e673a79c..4708c0bd89 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -1465,6 +1465,10 @@ static void local_cleanup(FsContext *ctx)
{
LocalData *data = ctx->private;
+ if (!data) {
+ return;
+ }
+
close(data->mountfd);
g_free(data);
}
@@ -1479,6 +1483,7 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
{
const char *sec_model = qemu_opt_get(opts, "security_model");
const char *path = qemu_opt_get(opts, "path");
+ const char *multidevs = qemu_opt_get(opts, "multidevs");
Error *local_err = NULL;
if (!sec_model) {
@@ -1502,13 +1507,32 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
return -1;
}
+ if (multidevs) {
+ if (!strcmp(multidevs, "remap")) {
+ fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
+ fse->export_flags |= V9FS_REMAP_INODES;
+ } else if (!strcmp(multidevs, "forbid")) {
+ fse->export_flags &= ~V9FS_REMAP_INODES;
+ fse->export_flags |= V9FS_FORBID_MULTIDEVS;
+ } else if (!strcmp(multidevs, "warn")) {
+ fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
+ fse->export_flags &= ~V9FS_REMAP_INODES;
+ } else {
+ error_setg(&local_err, "invalid multidevs property '%s'",
+ multidevs);
+ error_append_hint(&local_err, "Valid options are: multidevs="
+ "[remap|forbid|warn]\n");
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ }
+
if (!path) {
error_setg(errp, "path property not set");
return -1;
}
- fsdev_throttle_parse_opts(opts, &fse->fst, &local_err);
- if (local_err) {
+ if (fsdev_throttle_parse_opts(opts, &fse->fst, &local_err)) {
error_propagate_prepend(errp, local_err,
"invalid throttle configuration: ");
return -1;
diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c
index 57a8c1c808..97ab9c58a5 100644
--- a/hw/9pfs/9p-proxy.c
+++ b/hw/9pfs/9p-proxy.c
@@ -1185,6 +1185,10 @@ static void proxy_cleanup(FsContext *ctx)
{
V9fsProxy *proxy = ctx->private;
+ if (!proxy) {
+ return;
+ }
+
g_free(proxy->out_iovec.iov_base);
g_free(proxy->in_iovec.iov_base);
if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index cce2366219..37abcdb71e 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -26,6 +26,8 @@
#include "trace.h"
#include "migration/blocker.h"
#include "sysemu/qtest.h"
+#include "qemu/xxhash.h"
+#include <math.h>
int open_fd_hw;
int total_open_fd;
@@ -572,14 +574,374 @@ static void coroutine_fn virtfs_reset(V9fsPDU *pdu)
P9_STAT_MODE_NAMED_PIPE | \
P9_STAT_MODE_SOCKET)
-/* This is the algorithm from ufs in spfs */
-static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
+/* Mirrors all bits of a byte. So e.g. binary 10100000 would become 00000101. */
+static inline uint8_t mirror8bit(uint8_t byte)
{
+ return (byte * 0x0202020202ULL & 0x010884422010ULL) % 1023;
+}
+
+/* Same as mirror8bit() just for a 64 bit data type instead for a byte. */
+static inline uint64_t mirror64bit(uint64_t value)
+{
+ return ((uint64_t)mirror8bit(value & 0xff) << 56) |
+ ((uint64_t)mirror8bit((value >> 8) & 0xff) << 48) |
+ ((uint64_t)mirror8bit((value >> 16) & 0xff) << 40) |
+ ((uint64_t)mirror8bit((value >> 24) & 0xff) << 32) |
+ ((uint64_t)mirror8bit((value >> 32) & 0xff) << 24) |
+ ((uint64_t)mirror8bit((value >> 40) & 0xff) << 16) |
+ ((uint64_t)mirror8bit((value >> 48) & 0xff) << 8) |
+ ((uint64_t)mirror8bit((value >> 56) & 0xff));
+}
+
+/**
+ * @brief Parameter k for the Exponential Golomb algorihm to be used.
+ *
+ * The smaller this value, the smaller the minimum bit count for the Exp.
+ * Golomb generated affixes will be (at lowest index) however for the
+ * price of having higher maximum bit count of generated affixes (at highest
+ * index). Likewise increasing this parameter yields in smaller maximum bit
+ * count for the price of having higher minimum bit count.
+ *
+ * In practice that means: a good value for k depends on the expected amount
+ * of devices to be exposed by one export. For a small amount of devices k
+ * should be small, for a large amount of devices k might be increased
+ * instead. The default of k=0 should be fine for most users though.
+ *
+ * @b IMPORTANT: In case this ever becomes a runtime parameter; the value of
+ * k should not change as long as guest is still running! Because that would
+ * cause completely different inode numbers to be generated on guest.
+ */
+#define EXP_GOLOMB_K 0
+
+/**
+ * @brief Exponential Golomb algorithm for arbitrary k (including k=0).
+ *
+ * The Exponential Golomb algorithm generates @b prefixes (@b not suffixes!)
+ * with growing length and with the mathematical property of being
+ * "prefix-free". The latter means the generated prefixes can be prepended
+ * in front of arbitrary numbers and the resulting concatenated numbers are
+ * guaranteed to be always unique.
+ *
+ * This is a minor adjustment to the original Exp. Golomb algorithm in the
+ * sense that lowest allowed index (@param n) starts with 1, not with zero.
+ *
+ * @param n - natural number (or index) of the prefix to be generated
+ * (1, 2, 3, ...)
+ * @param k - parameter k of Exp. Golomb algorithm to be used
+ * (see comment on EXP_GOLOMB_K macro for details about k)
+ */
+static VariLenAffix expGolombEncode(uint64_t n, int k)
+{
+ const uint64_t value = n + (1 << k) - 1;
+ const int bits = (int) log2(value) + 1;
+ return (VariLenAffix) {
+ .type = AffixType_Prefix,
+ .value = value,
+ .bits = bits + MAX((bits - 1 - k), 0)
+ };
+}
+
+/**
+ * @brief Converts a suffix into a prefix, or a prefix into a suffix.
+ *
+ * Simply mirror all bits of the affix value, for the purpose to preserve
+ * respectively the mathematical "prefix-free" or "suffix-free" property
+ * after the conversion.
+ *
+ * If a passed prefix is suitable to create unique numbers, then the
+ * returned suffix is suitable to create unique numbers as well (and vice
+ * versa).
+ */
+static VariLenAffix invertAffix(const VariLenAffix *affix)
+{
+ return (VariLenAffix) {
+ .type =
+ (affix->type == AffixType_Suffix) ?
+ AffixType_Prefix : AffixType_Suffix,
+ .value =
+ mirror64bit(affix->value) >>
+ ((sizeof(affix->value) * 8) - affix->bits),
+ .bits = affix->bits
+ };
+}
+
+/**
+ * @brief Generates suffix numbers with "suffix-free" property.
+ *
+ * This is just a wrapper function on top of the Exp. Golomb algorithm.
+ *
+ * Since the Exp. Golomb algorithm generates prefixes, but we need suffixes,
+ * this function converts the Exp. Golomb prefixes into appropriate suffixes
+ * which are still suitable for generating unique numbers.
+ *
+ * @param n - natural number (or index) of the suffix to be generated
+ * (1, 2, 3, ...)
+ */
+static VariLenAffix affixForIndex(uint64_t index)
+{
+ VariLenAffix prefix;
+ prefix = expGolombEncode(index, EXP_GOLOMB_K);
+ return invertAffix(&prefix); /* convert prefix to suffix */
+}
+
+/* creative abuse of tb_hash_func7, which is based on xxhash */
+static uint32_t qpp_hash(QppEntry e)
+{
+ return qemu_xxhash7(e.ino_prefix, e.dev, 0, 0, 0);
+}
+
+static uint32_t qpf_hash(QpfEntry e)
+{
+ return qemu_xxhash7(e.ino, e.dev, 0, 0, 0);
+}
+
+static bool qpd_cmp_func(const void *obj, const void *userp)
+{
+ const QpdEntry *e1 = obj, *e2 = userp;
+ return e1->dev == e2->dev;
+}
+
+static bool qpp_cmp_func(const void *obj, const void *userp)
+{
+ const QppEntry *e1 = obj, *e2 = userp;
+ return e1->dev == e2->dev && e1->ino_prefix == e2->ino_prefix;
+}
+
+static bool qpf_cmp_func(const void *obj, const void *userp)
+{
+ const QpfEntry *e1 = obj, *e2 = userp;
+ return e1->dev == e2->dev && e1->ino == e2->ino;
+}
+
+static void qp_table_remove(void *p, uint32_t h, void *up)
+{
+ g_free(p);
+}
+
+static void qp_table_destroy(struct qht *ht)
+{
+ if (!ht || !ht->map) {
+ return;
+ }
+ qht_iter(ht, qp_table_remove, NULL);
+ qht_destroy(ht);
+}
+
+static void qpd_table_init(struct qht *ht)
+{
+ qht_init(ht, qpd_cmp_func, 1, QHT_MODE_AUTO_RESIZE);
+}
+
+static void qpp_table_init(struct qht *ht)
+{
+ qht_init(ht, qpp_cmp_func, 1, QHT_MODE_AUTO_RESIZE);
+}
+
+static void qpf_table_init(struct qht *ht)
+{
+ qht_init(ht, qpf_cmp_func, 1 << 16, QHT_MODE_AUTO_RESIZE);
+}
+
+/*
+ * Returns how many (high end) bits of inode numbers of the passed fs
+ * device shall be used (in combination with the device number) to
+ * generate hash values for qpp_table entries.
+ *
+ * This function is required if variable length suffixes are used for inode
+ * number mapping on guest level. Since a device may end up having multiple
+ * entries in qpp_table, each entry most probably with a different suffix
+ * length, we thus need this function in conjunction with qpd_table to
+ * "agree" about a fix amount of bits (per device) to be always used for
+ * generating hash values for the purpose of accessing qpp_table in order
+ * get consistent behaviour when accessing qpp_table.
+ */
+static int qid_inode_prefix_hash_bits(V9fsPDU *pdu, dev_t dev)
+{
+ QpdEntry lookup = {
+ .dev = dev
+ }, *val;
+ uint32_t hash = dev;
+ VariLenAffix affix;
+
+ val = qht_lookup(&pdu->s->qpd_table, &lookup, hash);
+ if (!val) {
+ val = g_malloc0(sizeof(QpdEntry));
+ *val = lookup;
+ affix = affixForIndex(pdu->s->qp_affix_next);
+ val->prefix_bits = affix.bits;
+ qht_insert(&pdu->s->qpd_table, val, hash, NULL);
+ pdu->s->qp_ndevices++;
+ }
+ return val->prefix_bits;
+}
+
+/**
+ * @brief Slow / full mapping host inode nr -> guest inode nr.
+ *
+ * This function performs a slower and much more costly remapping of an
+ * original file inode number on host to an appropriate different inode
+ * number on guest. For every (dev, inode) combination on host a new
+ * sequential number is generated, cached and exposed as inode number on
+ * guest.
+ *
+ * This is just a "last resort" fallback solution if the much faster/cheaper
+ * qid_path_suffixmap() failed. In practice this slow / full mapping is not
+ * expected ever to be used at all though.
+ *
+ * @see qid_path_suffixmap() for details
+ *
+ */
+static int qid_path_fullmap(V9fsPDU *pdu, const struct stat *stbuf,
+ uint64_t *path)
+{
+ QpfEntry lookup = {
+ .dev = stbuf->st_dev,
+ .ino = stbuf->st_ino
+ }, *val;
+ uint32_t hash = qpf_hash(lookup);
+ VariLenAffix affix;
+
+ val = qht_lookup(&pdu->s->qpf_table, &lookup, hash);
+
+ if (!val) {
+ if (pdu->s->qp_fullpath_next == 0) {
+ /* no more files can be mapped :'( */
+ error_report_once(
+ "9p: No more prefixes available for remapping inodes from "
+ "host to guest."
+ );
+ return -ENFILE;
+ }
+
+ val = g_malloc0(sizeof(QppEntry));
+ *val = lookup;
+
+ /* new unique inode and device combo */
+ affix = affixForIndex(
+ 1ULL << (sizeof(pdu->s->qp_affix_next) * 8)
+ );
+ val->path = (pdu->s->qp_fullpath_next++ << affix.bits) | affix.value;
+ pdu->s->qp_fullpath_next &= ((1ULL << (64 - affix.bits)) - 1);
+ qht_insert(&pdu->s->qpf_table, val, hash, NULL);
+ }
+
+ *path = val->path;
+ return 0;
+}
+
+/**
+ * @brief Quick mapping host inode nr -> guest inode nr.
+ *
+ * This function performs quick remapping of an original file inode number
+ * on host to an appropriate different inode number on guest. This remapping
+ * of inodes is required to avoid inode nr collisions on guest which would
+ * happen if the 9p export contains more than 1 exported file system (or
+ * more than 1 file system data set), because unlike on host level where the
+ * files would have different device nrs, all files exported by 9p would
+ * share the same device nr on guest (the device nr of the virtual 9p device
+ * that is).
+ *
+ * Inode remapping is performed by chopping off high end bits of the original
+ * inode number from host, shifting the result upwards and then assigning a
+ * generated suffix number for the low end bits, where the same suffix number
+ * will be shared by all inodes with the same device id AND the same high end
+ * bits that have been chopped off. That approach utilizes the fact that inode
+ * numbers very likely share the same high end bits (i.e. due to their common
+ * sequential generation by file systems) and hence we only have to generate
+ * and track a very limited amount of suffixes in practice due to that.
+ *
+ * We generate variable size suffixes for that purpose. The 1st generated
+ * suffix will only have 1 bit and hence we only need to chop off 1 bit from
+ * the original inode number. The subsequent suffixes being generated will
+ * grow in (bit) size subsequently, i.e. the 2nd and 3rd suffix being
+ * generated will have 3 bits and hence we have to chop off 3 bits from their
+ * original inodes, and so on. That approach of using variable length suffixes
+ * (i.e. over fixed size ones) utilizes the fact that in practice only a very
+ * limited amount of devices are shared by the same export (e.g. typically
+ * less than 2 dozen devices per 9p export), so in practice we need to chop
+ * off less bits than with fixed size prefixes and yet are flexible to add
+ * new devices at runtime below host's export directory at any time without
+ * having to reboot guest nor requiring to reconfigure guest for that. And due
+ * to the very limited amount of original high end bits that we chop off that
+ * way, the total amount of suffixes we need to generate is less than by using
+ * fixed size prefixes and hence it also improves performance of the inode
+ * remapping algorithm, and finally has the nice side effect that the inode
+ * numbers on guest will be much smaller & human friendly. ;-)
+ */
+static int qid_path_suffixmap(V9fsPDU *pdu, const struct stat *stbuf,
+ uint64_t *path)
+{
+ const int ino_hash_bits = qid_inode_prefix_hash_bits(pdu, stbuf->st_dev);
+ QppEntry lookup = {
+ .dev = stbuf->st_dev,
+ .ino_prefix = (uint16_t) (stbuf->st_ino >> (64 - ino_hash_bits))
+ }, *val;
+ uint32_t hash = qpp_hash(lookup);
+
+ val = qht_lookup(&pdu->s->qpp_table, &lookup, hash);
+
+ if (!val) {
+ if (pdu->s->qp_affix_next == 0) {
+ /* we ran out of affixes */
+ warn_report_once(
+ "9p: Potential degraded performance of inode remapping"
+ );
+ return -ENFILE;
+ }
+
+ val = g_malloc0(sizeof(QppEntry));
+ *val = lookup;
+
+ /* new unique inode affix and device combo */
+ val->qp_affix_index = pdu->s->qp_affix_next++;
+ val->qp_affix = affixForIndex(val->qp_affix_index);
+ qht_insert(&pdu->s->qpp_table, val, hash, NULL);
+ }
+ /* assuming generated affix to be suffix type, not prefix */
+ *path = (stbuf->st_ino << val->qp_affix.bits) | val->qp_affix.value;
+ return 0;
+}
+
+static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp)
+{
+ int err;
size_t size;
- memset(&qidp->path, 0, sizeof(qidp->path));
- size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
- memcpy(&qidp->path, &stbuf->st_ino, size);
+ if (pdu->s->ctx.export_flags & V9FS_REMAP_INODES) {
+ /* map inode+device to qid path (fast path) */
+ err = qid_path_suffixmap(pdu, stbuf, &qidp->path);
+ if (err == -ENFILE) {
+ /* fast path didn't work, fall back to full map */
+ err = qid_path_fullmap(pdu, stbuf, &qidp->path);
+ }
+ if (err) {
+ return err;
+ }
+ } else {
+ if (pdu->s->dev_id != stbuf->st_dev) {
+ if (pdu->s->ctx.export_flags & V9FS_FORBID_MULTIDEVS) {
+ error_report_once(
+ "9p: Multiple devices detected in same VirtFS export. "
+ "Access of guest to additional devices is (partly) "
+ "denied due to virtfs option 'multidevs=forbid' being "
+ "effective."
+ );
+ return -ENODEV;
+ } else {
+ warn_report_once(
+ "9p: Multiple devices detected in same VirtFS export, "
+ "which might lead to file ID collisions and severe "
+ "misbehaviours on guest! You should either use a "
+ "separate export for each device shared from host or "
+ "use virtfs option 'multidevs=remap'!"
+ );
+ }
+ }
+ memset(&qidp->path, 0, sizeof(qidp->path));
+ size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
+ memcpy(&qidp->path, &stbuf->st_ino, size);
+ }
+
qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
qidp->type = 0;
if (S_ISDIR(stbuf->st_mode)) {
@@ -588,6 +950,8 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
if (S_ISLNK(stbuf->st_mode)) {
qidp->type |= P9_QID_TYPE_SYMLINK;
}
+
+ return 0;
}
static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
@@ -600,10 +964,37 @@ static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
if (err < 0) {
return err;
}
- stat_to_qid(&stbuf, qidp);
+ err = stat_to_qid(pdu, &stbuf, qidp);
+ if (err < 0) {
+ return err;
+ }
return 0;
}
+static int coroutine_fn dirent_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
+ struct dirent *dent, V9fsQID *qidp)
+{
+ struct stat stbuf;
+ V9fsPath path;
+ int err;
+
+ v9fs_path_init(&path);
+
+ err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path);
+ if (err < 0) {
+ goto out;
+ }
+ err = v9fs_co_lstat(pdu, &path, &stbuf);
+ if (err < 0) {
+ goto out;
+ }
+ err = stat_to_qid(pdu, &stbuf, qidp);
+
+out:
+ v9fs_path_free(&path);
+ return err;
+}
+
V9fsPDU *pdu_alloc(V9fsState *s)
{
V9fsPDU *pdu = NULL;
@@ -744,9 +1135,9 @@ static int donttouch_stat(V9fsStat *stat)
{
if (stat->type == -1 &&
stat->dev == -1 &&
- stat->qid.type == -1 &&
- stat->qid.version == -1 &&
- stat->qid.path == -1 &&
+ stat->qid.type == 0xff &&
+ stat->qid.version == (uint32_t) -1 &&
+ stat->qid.path == (uint64_t) -1 &&
stat->mode == -1 &&
stat->atime == -1 &&
stat->mtime == -1 &&
@@ -831,7 +1222,10 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path,
memset(v9stat, 0, sizeof(*v9stat));
- stat_to_qid(stbuf, &v9stat->qid);
+ err = stat_to_qid(pdu, stbuf, &v9stat->qid);
+ if (err < 0) {
+ return err;
+ }
v9stat->mode = stat_to_v9mode(stbuf);
v9stat->atime = stbuf->st_atime;
v9stat->mtime = stbuf->st_mtime;
@@ -892,7 +1286,7 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path,
#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
-static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
+static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
V9fsStatDotl *v9lstat)
{
memset(v9lstat, 0, sizeof(*v9lstat));
@@ -914,7 +1308,7 @@ static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
/* Currently we only support BASIC fields in stat */
v9lstat->st_result_mask = P9_STATS_BASIC;
- stat_to_qid(stbuf, &v9lstat->qid);
+ return stat_to_qid(pdu, stbuf, &v9lstat->qid);
}
static void print_sg(struct iovec *sg, int cnt)
@@ -1116,7 +1510,6 @@ static void coroutine_fn v9fs_getattr(void *opaque)
uint64_t request_mask;
V9fsStatDotl v9stat_dotl;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask);
if (retval < 0) {
@@ -1137,7 +1530,10 @@ static void coroutine_fn v9fs_getattr(void *opaque)
if (retval < 0) {
goto out;
}
- stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl);
+ retval = stat_to_v9stat_dotl(pdu, &stbuf, &v9stat_dotl);
+ if (retval < 0) {
+ goto out;
+ }
/* fill st_gen if requested and supported by underlying fs */
if (request_mask & P9_STATS_GEN) {
@@ -1382,7 +1778,10 @@ static void coroutine_fn v9fs_walk(void *opaque)
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
v9fs_path_copy(&dpath, &path);
}
memcpy(&qids[name_idx], &qid, sizeof(qid));
@@ -1484,7 +1883,10 @@ static void coroutine_fn v9fs_open(void *opaque)
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
if (S_ISDIR(stbuf.st_mode)) {
err = v9fs_co_opendir(pdu, fidp);
if (err < 0) {
@@ -1594,7 +1996,10 @@ static void coroutine_fn v9fs_lcreate(void *opaque)
fidp->flags |= FID_NON_RECLAIMABLE;
}
iounit = get_iounit(pdu, &fidp->path);
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
if (err < 0) {
goto out;
@@ -1938,16 +2343,39 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
v9fs_string_free(&name);
return count;
}
- /*
- * Fill up just the path field of qid because the client uses
- * only that. To fill the entire qid structure we will have
- * to stat each dirent found, which is expensive
- */
- size = MIN(sizeof(dent->d_ino), sizeof(qid.path));
- memcpy(&qid.path, &dent->d_ino, size);
- /* Fill the other fields with dummy values */
- qid.type = 0;
- qid.version = 0;
+
+ if (pdu->s->ctx.export_flags & V9FS_REMAP_INODES) {
+ /*
+ * dirent_to_qid() implies expensive stat call for each entry,
+ * we must do that here though since inode remapping requires
+ * the device id, which in turn might be different for
+ * different entries; we cannot make any assumption to avoid
+ * that here.
+ */
+ err = dirent_to_qid(pdu, fidp, dent, &qid);
+ if (err < 0) {
+ v9fs_readdir_unlock(&fidp->fs.dir);
+ v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
+ v9fs_string_free(&name);
+ return err;
+ }
+ } else {
+ /*
+ * Fill up just the path field of qid because the client uses
+ * only that. To fill the entire qid structure we will have
+ * to stat each dirent found, which is expensive. For the
+ * latter reason we don't call dirent_to_qid() here. Only drawback
+ * is that no multi-device export detection of stat_to_qid()
+ * would be done and provided as error to the user here. But
+ * user would get that error anyway when accessing those
+ * files/dirs through other ways.
+ */
+ size = MIN(sizeof(dent->d_ino), sizeof(qid.path));
+ memcpy(&qid.path, &dent->d_ino, size);
+ /* Fill the other fields with dummy values */
+ qid.type = 0;
+ qid.version = 0;
+ }
/* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
len = pdu_marshal(pdu, 11 + count, "Qqbs",
@@ -2328,7 +2756,10 @@ static void coroutine_fn v9fs_create(void *opaque)
}
}
iounit = get_iounit(pdu, &fidp->path);
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
if (err < 0) {
goto out;
@@ -2385,7 +2816,10 @@ static void coroutine_fn v9fs_symlink(void *opaque)
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Q", &qid);
if (err < 0) {
goto out;
@@ -3065,7 +3499,10 @@ static void coroutine_fn v9fs_mknod(void *opaque)
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Q", &qid);
if (err < 0) {
goto out;
@@ -3223,7 +3660,10 @@ static void coroutine_fn v9fs_mkdir(void *opaque)
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Q", &qid);
if (err < 0) {
goto out;
@@ -3634,31 +4074,43 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
goto out;
}
+ s->dev_id = stat.st_dev;
+
+ /* init inode remapping : */
+ /* hash table for variable length inode suffixes */
+ qpd_table_init(&s->qpd_table);
+ /* hash table for slow/full inode remapping (most users won't need it) */
+ qpf_table_init(&s->qpf_table);
+ /* hash table for quick inode remapping */
+ qpp_table_init(&s->qpp_table);
+ s->qp_ndevices = 0;
+ s->qp_affix_next = 1; /* reserve 0 to detect overflow */
+ s->qp_fullpath_next = 1;
+
s->ctx.fst = &fse->fst;
fsdev_throttle_init(s->ctx.fst);
- v9fs_path_free(&path);
-
rc = 0;
out:
if (rc) {
- if (s->ops && s->ops->cleanup && s->ctx.private) {
- s->ops->cleanup(&s->ctx);
- }
- g_free(s->tag);
- g_free(s->ctx.fs_root);
- v9fs_path_free(&path);
+ v9fs_device_unrealize_common(s, NULL);
}
+ v9fs_path_free(&path);
return rc;
}
void v9fs_device_unrealize_common(V9fsState *s, Error **errp)
{
- if (s->ops->cleanup) {
+ if (s->ops && s->ops->cleanup) {
s->ops->cleanup(&s->ctx);
}
- fsdev_throttle_cleanup(s->ctx.fst);
+ if (s->ctx.fst) {
+ fsdev_throttle_cleanup(s->ctx.fst);
+ }
g_free(s->tag);
+ qp_table_destroy(&s->qpd_table);
+ qp_table_destroy(&s->qpp_table);
+ qp_table_destroy(&s->qpf_table);
g_free(s->ctx.fs_root);
}
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 8883761b2c..3904f82901 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -8,6 +8,7 @@
#include "fsdev/9p-iov-marshal.h"
#include "qemu/thread.h"
#include "qemu/coroutine.h"
+#include "qemu/qht.h"
enum {
P9_TLERROR = 6,
@@ -235,6 +236,58 @@ struct V9fsFidState
V9fsFidState *rclm_lst;
};
+typedef enum AffixType_t {
+ AffixType_Prefix,
+ AffixType_Suffix, /* A.k.a. postfix. */
+} AffixType_t;
+
+/**
+ * @brief Unique affix of variable length.
+ *
+ * An affix is (currently) either a suffix or a prefix, which is either
+ * going to be prepended (prefix) or appended (suffix) with some other
+ * number for the goal to generate unique numbers. Accordingly the
+ * suffixes (or prefixes) we generate @b must all have the mathematical
+ * property of being suffix-free (or prefix-free in case of prefixes)
+ * so that no matter what number we concatenate the affix with, that we
+ * always reliably get unique numbers as result after concatenation.
+ */
+typedef struct VariLenAffix {
+ AffixType_t type; /* Whether this affix is a suffix or a prefix. */
+ uint64_t value; /* Actual numerical value of this affix. */
+ /*
+ * Lenght of the affix, that is how many (of the lowest) bits of @c value
+ * must be used for appending/prepending this affix to its final resulting,
+ * unique number.
+ */
+ int bits;
+} VariLenAffix;
+
+/* See qid_inode_prefix_hash_bits(). */
+typedef struct {
+ dev_t dev; /* FS device on host. */
+ /*
+ * How many (high) bits of the original inode number shall be used for
+ * hashing.
+ */
+ int prefix_bits;
+} QpdEntry;
+
+/* QID path prefix entry, see stat_to_qid */
+typedef struct {
+ dev_t dev;
+ uint16_t ino_prefix;
+ uint32_t qp_affix_index;
+ VariLenAffix qp_affix;
+} QppEntry;
+
+/* QID path full entry, as above */
+typedef struct {
+ dev_t dev;
+ ino_t ino;
+ uint64_t path;
+} QpfEntry;
+
struct V9fsState
{
QLIST_HEAD(, V9fsPDU) free_list;
@@ -256,6 +309,13 @@ struct V9fsState
Error *migration_blocker;
V9fsConf fsconf;
V9fsQID root_qid;
+ dev_t dev_id;
+ struct qht qpd_table;
+ struct qht qpp_table;
+ struct qht qpf_table;
+ uint64_t qp_ndevices; /* Amount of entries in qpd_table. */
+ uint16_t qp_affix_next;
+ uint64_t qp_fullpath_next;
};
/* 9p2000.L open flags */
diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events
index c0a0a4ab5d..10188daf7f 100644
--- a/hw/9pfs/trace-events
+++ b/hw/9pfs/trace-events
@@ -6,7 +6,7 @@ v9fs_rerror(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d"
v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s"
v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s"
v9fs_attach(uint16_t tag, uint8_t id, int32_t fid, int32_t afid, char* uname, char* aname) "tag %u id %u fid %d afid %d uname %s aname %s"
-v9fs_attach_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d type %d version %d path %"PRId64
+v9fs_attach_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u type %u version %u path %"PRIu64
v9fs_stat(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d"
v9fs_stat_return(uint16_t tag, uint8_t id, int32_t mode, int32_t atime, int32_t mtime, int64_t length) "tag %d id %d stat={mode %d atime %d mtime %d length %"PRId64"}"
v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64
@@ -14,9 +14,9 @@ v9fs_getattr_return(uint16_t tag, uint8_t id, uint64_t result_mask, uint32_t mod
v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames) "tag %d id %d fid %d newfid %d nwnames %d"
v9fs_walk_return(uint16_t tag, uint8_t id, uint16_t nwnames, void* qids) "tag %d id %d nwnames %d qids %p"
v9fs_open(uint16_t tag, uint8_t id, int32_t fid, int32_t mode) "tag %d id %d fid %d mode %d"
-v9fs_open_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d"
+v9fs_open_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d"
v9fs_lcreate(uint16_t tag, uint8_t id, int32_t dfid, int32_t flags, int32_t mode, uint32_t gid) "tag %d id %d dfid %d flags %d mode %d gid %u"
-v9fs_lcreate_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int32_t iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d"
+v9fs_lcreate_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int32_t iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d"
v9fs_fsync(uint16_t tag, uint8_t id, int32_t fid, int datasync) "tag %d id %d fid %d datasync %d"
v9fs_clunk(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d"
v9fs_read(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t max_count) "tag %d id %d fid %d off %"PRIu64" max_count %u"
@@ -26,21 +26,21 @@ v9fs_readdir_return(uint16_t tag, uint8_t id, uint32_t count, ssize_t retval) "t
v9fs_write(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t count, int cnt) "tag %d id %d fid %d off %"PRIu64" count %u cnt %d"
v9fs_write_return(uint16_t tag, uint8_t id, int32_t total, ssize_t err) "tag %d id %d total %d err %zd"
v9fs_create(uint16_t tag, uint8_t id, int32_t fid, char* name, int32_t perm, int8_t mode) "tag %d id %d fid %d name %s perm %d mode %d"
-v9fs_create_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d"
+v9fs_create_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d"
v9fs_symlink(uint16_t tag, uint8_t id, int32_t fid, char* name, char* symname, uint32_t gid) "tag %d id %d fid %d name %s symname %s gid %u"
-v9fs_symlink_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}"
+v9fs_symlink_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u qid={type %u version %u path %"PRIu64"}"
v9fs_flush(uint16_t tag, uint8_t id, int16_t flush_tag) "tag %d id %d flush_tag %d"
v9fs_link(uint16_t tag, uint8_t id, int32_t dfid, int32_t oldfid, char* name) "tag %d id %d dfid %d oldfid %d name %s"
v9fs_remove(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d"
v9fs_wstat(uint16_t tag, uint8_t id, int32_t fid, int32_t mode, int32_t atime, int32_t mtime) "tag %u id %u fid %d stat={mode %d atime %d mtime %d}"
v9fs_mknod(uint16_t tag, uint8_t id, int32_t fid, int mode, int major, int minor) "tag %d id %d fid %d mode %d major %d minor %d"
-v9fs_mknod_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}"
+v9fs_mknod_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u qid={type %u version %u path %"PRIu64"}"
v9fs_lock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length) "tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64
v9fs_lock_return(uint16_t tag, uint8_t id, int8_t status) "tag %d id %d status %d"
v9fs_getlock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length)"tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64
v9fs_getlock_return(uint16_t tag, uint8_t id, uint8_t type, uint64_t start, uint64_t length, uint32_t proc_id) "tag %d id %d type %d start %"PRIu64" length %"PRIu64" proc_id %u"
v9fs_mkdir(uint16_t tag, uint8_t id, int32_t fid, char* name, int mode, uint32_t gid) "tag %u id %u fid %d name %s mode %d gid %u"
-v9fs_mkdir_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int err) "tag %u id %u qid={type %d version %d path %"PRId64"} err %d"
+v9fs_mkdir_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int err) "tag %u id %u qid={type %u version %u path %"PRIu64"} err %d"
v9fs_xattrwalk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, char* name) "tag %d id %d fid %d newfid %d name %s"
v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size %"PRId64
v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, uint64_t size, int flags) "tag %d id %d fid %d name %s size %"PRIu64" flags %d"
diff --git a/hw/ide/core.c b/hw/ide/core.c
index e6e54c6c9a..754ff4dc34 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -442,6 +442,14 @@ static void ide_issue_trim_cb(void *opaque, int ret)
TrimAIOCB *iocb = opaque;
IDEState *s = iocb->s;
+ if (iocb->i >= 0) {
+ if (ret >= 0) {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ }
+ }
+
if (ret >= 0) {
while (iocb->j < iocb->qiov->niov) {
int j = iocb->j;
@@ -459,10 +467,14 @@ static void ide_issue_trim_cb(void *opaque, int ret)
}
if (!ide_sect_range_ok(s, sector, count)) {
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP);
iocb->ret = -EINVAL;
goto done;
}
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP);
+
/* Got an entry! Submit and exit. */
iocb->aiocb = blk_aio_pdiscard(s->blk,
sector << BDRV_SECTOR_BITS,
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 4b3bd4a804..92c7e45df5 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -157,7 +157,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
int i;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -168,7 +168,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
reqh = (ClpReqHdr *)buffer;
req_len = lduw_p(&reqh->len);
if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -180,11 +180,11 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
resh = (ClpRspHdr *)(buffer + req_len);
res_len = lduw_p(&resh->len);
if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
if ((req_len + res_len) > 8192) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -390,12 +390,12 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
uint8_t pcias;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -429,25 +429,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
switch (pcias) {
case ZPCI_IO_BAR_MIN...ZPCI_IO_BAR_MAX:
if (!len || (len > (8 - (offset & 0x7)))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
result = zpci_read_bar(pbdev, pcias, offset, &data, len);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
case ZPCI_CONFIG_BAR:
if (!len || (len > (4 - (offset & 0x3))) || len == 3) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
data = pci_host_config_read_common(
pbdev->pdev, offset, pci_config_size(pbdev->pdev), len);
if (zpci_endian_swap(&data, len)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
@@ -489,12 +489,12 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
uint8_t pcias;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -536,13 +536,13 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
* A length of 0 is invalid and length should not cross a double word
*/
if (!len || (len > (8 - (offset & 0x7)))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
result = zpci_write_bar(pbdev, pcias, offset, data, len);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
@@ -550,7 +550,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
/* ZPCI uses the pseudo BAR number 15 as configuration space */
/* possible access lengths are 1,2,4 and must not cross a word */
if (!len || (len > (4 - (offset & 0x3))) || len == 3) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
/* len = 1,2,4 so we do not need to test */
@@ -622,12 +622,12 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
hwaddr start, end;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -709,7 +709,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
uint8_t buffer[128];
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -772,7 +772,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
if (!memory_region_access_valid(mr, offset, len, true,
MEMTXATTRS_UNSPECIFIED)) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -786,7 +786,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
ldq_p(buffer + i * 8),
MO_64, MEMTXATTRS_UNSPECIFIED);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
}
@@ -797,7 +797,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
return 0;
specification_error:
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -871,14 +871,14 @@ static int reg_ioat(CPUS390XState *env, S390PCIIOMMU *iommu, ZpciFib fib,
pba &= ~0xfff;
pal |= 0xfff;
if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
/* currently we only support designation type 1 with translation */
if (!(dt == ZPCI_IOTA_RTTO && t)) {
error_report("unsupported ioat dt %d t %d", dt, t);
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
@@ -1003,7 +1003,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uint64_t cc = ZPCI_PCI_LS_OK;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -1012,7 +1012,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
fh = env->regs[r1] >> 32;
if (fiba & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -1040,7 +1040,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
}
if (fib.fmt != 0) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -1151,7 +1151,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
break;
}
default:
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
cc = ZPCI_PCI_LS_ERR;
}
@@ -1171,7 +1171,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uint64_t cc = ZPCI_PCI_LS_OK;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -1185,7 +1185,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
}
if (fiba & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 18ad279a00..d3edeef0ad 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -650,7 +650,9 @@ DEFINE_CCW_MACHINE(4_2, "4.2", true);
static void ccw_machine_4_1_instance_options(MachineState *machine)
{
+ static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V4_1 };
ccw_machine_4_2_instance_options(machine);
+ s390_set_qemu_cpu_model(0x2964, 13, 2, qemu_cpu_feat);
}
static void ccw_machine_4_1_class_options(MachineClass *mc)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 915641a0f1..68b1675fd9 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1608,25 +1608,28 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
{
SCSIDiskReq *r = data->r;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
- uint64_t sector_num;
- uint32_t nb_sectors;
assert(r->req.aiocb == NULL);
- if (scsi_disk_req_check_error(r, ret, false)) {
- goto done;
- }
if (data->count > 0) {
- sector_num = ldq_be_p(&data->inbuf[0]);
- nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
- if (!check_lba_range(s, sector_num, nb_sectors)) {
+ r->sector = ldq_be_p(&data->inbuf[0])
+ * (s->qdev.blocksize / BDRV_SECTOR_SIZE);
+ r->sector_count = (ldl_be_p(&data->inbuf[8]) & 0xffffffffULL)
+ * (s->qdev.blocksize / BDRV_SECTOR_SIZE);
+ if (!check_lba_range(s, r->sector, r->sector_count)) {
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk),
+ BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
goto done;
}
+ block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
+ r->sector_count * BDRV_SECTOR_SIZE,
+ BLOCK_ACCT_UNMAP);
+
r->req.aiocb = blk_aio_pdiscard(s->qdev.conf.blk,
- sector_num * s->qdev.blocksize,
- nb_sectors * s->qdev.blocksize,
+ r->sector * BDRV_SECTOR_SIZE,
+ r->sector_count * BDRV_SECTOR_SIZE,
scsi_unmap_complete, data);
data->count--;
data->inbuf += 16;
@@ -1650,7 +1653,13 @@ static void scsi_unmap_complete(void *opaque, int ret)
r->req.aiocb = NULL;
aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
- scsi_unmap_complete_noio(data, ret);
+ if (scsi_disk_req_check_error(r, ret, true)) {
+ scsi_req_unref(&r->req);
+ g_free(data);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ scsi_unmap_complete_noio(data, ret);
+ }
aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
}
@@ -1680,6 +1689,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
}
if (blk_is_read_only(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;
}
@@ -1695,10 +1705,12 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
return;
invalid_param_len:
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
return;
invalid_field:
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index c5e6fe61cb..12fac39804 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -651,6 +651,7 @@ retry:
}
g_free(vdev->msi_vectors);
+ vdev->msi_vectors = NULL;
if (ret > 0 && ret != vdev->nr_vectors) {
vdev->nr_vectors = ret;
diff --git a/include/block/accounting.h b/include/block/accounting.h
index d1f67b10dd..878b4c3581 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -33,9 +33,11 @@ typedef struct BlockAcctTimedStats BlockAcctTimedStats;
typedef struct BlockAcctStats BlockAcctStats;
enum BlockAcctType {
+ BLOCK_ACCT_NONE = 0,
BLOCK_ACCT_READ,
BLOCK_ACCT_WRITE,
BLOCK_ACCT_FLUSH,
+ BLOCK_ACCT_UNMAP,
BLOCK_MAX_IOTYPE,
};
diff --git a/include/block/aio_task.h b/include/block/aio_task.h
new file mode 100644
index 0000000000..50bc1e1817
--- /dev/null
+++ b/include/block/aio_task.h
@@ -0,0 +1,54 @@
+/*
+ * Aio tasks loops
+ *
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLOCK_AIO_TASK_H
+#define BLOCK_AIO_TASK_H
+
+#include "qemu/coroutine.h"
+
+typedef struct AioTaskPool AioTaskPool;
+typedef struct AioTask AioTask;
+typedef int coroutine_fn (*AioTaskFunc)(AioTask *task);
+struct AioTask {
+ AioTaskPool *pool;
+ AioTaskFunc func;
+ int ret;
+};
+
+AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks);
+void aio_task_pool_free(AioTaskPool *);
+
+/* error code of failed task or 0 if all is OK */
+int aio_task_pool_status(AioTaskPool *pool);
+
+bool aio_task_pool_empty(AioTaskPool *pool);
+
+/* User provides filled @task, however task->pool will be set automatically */
+void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task);
+
+void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool);
+void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool);
+void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool);
+
+#endif /* BLOCK_AIO_TASK_H */
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
new file mode 100644
index 0000000000..e2e135ff1b
--- /dev/null
+++ b/include/block/block-copy.h
@@ -0,0 +1,93 @@
+/*
+ * block_copy API
+ *
+ * Copyright (C) 2013 Proxmox Server Solutions
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Authors:
+ * Dietmar Maurer (dietmar@proxmox.com)
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef BLOCK_COPY_H
+#define BLOCK_COPY_H
+
+#include "block/block.h"
+
+typedef struct BlockCopyInFlightReq {
+ int64_t start_byte;
+ int64_t end_byte;
+ QLIST_ENTRY(BlockCopyInFlightReq) list;
+ CoQueue wait_queue; /* coroutines blocked on this request */
+} BlockCopyInFlightReq;
+
+typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
+typedef void (*ProgressResetCallbackFunc)(void *opaque);
+typedef struct BlockCopyState {
+ /*
+ * BdrvChild objects are not owned or managed by block-copy. They are
+ * provided by block-copy user and user is responsible for appropriate
+ * permissions on these children.
+ */
+ BdrvChild *source;
+ BdrvChild *target;
+ BdrvDirtyBitmap *copy_bitmap;
+ int64_t cluster_size;
+ bool use_copy_range;
+ int64_t copy_range_size;
+ uint64_t len;
+ QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
+
+ BdrvRequestFlags write_flags;
+
+ /*
+ * skip_unallocated:
+ *
+ * Used by sync=top jobs, which first scan the source node for unallocated
+ * areas and clear them in the copy_bitmap. During this process, the bitmap
+ * is thus not fully initialized: It may still have bits set for areas that
+ * are unallocated and should actually not be copied.
+ *
+ * This is indicated by skip_unallocated.
+ *
+ * In this case, block_copy() will query the source’s allocation status,
+ * skip unallocated regions, clear them in the copy_bitmap, and invoke
+ * block_copy_reset_unallocated() every time it does.
+ */
+ bool skip_unallocated;
+
+ /* progress_bytes_callback: called when some copying progress is done. */
+ ProgressBytesCallbackFunc progress_bytes_callback;
+
+ /*
+ * progress_reset_callback: called when some bytes reset from copy_bitmap
+ * (see @skip_unallocated above). The callee is assumed to recalculate how
+ * many bytes remain based on the dirty bit count of copy_bitmap.
+ */
+ ProgressResetCallbackFunc progress_reset_callback;
+ void *progress_opaque;
+} BlockCopyState;
+
+BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
+ int64_t cluster_size,
+ BdrvRequestFlags write_flags,
+ Error **errp);
+
+void block_copy_set_callbacks(
+ BlockCopyState *s,
+ ProgressBytesCallbackFunc progress_bytes_callback,
+ ProgressResetCallbackFunc progress_reset_callback,
+ void *progress_opaque);
+
+void block_copy_state_free(BlockCopyState *s);
+
+int64_t block_copy_reset_unallocated(BlockCopyState *s,
+ int64_t offset, int64_t *count);
+
+int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes,
+ bool *error_is_read);
+
+#endif /* BLOCK_COPY_H */
diff --git a/include/block/block.h b/include/block/block.h
index 37c9de7446..792bb826db 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -501,6 +501,7 @@ int bdrv_get_flags(BlockDriverState *bs);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
Error **errp);
+BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t offset, int64_t bytes,
int64_t *cluster_offset,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 0422acdf1c..05056b308a 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -366,6 +366,7 @@ struct BlockDriver {
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs,
Error **errp);
+ BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
QEMUIOVector *qiov,
@@ -1196,6 +1197,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BdrvDirtyBitmap *sync_bitmap,
BitmapSyncMode bitmap_mode,
bool compress,
+ const char *filter_node_name,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 49db07ba0b..04795c49bf 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -72,6 +72,23 @@ void QEMU_NORETURN cpu_loop_exit(CPUState *cpu);
void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc);
void QEMU_NORETURN cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc);
+/**
+ * cpu_loop_exit_requested:
+ * @cpu: The CPU state to be tested
+ *
+ * Indicate if somebody asked for a return of the CPU to the main loop
+ * (e.g., via cpu_exit() or cpu_interrupt()).
+ *
+ * This is helpful for architectures that support interruptible
+ * instructions. After writing back all state to registers/memory, this
+ * call can be used to check if it makes sense to return to the main loop
+ * or to continue executing the interruptible instruction.
+ */
+static inline bool cpu_loop_exit_requested(CPUState *cpu)
+{
+ return (int32_t)atomic_read(&cpu_neg(cpu)->icount_decr.u32) < 0;
+}
+
#if !defined(CONFIG_USER_ONLY)
void cpu_reloading_memory_map(void);
/**
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
index e96e621de5..ad158bb247 100644
--- a/include/exec/ram_addr.h
+++ b/include/exec/ram_addr.h
@@ -193,30 +193,29 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
-
- blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+ WITH_RCU_READ_LOCK_GUARD() {
+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+
+ idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ base = page - offset;
+ while (page < end) {
+ unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
+ unsigned long num = next - base;
+ unsigned long found = find_next_bit(blocks->blocks[idx],
+ num, offset);
+ if (found < num) {
+ dirty = true;
+ break;
+ }
- idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- base = page - offset;
- while (page < end) {
- unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
- unsigned long num = next - base;
- unsigned long found = find_next_bit(blocks->blocks[idx], num, offset);
- if (found < num) {
- dirty = true;
- break;
+ page = next;
+ idx++;
+ offset = 0;
+ base += DIRTY_MEMORY_BLOCK_SIZE;
}
-
- page = next;
- idx++;
- offset = 0;
- base += DIRTY_MEMORY_BLOCK_SIZE;
}
- rcu_read_unlock();
-
return dirty;
}
@@ -234,7 +233,7 @@ static inline bool cpu_physical_memory_all_dirty(ram_addr_t start,
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
@@ -256,8 +255,6 @@ static inline bool cpu_physical_memory_all_dirty(ram_addr_t start,
base += DIRTY_MEMORY_BLOCK_SIZE;
}
- rcu_read_unlock();
-
return dirty;
}
@@ -309,13 +306,11 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr,
idx = page / DIRTY_MEMORY_BLOCK_SIZE;
offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
set_bit_atomic(offset, blocks->blocks[idx]);
-
- rcu_read_unlock();
}
static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
@@ -334,39 +329,37 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
+ blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]);
+ }
- for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
- blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]);
- }
+ idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ base = page - offset;
+ while (page < end) {
+ unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
- idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- base = page - offset;
- while (page < end) {
- unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
+ if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) {
+ bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx],
+ offset, next - page);
+ }
+ if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) {
+ bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx],
+ offset, next - page);
+ }
+ if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) {
+ bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx],
+ offset, next - page);
+ }
- if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) {
- bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx],
- offset, next - page);
+ page = next;
+ idx++;
+ offset = 0;
+ base += DIRTY_MEMORY_BLOCK_SIZE;
}
- if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) {
- bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx],
- offset, next - page);
- }
- if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) {
- bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx],
- offset, next - page);
- }
-
- page = next;
- idx++;
- offset = 0;
- base += DIRTY_MEMORY_BLOCK_SIZE;
}
- rcu_read_unlock();
-
xen_hvm_modified_memory(start, length);
}
@@ -396,36 +389,35 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
offset = BIT_WORD((start >> TARGET_PAGE_BITS) %
DIRTY_MEMORY_BLOCK_SIZE);
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
+ blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks;
+ }
- for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
- blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks;
- }
+ for (k = 0; k < nr; k++) {
+ if (bitmap[k]) {
+ unsigned long temp = leul_to_cpu(bitmap[k]);
- for (k = 0; k < nr; k++) {
- if (bitmap[k]) {
- unsigned long temp = leul_to_cpu(bitmap[k]);
+ atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp);
- atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp);
+ if (global_dirty_log) {
+ atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset],
+ temp);
+ }
- if (global_dirty_log) {
- atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset],
- temp);
+ if (tcg_enabled()) {
+ atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset],
+ temp);
+ }
}
- if (tcg_enabled()) {
- atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp);
+ if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) {
+ offset = 0;
+ idx++;
}
}
-
- if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) {
- offset = 0;
- idx++;
- }
}
- rcu_read_unlock();
-
xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS);
} else {
uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE;
diff --git a/include/migration/misc.h b/include/migration/misc.h
index b9d8e787af..d2762257aa 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -60,6 +60,7 @@ void migration_object_init(void);
void migration_shutdown(void);
void qemu_start_incoming_migration(const char *uri, Error **errp);
bool migration_is_idle(void);
+bool migration_is_active(MigrationState *);
void add_migration_state_change_notifier(Notifier *notify);
void remove_migration_state_change_notifier(Notifier *notify);
bool migration_in_setup(MigrationState *);
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 1fbfd099dd..b9ee563aa4 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -224,6 +224,7 @@ extern const VMStateInfo vmstate_info_unused_buffer;
extern const VMStateInfo vmstate_info_tmp;
extern const VMStateInfo vmstate_info_bitmap;
extern const VMStateInfo vmstate_info_qtailq;
+extern const VMStateInfo vmstate_info_gtree;
#define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
/*
@@ -754,6 +755,45 @@ extern const VMStateInfo vmstate_info_qtailq;
.start = offsetof(_type, _next), \
}
+/*
+ * For migrating a GTree whose key is a pointer to _key_type and the
+ * value, a pointer to _val_type
+ * The target tree must have been properly initialized
+ * _vmsd: Start address of the 2 element array containing the data vmsd
+ * and the key vmsd, in that order
+ * _key_type: type of the key
+ * _val_type: type of the value
+ */
+#define VMSTATE_GTREE_V(_field, _state, _version, _vmsd, \
+ _key_type, _val_type) \
+{ \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .vmsd = (_vmsd), \
+ .info = &vmstate_info_gtree, \
+ .start = sizeof(_key_type), \
+ .size = sizeof(_val_type), \
+ .offset = offsetof(_state, _field), \
+}
+
+/*
+ * For migrating a GTree with direct key and the value a pointer
+ * to _val_type
+ * The target tree must have been properly initialized
+ * _vmsd: data vmsd
+ * _val_type: type of the value
+ */
+#define VMSTATE_GTREE_DIRECT_KEY_V(_field, _state, _version, _vmsd, _val_type) \
+{ \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .vmsd = (_vmsd), \
+ .info = &vmstate_info_gtree, \
+ .start = 0, \
+ .size = sizeof(_val_type), \
+ .offset = offsetof(_state, _field), \
+}
+
/* _f : field name
_f_n : num of elements field_name
_n : num of elements
diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h
index 22876d1428..9c82683e37 100644
--- a/include/qemu/rcu.h
+++ b/include/qemu/rcu.h
@@ -154,6 +154,31 @@ extern void call_rcu1(struct rcu_head *head, RCUCBFunc *func);
}), \
(RCUCBFunc *)g_free);
+typedef void RCUReadAuto;
+static inline RCUReadAuto *rcu_read_auto_lock(void)
+{
+ rcu_read_lock();
+ /* Anything non-NULL causes the cleanup function to be called */
+ return (void *)(uintptr_t)0x1;
+}
+
+static inline void rcu_read_auto_unlock(RCUReadAuto *r)
+{
+ rcu_read_unlock();
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock)
+
+#define WITH_RCU_READ_LOCK_GUARD() \
+ WITH_RCU_READ_LOCK_GUARD_(_rcu_read_auto##__COUNTER__)
+
+#define WITH_RCU_READ_LOCK_GUARD_(var) \
+ for (g_autoptr(RCUReadAuto) var = rcu_read_auto_lock(); \
+ (var); rcu_read_auto_unlock(var), (var) = NULL)
+
+#define RCU_READ_LOCK_GUARD() \
+ g_autoptr(RCUReadAuto) _rcu_read_auto __attribute__((unused)) = rcu_read_auto_lock()
+
#ifdef __cplusplus
}
#endif
diff --git a/memory.c b/memory.c
index 978da3d3df..c952eabb5d 100644
--- a/memory.c
+++ b/memory.c
@@ -779,14 +779,13 @@ FlatView *address_space_get_flatview(AddressSpace *as)
{
FlatView *view;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
do {
view = address_space_to_flatview(as);
/* If somebody has replaced as->current_map concurrently,
* flatview_ref returns false.
*/
} while (!flatview_ref(view));
- rcu_read_unlock();
return view;
}
@@ -2166,12 +2165,11 @@ int memory_region_get_fd(MemoryRegion *mr)
{
int fd;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
while (mr->alias) {
mr = mr->alias;
}
fd = mr->ram_block->fd;
- rcu_read_unlock();
return fd;
}
@@ -2181,14 +2179,13 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
void *ptr;
uint64_t offset = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
while (mr->alias) {
offset += mr->alias_offset;
mr = mr->alias;
}
assert(mr->ram_block);
ptr = qemu_map_ram_ptr(mr->ram_block, offset);
- rcu_read_unlock();
return ptr;
}
@@ -2578,12 +2575,11 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
hwaddr addr, uint64_t size)
{
MemoryRegionSection ret;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
ret = memory_region_find_rcu(mr, addr, size);
if (ret.mr) {
memory_region_ref(ret.mr);
}
- rcu_read_unlock();
return ret;
}
@@ -2591,9 +2587,8 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr)
{
MemoryRegion *mr;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
mr = memory_region_find_rcu(container, addr, 1).mr;
- rcu_read_unlock();
return mr && mr != container;
}
diff --git a/migration/migration.c b/migration/migration.c
index 5f7e4d15e9..3febd0f8f3 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1533,8 +1533,7 @@ static void migrate_fd_cleanup(MigrationState *s)
qemu_fclose(tmp);
}
- assert((s->state != MIGRATION_STATUS_ACTIVE) &&
- (s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE));
+ assert(!migration_is_active(s));
if (s->state == MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
@@ -1703,6 +1702,12 @@ bool migration_is_idle(void)
return false;
}
+bool migration_is_active(MigrationState *s)
+{
+ return (s->state == MIGRATION_STATUS_ACTIVE ||
+ s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+}
+
void migrate_init(MigrationState *s)
{
/*
@@ -2481,7 +2486,7 @@ retry:
out:
res = qemu_file_get_error(rp);
if (res) {
- if (res == -EIO) {
+ if (res == -EIO && migration_in_postcopy()) {
/*
* Maybe there is something we can do: it looks like a
* network down issue, and we pause for a recovery.
@@ -3144,8 +3149,7 @@ static MigIterateState migration_iteration_run(MigrationState *s)
return MIG_ITERATE_SKIP;
}
/* Just another iteration step */
- qemu_savevm_state_iterate(s->to_dst_file,
- s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ qemu_savevm_state_iterate(s->to_dst_file, in_postcopy);
} else {
trace_migration_thread_low_pending(pending_size);
migration_completion(s);
@@ -3266,8 +3270,7 @@ static void *migration_thread(void *opaque)
trace_migration_thread_setup_complete();
- while (s->state == MIGRATION_STATUS_ACTIVE ||
- s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
+ while (migration_is_active(s)) {
int64_t current_time;
if (urgent || !qemu_file_rate_limit(s->to_dst_file)) {
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 1f63e65ed7..abccafc8c8 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -577,8 +577,6 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis)
}
}
- postcopy_state_set(POSTCOPY_INCOMING_END);
-
if (mis->postcopy_tmp_page) {
munmap(mis->postcopy_tmp_page, mis->largest_page_size);
mis->postcopy_tmp_page = NULL;
@@ -768,9 +766,11 @@ static void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid,
atomic_xchg(&dc->page_fault_vcpu_time[cpu], low_time_offset);
atomic_xchg(&dc->vcpu_addr[cpu], addr);
- /* check it here, not at the begining of the function,
- * due to, check could accur early than bitmap_set in
- * qemu_ufd_copy_ioctl */
+ /*
+ * check it here, not at the beginning of the function,
+ * due to, check could occur early than bitmap_set in
+ * qemu_ufd_copy_ioctl
+ */
already_received = ramblock_recv_bitmap_test(rb, (void *)addr);
if (already_received) {
atomic_xchg(&dc->vcpu_addr[cpu], 0);
@@ -1094,7 +1094,7 @@ retry:
return NULL;
}
-int postcopy_ram_enable_notify(MigrationIncomingState *mis)
+int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
{
/* Open the fd for the kernel to give us userfaults */
mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
@@ -1134,6 +1134,32 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
return -1;
}
+ mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE |
+ MAP_ANONYMOUS, -1, 0);
+ if (mis->postcopy_tmp_page == MAP_FAILED) {
+ mis->postcopy_tmp_page = NULL;
+ error_report("%s: Failed to map postcopy_tmp_page %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages
+ */
+ mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (mis->postcopy_tmp_zero_page == MAP_FAILED) {
+ int e = errno;
+ mis->postcopy_tmp_zero_page = NULL;
+ error_report("%s: Failed to map large zero page %s",
+ __func__, strerror(e));
+ return -e;
+ }
+ memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
+
/*
* Ballooning can mark pages as absent while we're postcopying
* that would cause false userfaults.
@@ -1240,50 +1266,10 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host,
qemu_ram_block_host_offset(rb,
host));
} else {
- /* The kernel can't use UFFDIO_ZEROPAGE for hugepages */
- if (!mis->postcopy_tmp_zero_page) {
- mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0);
- if (mis->postcopy_tmp_zero_page == MAP_FAILED) {
- int e = errno;
- mis->postcopy_tmp_zero_page = NULL;
- error_report("%s: %s mapping large zero page",
- __func__, strerror(e));
- return -e;
- }
- memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
- }
- return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page,
- rb);
+ return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page, rb);
}
}
-/*
- * Returns a target page of memory that can be mapped at a later point in time
- * using postcopy_place_page
- * The same address is used repeatedly, postcopy_place_page just takes the
- * backing page away.
- * Returns: Pointer to allocated page
- *
- */
-void *postcopy_get_tmp_page(MigrationIncomingState *mis)
-{
- if (!mis->postcopy_tmp_page) {
- mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE, MAP_PRIVATE |
- MAP_ANONYMOUS, -1, 0);
- if (mis->postcopy_tmp_page == MAP_FAILED) {
- mis->postcopy_tmp_page = NULL;
- error_report("%s: %s", __func__, strerror(errno));
- return NULL;
- }
- }
-
- return mis->postcopy_tmp_page;
-}
-
#else
/* No target OS support, stubs just fail */
void fill_destination_postcopy_migration_info(MigrationInfo *info)
@@ -1321,7 +1307,7 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb,
return -1;
}
-int postcopy_ram_enable_notify(MigrationIncomingState *mis)
+int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
{
assert(0);
return -1;
@@ -1341,12 +1327,6 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host,
return -1;
}
-void *postcopy_get_tmp_page(MigrationIncomingState *mis)
-{
- assert(0);
- return NULL;
-}
-
int postcopy_wake_shared(struct PostCopyFD *pcfd,
uint64_t client_addr,
RAMBlock *rb)
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
index 9c8bd2bae0..9941feb63a 100644
--- a/migration/postcopy-ram.h
+++ b/migration/postcopy-ram.h
@@ -20,7 +20,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis);
* Make all of RAM sensitive to accesses to areas that haven't yet been written
* and wire up anything necessary to deal with it.
*/
-int postcopy_ram_enable_notify(MigrationIncomingState *mis);
+int postcopy_ram_incoming_setup(MigrationIncomingState *mis);
/*
* Initialise postcopy-ram, setting the RAM to a state where we can go into
@@ -100,13 +100,6 @@ typedef enum {
POSTCOPY_INCOMING_END
} PostcopyState;
-/*
- * Allocate a page of memory that can be mapped at a later point in time
- * using postcopy_place_page
- * Returns: Pointer to allocated page
- */
-void *postcopy_get_tmp_page(MigrationIncomingState *mis);
-
PostcopyState postcopy_state_get(void);
/* Set the state and return the old state */
PostcopyState postcopy_state_set(PostcopyState new_state);
diff --git a/migration/ram.c b/migration/ram.c
index 22423f08cd..5078f94490 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -181,14 +181,14 @@ int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque)
RAMBlock *block;
int ret = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
+
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
ret = func(block, opaque);
if (ret) {
break;
}
}
- rcu_read_unlock();
return ret;
}
@@ -791,13 +791,10 @@ static void multifd_pages_clear(MultiFDPages_t *pages)
static void multifd_send_fill_packet(MultiFDSendParams *p)
{
MultiFDPacket_t *packet = p->packet;
- uint32_t page_max = MULTIFD_PACKET_SIZE / qemu_target_page_size();
int i;
- packet->magic = cpu_to_be32(MULTIFD_MAGIC);
- packet->version = cpu_to_be32(MULTIFD_VERSION);
packet->flags = cpu_to_be32(p->flags);
- packet->pages_alloc = cpu_to_be32(page_max);
+ packet->pages_alloc = cpu_to_be32(p->pages->allocated);
packet->pages_used = cpu_to_be32(p->pages->used);
packet->next_packet_size = cpu_to_be32(p->next_packet_size);
packet->packet_num = cpu_to_be64(p->packet_num);
@@ -838,7 +835,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
packet->pages_alloc = be32_to_cpu(packet->pages_alloc);
/*
- * If we recevied a packet that is 100 times bigger than expected
+ * If we received a packet that is 100 times bigger than expected
* just stop migration. It is a magic number.
*/
if (packet->pages_alloc > pages_max * 100) {
@@ -1132,7 +1129,6 @@ static void *multifd_send_thread(void *opaque)
p->flags = 0;
p->num_packets++;
p->num_pages += used;
- p->pages->used = 0;
qemu_mutex_unlock(&p->mutex);
trace_multifd_send(p->id, packet_num, used, flags,
@@ -1241,6 +1237,8 @@ int multifd_save_setup(void)
p->packet_len = sizeof(MultiFDPacket_t)
+ sizeof(ram_addr_t) * page_count;
p->packet = g_malloc0(p->packet_len);
+ p->packet->magic = cpu_to_be32(MULTIFD_MAGIC);
+ p->packet->version = cpu_to_be32(MULTIFD_VERSION);
p->name = g_strdup_printf("multifdsend_%d", i);
socket_send_channel_create(multifd_new_send_channel_async, p);
}
@@ -1848,12 +1846,12 @@ static void migration_bitmap_sync(RAMState *rs)
memory_global_dirty_log_sync();
qemu_mutex_lock(&rs->bitmap_mutex);
- rcu_read_lock();
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- ramblock_sync_dirty_bitmap(rs, block);
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ ramblock_sync_dirty_bitmap(rs, block);
+ }
+ ram_counters.remaining = ram_bytes_remaining();
}
- ram_counters.remaining = ram_bytes_remaining();
- rcu_read_unlock();
qemu_mutex_unlock(&rs->bitmap_mutex);
memory_global_after_dirty_log_sync();
@@ -2397,13 +2395,12 @@ static void migration_page_queue_free(RAMState *rs)
/* This queue generally should be empty - but in the case of a failed
* migration might have some droppings in.
*/
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
QSIMPLEQ_FOREACH_SAFE(mspr, &rs->src_page_requests, next_req, next_mspr) {
memory_region_unref(mspr->rb->mr);
QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req);
g_free(mspr);
}
- rcu_read_unlock();
}
/**
@@ -2424,7 +2421,8 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
RAMState *rs = ram_state;
ram_counters.postcopy_requests++;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
+
if (!rbname) {
/* Reuse last RAMBlock */
ramblock = rs->last_req_rb;
@@ -2466,12 +2464,10 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
QSIMPLEQ_INSERT_TAIL(&rs->src_page_requests, new_entry, next_req);
migration_make_urgent_request();
qemu_mutex_unlock(&rs->src_page_req_mutex);
- rcu_read_unlock();
return 0;
err:
- rcu_read_unlock();
return -1;
}
@@ -2700,7 +2696,8 @@ static uint64_t ram_bytes_total_common(bool count_ignored)
RAMBlock *block;
uint64_t total = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
+
if (count_ignored) {
RAMBLOCK_FOREACH_MIGRATABLE(block) {
total += block->used_length;
@@ -2710,7 +2707,6 @@ static uint64_t ram_bytes_total_common(bool count_ignored)
total += block->used_length;
}
}
- rcu_read_unlock();
return total;
}
@@ -3034,7 +3030,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms)
RAMBlock *block;
int ret;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
/* This should be our last sync, the src is now paused */
migration_bitmap_sync(rs);
@@ -3048,7 +3044,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms)
/* Deal with TPS != HPS and huge pages */
ret = postcopy_chunk_hostpages(ms, block);
if (ret) {
- rcu_read_unlock();
return ret;
}
@@ -3060,7 +3055,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms)
trace_ram_postcopy_send_discard_bitmap();
ret = postcopy_each_ram_send_discard(ms);
- rcu_read_unlock();
return ret;
}
@@ -3081,7 +3075,7 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length)
trace_ram_discard_range(rbname, start, length);
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBlock *rb = qemu_ram_block_by_name(rbname);
if (!rb) {
@@ -3101,8 +3095,6 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length)
ret = ram_block_discard_range(rb, start, length);
err:
- rcu_read_unlock();
-
return ret;
}
@@ -3231,13 +3223,12 @@ static void ram_init_bitmaps(RAMState *rs)
/* For memory_global_dirty_log_start below. */
qemu_mutex_lock_iothread();
qemu_mutex_lock_ramlist();
- rcu_read_lock();
-
- ram_list_init_bitmaps();
- memory_global_dirty_log_start();
- migration_bitmap_sync_precopy(rs);
- rcu_read_unlock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ ram_list_init_bitmaps();
+ memory_global_dirty_log_start();
+ migration_bitmap_sync_precopy(rs);
+ }
qemu_mutex_unlock_ramlist();
qemu_mutex_unlock_iothread();
}
@@ -3373,24 +3364,23 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
}
(*rsp)->f = f;
- rcu_read_lock();
-
- qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE);
+ WITH_RCU_READ_LOCK_GUARD() {
+ qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE);
- RAMBLOCK_FOREACH_MIGRATABLE(block) {
- qemu_put_byte(f, strlen(block->idstr));
- qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
- qemu_put_be64(f, block->used_length);
- if (migrate_postcopy_ram() && block->page_size != qemu_host_page_size) {
- qemu_put_be64(f, block->page_size);
- }
- if (migrate_ignore_shared()) {
- qemu_put_be64(f, block->mr->addr);
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
+ qemu_put_be64(f, block->used_length);
+ if (migrate_postcopy_ram() && block->page_size !=
+ qemu_host_page_size) {
+ qemu_put_be64(f, block->page_size);
+ }
+ if (migrate_ignore_shared()) {
+ qemu_put_be64(f, block->mr->addr);
+ }
}
}
- rcu_read_unlock();
-
ram_control_before_iterate(f, RAM_CONTROL_SETUP);
ram_control_after_iterate(f, RAM_CONTROL_SETUP);
@@ -3425,55 +3415,57 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
goto out;
}
- rcu_read_lock();
- if (ram_list.version != rs->last_version) {
- ram_state_reset(rs);
- }
-
- /* Read version before ram_list.blocks */
- smp_rmb();
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (ram_list.version != rs->last_version) {
+ ram_state_reset(rs);
+ }
- ram_control_before_iterate(f, RAM_CONTROL_ROUND);
+ /* Read version before ram_list.blocks */
+ smp_rmb();
- t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- i = 0;
- while ((ret = qemu_file_rate_limit(f)) == 0 ||
- !QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
- int pages;
+ ram_control_before_iterate(f, RAM_CONTROL_ROUND);
- if (qemu_file_get_error(f)) {
- break;
- }
+ t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ i = 0;
+ while ((ret = qemu_file_rate_limit(f)) == 0 ||
+ !QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
+ int pages;
- pages = ram_find_and_save_block(rs, false);
- /* no more pages to sent */
- if (pages == 0) {
- done = 1;
- break;
- }
+ if (qemu_file_get_error(f)) {
+ break;
+ }
- if (pages < 0) {
- qemu_file_set_error(f, pages);
- break;
- }
+ pages = ram_find_and_save_block(rs, false);
+ /* no more pages to sent */
+ if (pages == 0) {
+ done = 1;
+ break;
+ }
- rs->target_page_count += pages;
-
- /* we want to check in the 1st loop, just in case it was the 1st time
- and we had to sync the dirty bitmap.
- qemu_clock_get_ns() is a bit expensive, so we only check each some
- iterations
- */
- if ((i & 63) == 0) {
- uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000;
- if (t1 > MAX_WAIT) {
- trace_ram_save_iterate_big_wait(t1, i);
+ if (pages < 0) {
+ qemu_file_set_error(f, pages);
break;
}
+
+ rs->target_page_count += pages;
+
+ /*
+ * we want to check in the 1st loop, just in case it was the 1st
+ * time and we had to sync the dirty bitmap.
+ * qemu_clock_get_ns() is a bit expensive, so we only check each
+ * some iterations
+ */
+ if ((i & 63) == 0) {
+ uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) /
+ 1000000;
+ if (t1 > MAX_WAIT) {
+ trace_ram_save_iterate_big_wait(t1, i);
+ break;
+ }
+ }
+ i++;
}
- i++;
}
- rcu_read_unlock();
/*
* Must occur before EOS (or any QEMUFile operation)
@@ -3511,35 +3503,33 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
RAMState *rs = *temp;
int ret = 0;
- rcu_read_lock();
-
- if (!migration_in_postcopy()) {
- migration_bitmap_sync_precopy(rs);
- }
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (!migration_in_postcopy()) {
+ migration_bitmap_sync_precopy(rs);
+ }
- ram_control_before_iterate(f, RAM_CONTROL_FINISH);
+ ram_control_before_iterate(f, RAM_CONTROL_FINISH);
- /* try transferring iterative blocks of memory */
+ /* try transferring iterative blocks of memory */
- /* flush all remaining blocks regardless of rate limiting */
- while (true) {
- int pages;
+ /* flush all remaining blocks regardless of rate limiting */
+ while (true) {
+ int pages;
- pages = ram_find_and_save_block(rs, !migration_in_colo_state());
- /* no more blocks to sent */
- if (pages == 0) {
- break;
- }
- if (pages < 0) {
- ret = pages;
- break;
+ pages = ram_find_and_save_block(rs, !migration_in_colo_state());
+ /* no more blocks to sent */
+ if (pages == 0) {
+ break;
+ }
+ if (pages < 0) {
+ ret = pages;
+ break;
+ }
}
- }
- flush_compressed_data(rs);
- ram_control_after_iterate(f, RAM_CONTROL_FINISH);
-
- rcu_read_unlock();
+ flush_compressed_data(rs);
+ ram_control_after_iterate(f, RAM_CONTROL_FINISH);
+ }
multifd_send_sync_main(rs);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
@@ -3562,9 +3552,9 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
if (!migration_in_postcopy() &&
remaining_size < max_size) {
qemu_mutex_lock_iothread();
- rcu_read_lock();
- migration_bitmap_sync_precopy(rs);
- rcu_read_unlock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ migration_bitmap_sync_precopy(rs);
+ }
qemu_mutex_unlock_iothread();
remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE;
}
@@ -3908,7 +3898,13 @@ int colo_init_ram_cache(void)
error_report("%s: Can't alloc memory for COLO cache of block %s,"
"size 0x" RAM_ADDR_FMT, __func__, block->idstr,
block->used_length);
- goto out_locked;
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ if (block->colo_cache) {
+ qemu_anon_ram_free(block->colo_cache, block->used_length);
+ block->colo_cache = NULL;
+ }
+ }
+ return -errno;
}
memcpy(block->colo_cache, block->host, block->used_length);
}
@@ -3934,18 +3930,6 @@ int colo_init_ram_cache(void)
memory_global_dirty_log_start();
return 0;
-
-out_locked:
-
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- if (block->colo_cache) {
- qemu_anon_ram_free(block->colo_cache, block->used_length);
- block->colo_cache = NULL;
- }
- }
-
- rcu_read_unlock();
- return -errno;
}
/* It is need to hold the global lock to call this helper */
@@ -3959,16 +3943,14 @@ void colo_release_ram_cache(void)
block->bmap = NULL;
}
- rcu_read_lock();
-
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- if (block->colo_cache) {
- qemu_anon_ram_free(block->colo_cache, block->used_length);
- block->colo_cache = NULL;
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ if (block->colo_cache) {
+ qemu_anon_ram_free(block->colo_cache, block->used_length);
+ block->colo_cache = NULL;
+ }
}
}
-
- rcu_read_unlock();
qemu_mutex_destroy(&ram_state->bitmap_mutex);
g_free(ram_state);
ram_state = NULL;
@@ -4048,7 +4030,7 @@ static int ram_load_postcopy(QEMUFile *f)
bool matches_target_page_size = false;
MigrationIncomingState *mis = migration_incoming_get_current();
/* Temporary page that is later 'placed' */
- void *postcopy_host_page = postcopy_get_tmp_page(mis);
+ void *postcopy_host_page = mis->postcopy_tmp_page;
void *last_host = NULL;
bool all_zero = false;
@@ -4206,31 +4188,30 @@ static void colo_flush_ram_cache(void)
unsigned long offset = 0;
memory_global_dirty_log_sync();
- rcu_read_lock();
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- ramblock_sync_dirty_bitmap(ram_state, block);
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ ramblock_sync_dirty_bitmap(ram_state, block);
+ }
}
- rcu_read_unlock();
trace_colo_flush_ram_cache_begin(ram_state->migration_dirty_pages);
- rcu_read_lock();
- block = QLIST_FIRST_RCU(&ram_list.blocks);
+ WITH_RCU_READ_LOCK_GUARD() {
+ block = QLIST_FIRST_RCU(&ram_list.blocks);
- while (block) {
- offset = migration_bitmap_find_dirty(ram_state, block, offset);
+ while (block) {
+ offset = migration_bitmap_find_dirty(ram_state, block, offset);
- if (offset << TARGET_PAGE_BITS >= block->used_length) {
- offset = 0;
- block = QLIST_NEXT_RCU(block, next);
- } else {
- migration_bitmap_clear_dirty(ram_state, block, offset);
- dst_host = block->host + (offset << TARGET_PAGE_BITS);
- src_host = block->colo_cache + (offset << TARGET_PAGE_BITS);
- memcpy(dst_host, src_host, TARGET_PAGE_SIZE);
+ if (offset << TARGET_PAGE_BITS >= block->used_length) {
+ offset = 0;
+ block = QLIST_NEXT_RCU(block, next);
+ } else {
+ migration_bitmap_clear_dirty(ram_state, block, offset);
+ dst_host = block->host + (offset << TARGET_PAGE_BITS);
+ src_host = block->colo_cache + (offset << TARGET_PAGE_BITS);
+ memcpy(dst_host, src_host, TARGET_PAGE_SIZE);
+ }
}
}
-
- rcu_read_unlock();
trace_colo_flush_ram_cache_end();
}
@@ -4429,16 +4410,15 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
* it will be necessary to reduce the granularity of this
* critical section.
*/
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (postcopy_running) {
+ ret = ram_load_postcopy(f);
+ } else {
+ ret = ram_load_precopy(f);
+ }
- if (postcopy_running) {
- ret = ram_load_postcopy(f);
- } else {
- ret = ram_load_precopy(f);
+ ret |= wait_for_decompress_done();
}
-
- ret |= wait_for_decompress_done();
- rcu_read_unlock();
trace_ram_load_complete(ret, seq_iter);
if (!ret && migration_incoming_in_colo_state()) {
diff --git a/migration/rdma.c b/migration/rdma.c
index 4c74e88a37..e241dcb992 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -88,7 +88,6 @@ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL;
" to abort!"); \
rdma->error_reported = 1; \
} \
- rcu_read_unlock(); \
return rdma->error_state; \
} \
} while (0)
@@ -2678,11 +2677,10 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
size_t i;
size_t len = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -2695,7 +2693,6 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
ret = qemu_rdma_write_flush(f, rdma);
if (ret < 0) {
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -2715,7 +2712,6 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
if (ret < 0) {
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -2724,7 +2720,6 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
}
}
- rcu_read_unlock();
return done;
}
@@ -2764,11 +2759,10 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
ssize_t i;
size_t done = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmain);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -2805,7 +2799,6 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
if (ret < 0) {
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -2819,14 +2812,12 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
/* Still didn't get enough, so lets just return */
if (want) {
if (done == 0) {
- rcu_read_unlock();
return QIO_CHANNEL_ERR_BLOCK;
} else {
break;
}
}
}
- rcu_read_unlock();
return done;
}
@@ -2882,7 +2873,7 @@ qio_channel_rdma_source_prepare(GSource *source,
GIOCondition cond = 0;
*timeout = -1;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
if (rsource->condition == G_IO_IN) {
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
} else {
@@ -2891,7 +2882,6 @@ qio_channel_rdma_source_prepare(GSource *source,
if (!rdma) {
error_report("RDMAContext is NULL when prepare Gsource");
- rcu_read_unlock();
return FALSE;
}
@@ -2900,7 +2890,6 @@ qio_channel_rdma_source_prepare(GSource *source,
}
cond |= G_IO_OUT;
- rcu_read_unlock();
return cond & rsource->condition;
}
@@ -2911,7 +2900,7 @@ qio_channel_rdma_source_check(GSource *source)
RDMAContext *rdma;
GIOCondition cond = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
if (rsource->condition == G_IO_IN) {
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
} else {
@@ -2920,7 +2909,6 @@ qio_channel_rdma_source_check(GSource *source)
if (!rdma) {
error_report("RDMAContext is NULL when check Gsource");
- rcu_read_unlock();
return FALSE;
}
@@ -2929,7 +2917,6 @@ qio_channel_rdma_source_check(GSource *source)
}
cond |= G_IO_OUT;
- rcu_read_unlock();
return cond & rsource->condition;
}
@@ -2943,7 +2930,7 @@ qio_channel_rdma_source_dispatch(GSource *source,
RDMAContext *rdma;
GIOCondition cond = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
if (rsource->condition == G_IO_IN) {
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
} else {
@@ -2952,7 +2939,6 @@ qio_channel_rdma_source_dispatch(GSource *source,
if (!rdma) {
error_report("RDMAContext is NULL when dispatch Gsource");
- rcu_read_unlock();
return FALSE;
}
@@ -2961,7 +2947,6 @@ qio_channel_rdma_source_dispatch(GSource *source,
}
cond |= G_IO_OUT;
- rcu_read_unlock();
return (*func)(QIO_CHANNEL(rsource->rioc),
(cond & rsource->condition),
user_data);
@@ -3073,7 +3058,7 @@ qio_channel_rdma_shutdown(QIOChannel *ioc,
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
RDMAContext *rdmain, *rdmaout;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdmain = atomic_rcu_read(&rioc->rdmain);
rdmaout = atomic_rcu_read(&rioc->rdmain);
@@ -3100,7 +3085,6 @@ qio_channel_rdma_shutdown(QIOChannel *ioc,
break;
}
- rcu_read_unlock();
return 0;
}
@@ -3146,18 +3130,16 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque,
RDMAContext *rdma;
int ret;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
CHECK_ERROR_STATE();
if (migration_in_postcopy()) {
- rcu_read_unlock();
return RAM_SAVE_CONTROL_NOT_SUPP;
}
@@ -3242,11 +3224,9 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque,
}
}
- rcu_read_unlock();
return RAM_SAVE_CONTROL_DELAYED;
err:
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -3470,11 +3450,10 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque)
int count = 0;
int i = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmain);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -3717,7 +3696,6 @@ out:
if (ret < 0) {
rdma->error_state = ret;
}
- rcu_read_unlock();
return ret;
}
@@ -3735,11 +3713,10 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
int curr;
int found = -1;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmain);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -3753,7 +3730,6 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
if (found == -1) {
error_report("RAMBlock '%s' not found on destination", name);
- rcu_read_unlock();
return -ENOENT;
}
@@ -3761,7 +3737,6 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
trace_rdma_block_notification_handle(name, rdma->next_src_index);
rdma->next_src_index++;
- rcu_read_unlock();
return 0;
}
@@ -3786,17 +3761,15 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque,
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
RDMAContext *rdma;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
CHECK_ERROR_STATE();
if (migration_in_postcopy()) {
- rcu_read_unlock();
return 0;
}
@@ -3804,7 +3777,6 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque,
qemu_put_be64(f, RAM_SAVE_FLAG_HOOK);
qemu_fflush(f);
- rcu_read_unlock();
return 0;
}
@@ -3821,17 +3793,15 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
RDMAControlHeader head = { .len = 0, .repeat = 1 };
int ret = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
CHECK_ERROR_STATE();
if (migration_in_postcopy()) {
- rcu_read_unlock();
return 0;
}
@@ -3863,7 +3833,6 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
qemu_rdma_reg_whole_ram_blocks : NULL);
if (ret < 0) {
ERROR(errp, "receiving remote info!");
- rcu_read_unlock();
return ret;
}
@@ -3887,7 +3856,6 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
"not identical on both the source and destination.",
local->nb_blocks, nb_dest_blocks);
rdma->error_state = -EINVAL;
- rcu_read_unlock();
return -EINVAL;
}
@@ -3904,7 +3872,6 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
local->block[i].length,
rdma->dest_blocks[i].length);
rdma->error_state = -EINVAL;
- rcu_read_unlock();
return -EINVAL;
}
local->block[i].remote_host_addr =
@@ -3922,11 +3889,9 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
goto err;
}
- rcu_read_unlock();
return 0;
err:
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
diff --git a/migration/savevm.c b/migration/savevm.c
index bb9462a54d..8d95e261f6 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1215,6 +1215,8 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy)
save_section_footer(f, se);
if (ret < 0) {
+ error_report("failed to save SaveStateEntry with id(name): %d(%s)",
+ se->section_id, se->idstr);
qemu_file_set_error(f, ret);
}
if (ret <= 0) {
@@ -1835,6 +1837,8 @@ static void *postcopy_ram_listen_thread(void *opaque)
rcu_unregister_thread();
mis->have_listen_thread = false;
+ postcopy_state_set(POSTCOPY_INCOMING_END);
+
return NULL;
}
@@ -1865,7 +1869,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
* shouldn't be doing anything yet so don't actually expect requests
*/
if (migrate_postcopy_ram()) {
- if (postcopy_ram_enable_notify(mis)) {
+ if (postcopy_ram_incoming_setup(mis)) {
postcopy_ram_incoming_cleanup(mis);
return -1;
}
@@ -1876,11 +1880,6 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
return -1;
}
- if (mis->have_listen_thread) {
- error_report("CMD_POSTCOPY_RAM_LISTEN already has a listen thread");
- return -1;
- }
-
mis->have_listen_thread = true;
/* Start up the listening thread and wait for it to signal ready */
qemu_sem_init(&mis->listen_thread_sem, 0);
@@ -1934,7 +1933,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
/* After all discards we can start running and asking for pages */
static int loadvm_postcopy_handle_run(MigrationIncomingState *mis)
{
- PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
+ PostcopyState ps = postcopy_state_get();
trace_loadvm_postcopy_handle_run();
if (ps != POSTCOPY_INCOMING_LISTENING) {
@@ -1942,6 +1941,7 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis)
return -1;
}
+ postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
mis->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, mis);
qemu_bh_schedule(mis->bh);
diff --git a/migration/trace-events b/migration/trace-events
index 858d415d56..6dee7b5389 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -71,6 +71,11 @@ get_qtailq_end(const char *name, const char *reason, int val) "%s %s/%d"
put_qtailq(const char *name, int version_id) "%s v%d"
put_qtailq_end(const char *name, const char *reason) "%s %s"
+get_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d"
+get_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d"
+put_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d"
+put_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d"
+
# qemu-file.c
qemu_file_fclose(void) ""
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index bee658a1b2..7236cf92bc 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -691,3 +691,155 @@ const VMStateInfo vmstate_info_qtailq = {
.get = get_qtailq,
.put = put_qtailq,
};
+
+struct put_gtree_data {
+ QEMUFile *f;
+ const VMStateDescription *key_vmsd;
+ const VMStateDescription *val_vmsd;
+ QJSON *vmdesc;
+ int ret;
+};
+
+static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data)
+{
+ struct put_gtree_data *capsule = (struct put_gtree_data *)data;
+ QEMUFile *f = capsule->f;
+ int ret;
+
+ qemu_put_byte(f, true);
+
+ /* put the key */
+ if (!capsule->key_vmsd) {
+ qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
+ } else {
+ ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc);
+ if (ret) {
+ capsule->ret = ret;
+ return true;
+ }
+ }
+
+ /* put the data */
+ ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc);
+ if (ret) {
+ capsule->ret = ret;
+ return true;
+ }
+ return false;
+}
+
+static int put_gtree(QEMUFile *f, void *pv, size_t unused_size,
+ const VMStateField *field, QJSON *vmdesc)
+{
+ bool direct_key = (!field->start);
+ const VMStateDescription *key_vmsd = direct_key ? NULL : &field->vmsd[1];
+ const VMStateDescription *val_vmsd = &field->vmsd[0];
+ const char *key_vmsd_name = direct_key ? "direct" : key_vmsd->name;
+ struct put_gtree_data capsule = {
+ .f = f,
+ .key_vmsd = key_vmsd,
+ .val_vmsd = val_vmsd,
+ .vmdesc = vmdesc,
+ .ret = 0};
+ GTree **pval = pv;
+ GTree *tree = *pval;
+ uint32_t nnodes = g_tree_nnodes(tree);
+ int ret;
+
+ trace_put_gtree(field->name, key_vmsd_name, val_vmsd->name, nnodes);
+ qemu_put_be32(f, nnodes);
+ g_tree_foreach(tree, put_gtree_elem, (gpointer)&capsule);
+ qemu_put_byte(f, false);
+ ret = capsule.ret;
+ if (ret) {
+ error_report("%s : failed to save gtree (%d)", field->name, ret);
+ }
+ trace_put_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret);
+ return ret;
+}
+
+static int get_gtree(QEMUFile *f, void *pv, size_t unused_size,
+ const VMStateField *field)
+{
+ bool direct_key = (!field->start);
+ const VMStateDescription *key_vmsd = direct_key ? NULL : &field->vmsd[1];
+ const VMStateDescription *val_vmsd = &field->vmsd[0];
+ const char *key_vmsd_name = direct_key ? "direct" : key_vmsd->name;
+ int version_id = field->version_id;
+ size_t key_size = field->start;
+ size_t val_size = field->size;
+ int nnodes, count = 0;
+ GTree **pval = pv;
+ GTree *tree = *pval;
+ void *key, *val;
+ int ret = 0;
+
+ /* in case of direct key, the key vmsd can be {}, ie. check fields */
+ if (!direct_key && version_id > key_vmsd->version_id) {
+ error_report("%s %s", key_vmsd->name, "too new");
+ return -EINVAL;
+ }
+ if (!direct_key && version_id < key_vmsd->minimum_version_id) {
+ error_report("%s %s", key_vmsd->name, "too old");
+ return -EINVAL;
+ }
+ if (version_id > val_vmsd->version_id) {
+ error_report("%s %s", val_vmsd->name, "too new");
+ return -EINVAL;
+ }
+ if (version_id < val_vmsd->minimum_version_id) {
+ error_report("%s %s", val_vmsd->name, "too old");
+ return -EINVAL;
+ }
+
+ nnodes = qemu_get_be32(f);
+ trace_get_gtree(field->name, key_vmsd_name, val_vmsd->name, nnodes);
+
+ while (qemu_get_byte(f)) {
+ if ((++count) > nnodes) {
+ ret = -EINVAL;
+ break;
+ }
+ if (direct_key) {
+ key = (void *)(uintptr_t)qemu_get_be64(f);
+ } else {
+ key = g_malloc0(key_size);
+ ret = vmstate_load_state(f, key_vmsd, key, version_id);
+ if (ret) {
+ error_report("%s : failed to load %s (%d)",
+ field->name, key_vmsd->name, ret);
+ goto key_error;
+ }
+ }
+ val = g_malloc0(val_size);
+ ret = vmstate_load_state(f, val_vmsd, val, version_id);
+ if (ret) {
+ error_report("%s : failed to load %s (%d)",
+ field->name, val_vmsd->name, ret);
+ goto val_error;
+ }
+ g_tree_insert(tree, key, val);
+ }
+ if (count != nnodes) {
+ error_report("%s inconsistent stream when loading the gtree",
+ field->name);
+ return -EINVAL;
+ }
+ trace_get_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret);
+ return ret;
+val_error:
+ g_free(val);
+key_error:
+ if (!direct_key) {
+ g_free(key);
+ }
+ trace_get_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret);
+ return ret;
+}
+
+
+const VMStateInfo vmstate_info_gtree = {
+ .name = "gtree",
+ .get = get_gtree,
+ .put = put_gtree,
+};
diff --git a/qapi/block-core.json b/qapi/block-core.json
index e6edd641f1..f66553aac7 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -860,6 +860,8 @@
#
# @wr_bytes: The number of bytes written by the device.
#
+# @unmap_bytes: The number of bytes unmapped by the device (Since 4.2)
+#
# @rd_operations: The number of read operations performed by the device.
#
# @wr_operations: The number of write operations performed by the device.
@@ -867,12 +869,18 @@
# @flush_operations: The number of cache flush operations performed by the
# device (since 0.15.0)
#
-# @flush_total_time_ns: Total time spend on cache flushes in nano-seconds
-# (since 0.15.0).
+# @unmap_operations: The number of unmap operations performed by the device
+# (Since 4.2)
+#
+# @rd_total_time_ns: Total time spent on reads in nanoseconds (since 0.15.0).
+#
+# @wr_total_time_ns: Total time spent on writes in nanoseconds (since 0.15.0).
#
-# @wr_total_time_ns: Total time spend on writes in nano-seconds (since 0.15.0).
+# @flush_total_time_ns: Total time spent on cache flushes in nanoseconds
+# (since 0.15.0).
#
-# @rd_total_time_ns: Total_time_spend on reads in nano-seconds (since 0.15.0).
+# @unmap_total_time_ns: Total time spent on unmap operations in nanoseconds
+# (Since 4.2)
#
# @wr_highest_offset: The offset after the greatest byte written to the
# device. The intended use of this information is for
@@ -885,6 +893,9 @@
# @wr_merged: Number of write requests that have been merged into another
# request (Since 2.3).
#
+# @unmap_merged: Number of unmap requests that have been merged into another
+# request (Since 4.2)
+#
# @idle_time_ns: Time since the last I/O operation, in
# nanoseconds. If the field is absent it means that
# there haven't been any operations yet (Since 2.5).
@@ -898,6 +909,9 @@
# @failed_flush_operations: The number of failed flush operations
# performed by the device (Since 2.5)
#
+# @failed_unmap_operations: The number of failed unmap operations performed
+# by the device (Since 4.2)
+#
# @invalid_rd_operations: The number of invalid read operations
# performed by the device (Since 2.5)
#
@@ -907,6 +921,9 @@
# @invalid_flush_operations: The number of invalid flush operations
# performed by the device (Since 2.5)
#
+# @invalid_unmap_operations: The number of invalid unmap operations performed
+# by the device (Since 4.2)
+#
# @account_invalid: Whether invalid operations are included in the
# last access statistics (Since 2.5)
#
@@ -925,14 +942,18 @@
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
- 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
- 'wr_operations': 'int', 'flush_operations': 'int',
- 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
- 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int',
- 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int',
+ 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'unmap_bytes' : 'int',
+ 'rd_operations': 'int', 'wr_operations': 'int',
+ 'flush_operations': 'int', 'unmap_operations': 'int',
+ 'rd_total_time_ns': 'int', 'wr_total_time_ns': 'int',
+ 'flush_total_time_ns': 'int', 'unmap_total_time_ns': 'int',
+ 'wr_highest_offset': 'int',
+ 'rd_merged': 'int', 'wr_merged': 'int', 'unmap_merged': 'int',
+ '*idle_time_ns': 'int',
'failed_rd_operations': 'int', 'failed_wr_operations': 'int',
- 'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
- 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
+ 'failed_flush_operations': 'int', 'failed_unmap_operations': 'int',
+ 'invalid_rd_operations': 'int', 'invalid_wr_operations': 'int',
+ 'invalid_flush_operations': 'int', 'invalid_unmap_operations': 'int',
'account_invalid': 'bool', 'account_failed': 'bool',
'timed_stats': ['BlockDeviceTimedStats'],
'*rd_latency_histogram': 'BlockLatencyHistogramInfo',
@@ -940,6 +961,41 @@
'*flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
##
+# @BlockStatsSpecificFile:
+#
+# File driver statistics
+#
+# @discard-nb-ok: The number of successful discard operations performed by
+# the driver.
+#
+# @discard-nb-failed: The number of failed discard operations performed by
+# the driver.
+#
+# @discard-bytes-ok: The number of bytes discarded by the driver.
+#
+# Since: 4.2
+##
+{ 'struct': 'BlockStatsSpecificFile',
+ 'data': {
+ 'discard-nb-ok': 'uint64',
+ 'discard-nb-failed': 'uint64',
+ 'discard-bytes-ok': 'uint64' } }
+
+##
+# @BlockStatsSpecific:
+#
+# Block driver specific statistics
+#
+# Since: 4.2
+##
+{ 'union': 'BlockStatsSpecific',
+ 'base': { 'driver': 'BlockdevDriver' },
+ 'discriminator': 'driver',
+ 'data': {
+ 'file': 'BlockStatsSpecificFile',
+ 'host_device': 'BlockStatsSpecificFile' } }
+
+##
# @BlockStats:
#
# Statistics of a virtual block device or a block backing device.
@@ -954,6 +1010,8 @@
#
# @stats: A @BlockDeviceStats for the device.
#
+# @driver-specific: Optional driver-specific stats. (Since 4.2)
+#
# @parent: This describes the file block device if it has one.
# Contains recursively the statistics of the underlying
# protocol (e.g. the host file for a qcow2 image). If there is
@@ -967,6 +1025,7 @@
{ 'struct': 'BlockStats',
'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str',
'stats': 'BlockDeviceStats',
+ '*driver-specific': 'BlockStatsSpecific',
'*parent': 'BlockStats',
'*backing': 'BlockStats'} }
@@ -1391,6 +1450,11 @@
# list without user intervention.
# Defaults to true. (Since 2.12)
#
+# @filter-node-name: the node name that should be assigned to the
+# filter driver that the backup job inserts into the graph
+# above node specified by @drive. If this option is not given,
+# a node name is autogenerated. (Since: 4.2)
+#
# 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.
@@ -1404,7 +1468,8 @@
'*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError',
- '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool',
+ '*filter-node-name': 'str' } }
##
# @DriveBackup:
diff --git a/qemu-options.hx b/qemu-options.hx
index 2a04ca6ac5..793d70ff93 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1339,7 +1339,7 @@ ETEXI
DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
"-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n"
- " [,id=id][,writeout=immediate][,readonly][,fmode=fmode][,dmode=dmode]\n"
+ " [,id=id][,writeout=immediate][,readonly][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n"
"-virtfs proxy,mount_tag=tag,socket=socket[,id=id][,writeout=immediate][,readonly]\n"
"-virtfs proxy,mount_tag=tag,sock_fd=sock_fd[,id=id][,writeout=immediate][,readonly]\n"
"-virtfs synth,mount_tag=tag[,id=id][,readonly]\n",
@@ -1347,7 +1347,7 @@ DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
STEXI
-@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}]
+@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}][,multidevs=@var{multidevs}]
@itemx -virtfs proxy,socket=@var{socket},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly]
@itemx -virtfs proxy,sock_fd=@var{sock_fd},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly]
@itemx -virtfs synth,mount_tag=@var{mount_tag}
@@ -1403,6 +1403,28 @@ Specifies the default mode for newly created directories on the host. Works
only with security models "mapped-xattr" and "mapped-file".
@item mount_tag=@var{mount_tag}
Specifies the tag name to be used by the guest to mount this export point.
+@item multidevs=@var{multidevs}
+Specifies how to deal with multiple devices being shared with a 9p export.
+Supported behaviours are either "remap", "forbid" or "warn". The latter is
+the default behaviour on which virtfs 9p expects only one device to be
+shared with the same export, and if more than one device is shared and
+accessed via the same 9p export then only a warning message is logged
+(once) by qemu on host side. In order to avoid file ID collisions on guest
+you should either create a separate virtfs export for each device to be
+shared with guests (recommended way) or you might use "remap" instead which
+allows you to share multiple devices with only one export instead, which is
+achieved by remapping the original inode numbers from host to guest in a
+way that would prevent such collisions. Remapping inodes in such use cases
+is required because the original device IDs from host are never passed and
+exposed on guest. Instead all files of an export shared with virtfs always
+share the same device id on guest. So two files with identical inode
+numbers but from actually different devices on host would otherwise cause a
+file ID collision and hence potential misbehaviours on guest. "forbid" on
+the other hand assumes like "warn" that only one device is shared by the
+same export, however it will not only log a warning message but also
+deny access to additional devices on guest. Note though that "forbid" does
+currently not block all possible file access operations (e.g. readdir()
+would still return entries from other devices).
@end table
ETEXI
diff --git a/target/s390x/cc_helper.c b/target/s390x/cc_helper.c
index cf68792733..44731e4a85 100644
--- a/target/s390x/cc_helper.c
+++ b/target/s390x/cc_helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qemu/host-utils.h"
@@ -588,8 +589,7 @@ void HELPER(sacf)(CPUS390XState *env, uint64_t a1)
break;
default:
HELPER_LOG("unknown sacf mode: %" PRIx64 "\n", a1);
- s390_program_interrupt(env, PGM_SPECIFICATION, 2, GETPC());
- break;
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
}
#endif
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 163dae13d7..17460ed7b3 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -1,6 +1,10 @@
/*
* S/390 virtual CPU header
*
+ * For details on the s390x architecture and used definitions (e.g.,
+ * PSW, PER and DAT (Dynamic Address Translation)), please refer to
+ * the "z/Architecture Principles of Operations" - a.k.a. PoP.
+ *
* Copyright (c) 2009 Ulrich Hecht
* Copyright IBM Corp. 2012, 2018
*
@@ -30,7 +34,7 @@
/* The z/Architecture has a strong memory model with some store-after-load re-ordering */
#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD)
-#define TARGET_INSN_START_EXTRA_WORDS 1
+#define TARGET_INSN_START_EXTRA_WORDS 2
#define MMU_MODE0_SUFFIX _primary
#define MMU_MODE1_SUFFIX _secondary
@@ -311,6 +315,7 @@ extern const VMStateDescription vmstate_s390_cpu;
#define CR0_EDAT 0x0000000000800000ULL
#define CR0_AFP 0x0000000000040000ULL
#define CR0_VECTOR 0x0000000000020000ULL
+#define CR0_IEP 0x0000000000100000ULL
#define CR0_EMERGENCY_SIGNAL_SC 0x0000000000004000ULL
#define CR0_EXTERNAL_CALL_SC 0x0000000000002000ULL
#define CR0_CKC_SC 0x0000000000000800ULL
@@ -558,26 +563,60 @@ QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096);
#define ASCE_TYPE_SEGMENT 0x00 /* segment table type */
#define ASCE_TABLE_LENGTH 0x03 /* region table length */
-#define REGION_ENTRY_ORIGIN (~0xfffULL) /* region/segment table origin */
-#define REGION_ENTRY_RO 0x200 /* region/segment protection bit */
-#define REGION_ENTRY_TF 0xc0 /* region/segment table offset */
-#define REGION_ENTRY_INV 0x20 /* invalid region table entry */
-#define REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */
-#define REGION_ENTRY_TYPE_R1 0x0c /* region first table type */
-#define REGION_ENTRY_TYPE_R2 0x08 /* region second table type */
-#define REGION_ENTRY_TYPE_R3 0x04 /* region third table type */
-#define REGION_ENTRY_LENGTH 0x03 /* region third length */
-
-#define SEGMENT_ENTRY_ORIGIN (~0x7ffULL) /* segment table origin */
-#define SEGMENT_ENTRY_FC 0x400 /* format control */
-#define SEGMENT_ENTRY_RO 0x200 /* page protection bit */
-#define SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */
-
-#define VADDR_PX 0xff000 /* page index bits */
-
-#define PAGE_RO 0x200 /* HW read-only bit */
-#define PAGE_INVALID 0x400 /* HW invalid bit */
-#define PAGE_RES0 0x800 /* bit must be zero */
+#define REGION_ENTRY_ORIGIN 0xfffffffffffff000ULL
+#define REGION_ENTRY_P 0x0000000000000200ULL
+#define REGION_ENTRY_TF 0x00000000000000c0ULL
+#define REGION_ENTRY_I 0x0000000000000020ULL
+#define REGION_ENTRY_TT 0x000000000000000cULL
+#define REGION_ENTRY_TL 0x0000000000000003ULL
+
+#define REGION_ENTRY_TT_REGION1 0x000000000000000cULL
+#define REGION_ENTRY_TT_REGION2 0x0000000000000008ULL
+#define REGION_ENTRY_TT_REGION3 0x0000000000000004ULL
+
+#define REGION3_ENTRY_RFAA 0xffffffff80000000ULL
+#define REGION3_ENTRY_AV 0x0000000000010000ULL
+#define REGION3_ENTRY_ACC 0x000000000000f000ULL
+#define REGION3_ENTRY_F 0x0000000000000800ULL
+#define REGION3_ENTRY_FC 0x0000000000000400ULL
+#define REGION3_ENTRY_IEP 0x0000000000000100ULL
+#define REGION3_ENTRY_CR 0x0000000000000010ULL
+
+#define SEGMENT_ENTRY_ORIGIN 0xfffffffffffff800ULL
+#define SEGMENT_ENTRY_SFAA 0xfffffffffff00000ULL
+#define SEGMENT_ENTRY_AV 0x0000000000010000ULL
+#define SEGMENT_ENTRY_ACC 0x000000000000f000ULL
+#define SEGMENT_ENTRY_F 0x0000000000000800ULL
+#define SEGMENT_ENTRY_FC 0x0000000000000400ULL
+#define SEGMENT_ENTRY_P 0x0000000000000200ULL
+#define SEGMENT_ENTRY_IEP 0x0000000000000100ULL
+#define SEGMENT_ENTRY_I 0x0000000000000020ULL
+#define SEGMENT_ENTRY_CS 0x0000000000000010ULL
+#define SEGMENT_ENTRY_TT 0x000000000000000cULL
+
+#define SEGMENT_ENTRY_TT_SEGMENT 0x0000000000000000ULL
+
+#define PAGE_ENTRY_0 0x0000000000000800ULL
+#define PAGE_ENTRY_I 0x0000000000000400ULL
+#define PAGE_ENTRY_P 0x0000000000000200ULL
+#define PAGE_ENTRY_IEP 0x0000000000000100ULL
+
+#define VADDR_REGION1_TX_MASK 0xffe0000000000000ULL
+#define VADDR_REGION2_TX_MASK 0x001ffc0000000000ULL
+#define VADDR_REGION3_TX_MASK 0x000003ff80000000ULL
+#define VADDR_SEGMENT_TX_MASK 0x000000007ff00000ULL
+#define VADDR_PAGE_TX_MASK 0x00000000000ff000ULL
+
+#define VADDR_REGION1_TX(vaddr) (((vaddr) & VADDR_REGION1_TX_MASK) >> 53)
+#define VADDR_REGION2_TX(vaddr) (((vaddr) & VADDR_REGION2_TX_MASK) >> 42)
+#define VADDR_REGION3_TX(vaddr) (((vaddr) & VADDR_REGION3_TX_MASK) >> 31)
+#define VADDR_SEGMENT_TX(vaddr) (((vaddr) & VADDR_SEGMENT_TX_MASK) >> 20)
+#define VADDR_PAGE_TX(vaddr) (((vaddr) & VADDR_PAGE_TX_MASK) >> 12)
+
+#define VADDR_REGION1_TL(vaddr) (((vaddr) & 0xc000000000000000ULL) >> 62)
+#define VADDR_REGION2_TL(vaddr) (((vaddr) & 0x0018000000000000ULL) >> 51)
+#define VADDR_REGION3_TL(vaddr) (((vaddr) & 0x0000030000000000ULL) >> 40)
+#define VADDR_SEGMENT_TL(vaddr) (((vaddr) & 0x0000000060000000ULL) >> 29)
#define SK_C (0x1 << 1)
#define SK_R (0x1 << 2)
@@ -765,11 +804,8 @@ int cpu_s390x_signal_handler(int host_signum, void *pinfo, void *puc);
void s390_crw_mchk(void);
void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
uint32_t io_int_parm, uint32_t io_int_word);
-/* automatically detect the instruction length */
-#define ILEN_AUTO 0xff
#define RA_IGNORED 0
-void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
- uintptr_t ra);
+void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra);
/* service interrupts are floating therefore we must not pass an cpustate */
void s390_sclp_extint(uint32_t parm);
diff --git a/target/s390x/crypto_helper.c b/target/s390x/crypto_helper.c
index 5c79790187..ff3fbc3950 100644
--- a/target/s390x/crypto_helper.c
+++ b/target/s390x/crypto_helper.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
@@ -34,16 +35,14 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3,
case S390_FEAT_TYPE_PCKMO:
case S390_FEAT_TYPE_PCC:
if (mod) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
- return 0;
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
break;
}
s390_get_feat_block(type, subfunc);
if (!test_be_bit(fc, subfunc)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
- return 0;
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
switch (fc) {
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index 65eabf0461..53c2f81f2a 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -61,12 +61,12 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
IplParameterBlock *iplb;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return;
}
if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
@@ -82,13 +82,13 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
break;
case 5:
if ((r1 & 1) || (addr & 0x0fffULL)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (!address_space_access_valid(&address_space_memory, addr,
sizeof(IplParameterBlock), false,
MEMTXATTRS_UNSPECIFIED)) {
- s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
return;
}
iplb = g_new0(IplParameterBlock, 1);
@@ -112,13 +112,13 @@ out:
return;
case 6:
if ((r1 & 1) || (addr & 0x0fffULL)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (!address_space_access_valid(&address_space_memory, addr,
sizeof(IplParameterBlock), true,
MEMTXATTRS_UNSPECIFIED)) {
- s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
return;
}
iplb = s390_ipl_get_iplb();
@@ -130,7 +130,7 @@ out:
}
return;
default:
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
break;
}
}
diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c
index 892f659d5a..e70c20d363 100644
--- a/target/s390x/excp_helper.c
+++ b/target/s390x/excp_helper.c
@@ -34,15 +34,15 @@
#include "hw/boards.h"
#endif
-void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
- int ilen, uintptr_t ra)
+void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env,
+ uint32_t code, uintptr_t ra)
{
CPUState *cs = env_cpu(env);
cpu_restore_state(cs, ra, true);
qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
env->psw.addr);
- trigger_pgm_exception(env, code, ilen);
+ trigger_pgm_exception(env, code);
cpu_loop_exit(cs);
}
@@ -60,7 +60,7 @@ void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc,
if (env->cregs[0] & CR0_AFP) {
env->fpc = deposit32(env->fpc, 8, 8, dxc);
}
- tcg_s390_program_interrupt(env, PGM_DATA, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_DATA, ra);
}
void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc,
@@ -75,7 +75,7 @@ void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc,
/* Always store the VXC into the FPC, without AFP it is undefined */
env->fpc = deposit32(env->fpc, 8, 8, vxc);
- tcg_s390_program_interrupt(env, PGM_VECTOR_PROCESSING, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_VECTOR_PROCESSING, ra);
}
void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc)
@@ -96,7 +96,7 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
{
S390CPU *cpu = S390_CPU(cs);
- trigger_pgm_exception(&cpu->env, PGM_ADDRESSING, ILEN_AUTO);
+ trigger_pgm_exception(&cpu->env, PGM_ADDRESSING);
/* On real machines this value is dropped into LowMem. Since this
is userland, simply put this someplace that cpu_loop can find it. */
cpu->env.__excp_addr = address;
@@ -126,8 +126,8 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
target_ulong vaddr, raddr;
- uint64_t asc;
- int prot, fail;
+ uint64_t asc, tec;
+ int prot, excp;
qemu_log_mask(CPU_LOG_MMU, "%s: addr 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
__func__, address, access_type, mmu_idx);
@@ -140,30 +140,30 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (!(env->psw.mask & PSW_MASK_64)) {
vaddr &= 0x7fffffff;
}
- fail = mmu_translate(env, vaddr, access_type, asc, &raddr, &prot, true);
+ excp = mmu_translate(env, vaddr, access_type, asc, &raddr, &prot, &tec);
} else if (mmu_idx == MMU_REAL_IDX) {
/* 31-Bit mode */
if (!(env->psw.mask & PSW_MASK_64)) {
vaddr &= 0x7fffffff;
}
- fail = mmu_translate_real(env, vaddr, access_type, &raddr, &prot);
+ excp = mmu_translate_real(env, vaddr, access_type, &raddr, &prot, &tec);
} else {
g_assert_not_reached();
}
/* check out of RAM access */
- if (!fail &&
+ if (!excp &&
!address_space_access_valid(&address_space_memory, raddr,
TARGET_PAGE_SIZE, access_type,
MEMTXATTRS_UNSPECIFIED)) {
qemu_log_mask(CPU_LOG_MMU,
"%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n",
__func__, (uint64_t)raddr, (uint64_t)ram_size);
- trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO);
- fail = 1;
+ excp = PGM_ADDRESSING;
+ tec = 0; /* unused */
}
- if (!fail) {
+ if (!excp) {
qemu_log_mask(CPU_LOG_MMU,
"%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n",
__func__, (uint64_t)vaddr, (uint64_t)raddr, prot);
@@ -175,23 +175,20 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
return false;
}
- cpu_restore_state(cs, retaddr, true);
+ if (excp != PGM_ADDRESSING) {
+ stq_phys(env_cpu(env)->as,
+ env->psa + offsetof(LowCore, trans_exc_code), tec);
+ }
/*
- * The ILC value for code accesses is undefined. The important
- * thing here is to *not* leave env->int_pgm_ilen set to ILEN_AUTO,
- * which would cause do_program_interrupt to attempt to read from
- * env->psw.addr again. C.f. the condition in trigger_page_fault,
- * but is not universally applied.
- *
- * ??? If we remove ILEN_AUTO, by moving the computation of ILEN
- * into cpu_restore_state, then we may remove this entirely.
+ * For data accesses, ILEN will be filled in from the unwind info,
+ * within cpu_loop_exit_restore. For code accesses, retaddr == 0,
+ * and so unwinding will not occur. However, ILEN is also undefined
+ * for that case -- we choose to set ILEN = 2.
*/
- if (access_type == MMU_INST_FETCH) {
- env->int_pgm_ilen = 2;
- }
-
- cpu_loop_exit(cs);
+ env->int_pgm_ilen = 2;
+ trigger_pgm_exception(env, excp);
+ cpu_loop_exit_restore(cs, retaddr);
}
static void do_program_interrupt(CPUS390XState *env)
@@ -200,9 +197,6 @@ static void do_program_interrupt(CPUS390XState *env)
LowCore *lowcore;
int ilen = env->int_pgm_ilen;
- if (ilen == ILEN_AUTO) {
- ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
- }
assert(ilen == 2 || ilen == 4 || ilen == 6);
switch (env->int_pgm_code) {
@@ -614,7 +608,7 @@ void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, retaddr);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr);
}
#endif /* CONFIG_USER_ONLY */
diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c
index 5faf973c6c..8bb9f54fd0 100644
--- a/target/s390x/fpu_helper.c
+++ b/target/s390x/fpu_helper.c
@@ -825,7 +825,7 @@ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
{
if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
(!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
/* Install everything in the main FPC. */
@@ -843,7 +843,7 @@ void HELPER(sfas)(CPUS390XState *env, uint64_t fpc)
if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
(!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
/*
@@ -880,7 +880,7 @@ void HELPER(sfas)(CPUS390XState *env, uint64_t fpc)
void HELPER(srnm)(CPUS390XState *env, uint64_t rnd)
{
if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
env->fpc = deposit32(env->fpc, 0, 3, rnd);
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index 49a650ac52..6278845b12 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -698,15 +698,23 @@ static uint16_t qemu_V4_0[] = {
S390_FEAT_ZPCI,
};
-static uint16_t qemu_LATEST[] = {
+static uint16_t qemu_V4_1[] = {
S390_FEAT_STFLE_53,
S390_FEAT_VECTOR,
};
+static uint16_t qemu_LATEST[] = {
+ S390_FEAT_ACCESS_EXCEPTION_FS_INDICATION,
+ S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2,
+ S390_FEAT_ESOP,
+};
+
/* add all new definitions before this point */
static uint16_t qemu_MAX[] = {
/* generates a dependency warning, leave it out for now */
S390_FEAT_MSA_EXT_5,
+ /* features introduced after the z13 */
+ S390_FEAT_INSTRUCTION_EXEC_PROT,
};
/****** END FEATURE DEFS ******/
@@ -824,6 +832,7 @@ static FeatGroupDefSpec QemuFeatDef[] = {
QEMU_FEAT_INITIALIZER(V2_11),
QEMU_FEAT_INITIALIZER(V3_1),
QEMU_FEAT_INITIALIZER(V4_0),
+ QEMU_FEAT_INITIALIZER(V4_1),
QEMU_FEAT_INITIALIZER(LATEST),
QEMU_FEAT_INITIALIZER(MAX),
};
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 948c0398d4..a3a49164e4 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -52,6 +52,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
target_ulong raddr;
int prot;
uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ uint64_t tec;
/* 31-Bit mode */
if (!(env->psw.mask & PSW_MASK_64)) {
@@ -63,7 +64,11 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr)
asc = PSW_ASC_PRIMARY;
}
- if (mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false)) {
+ /*
+ * We want to read code even if IEP is active. Use MMU_DATA_LOAD instead
+ * of MMU_INST_FETCH.
+ */
+ if (mmu_translate(env, vaddr, MMU_DATA_LOAD, asc, &raddr, &prot, &tec)) {
return -1;
}
return raddr;
diff --git a/target/s390x/int_helper.c b/target/s390x/int_helper.c
index d13cc49be6..658507dd6d 100644
--- a/target/s390x/int_helper.c
+++ b/target/s390x/int_helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
@@ -39,7 +40,7 @@ int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64)
int64_t q;
if (b == 0) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
ret = q = a / b;
@@ -47,7 +48,7 @@ int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64)
/* Catch non-representable quotient. */
if (ret != q) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
return ret;
@@ -60,7 +61,7 @@ uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64)
uint64_t q;
if (b == 0) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
ret = q = a / b;
@@ -68,7 +69,7 @@ uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64)
/* Catch non-representable quotient. */
if (ret != q) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
return ret;
@@ -79,7 +80,7 @@ int64_t HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b)
{
/* Catch divide by zero, and non-representable quotient (MIN / -1). */
if (b == 0 || (b == -1 && a == (1ll << 63))) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
env->retxl = a % b;
return a / b;
@@ -92,7 +93,7 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al,
uint64_t ret;
/* Signal divide by zero. */
if (b == 0) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
if (ah == 0) {
/* 64 -> 64/64 case */
@@ -106,7 +107,7 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al,
env->retxl = a % b;
ret = q;
if (ret != q) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
#else
/* 32-bit hosts would need special wrapper functionality - just abort if
diff --git a/target/s390x/internal.h b/target/s390x/internal.h
index c243fa725b..d37816104d 100644
--- a/target/s390x/internal.h
+++ b/target/s390x/internal.h
@@ -317,7 +317,7 @@ void cpu_unmap_lowcore(LowCore *lowcore);
/* interrupt.c */
-void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen);
+void trigger_pgm_exception(CPUS390XState *env, uint32_t code);
void cpu_inject_clock_comparator(S390CPU *cpu);
void cpu_inject_cpu_timer(S390CPU *cpu);
void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr);
@@ -360,9 +360,9 @@ void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len,
/* mmu_helper.c */
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
- target_ulong *raddr, int *flags, bool exc);
+ target_ulong *raddr, int *flags, uint64_t *tec);
int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
- target_ulong *addr, int *flags);
+ target_ulong *addr, int *flags, uint64_t *tec);
/* misc_helper.c */
diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c
index a841f7187d..4cdbbc8849 100644
--- a/target/s390x/interrupt.c
+++ b/target/s390x/interrupt.c
@@ -22,22 +22,21 @@
#endif
/* Ensure to exit the TB after this call! */
-void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
+void trigger_pgm_exception(CPUS390XState *env, uint32_t code)
{
CPUState *cs = env_cpu(env);
cs->exception_index = EXCP_PGM;
env->int_pgm_code = code;
- env->int_pgm_ilen = ilen;
+ /* env->int_pgm_ilen is already set, or will be set during unwinding */
}
-void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
- uintptr_t ra)
+void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra)
{
if (kvm_enabled()) {
kvm_s390_program_interrupt(env_archcpu(env), code);
} else if (tcg_enabled()) {
- tcg_s390_program_interrupt(env, code, ilen, ra);
+ tcg_s390_program_interrupt(env, code, ra);
} else {
g_assert_not_reached();
}
diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c
index 83c164a168..c437a1d8c6 100644
--- a/target/s390x/ioinst.c
+++ b/target/s390x/ioinst.c
@@ -44,7 +44,7 @@ void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("xsch", cssid, ssid, schid);
@@ -62,7 +62,7 @@ void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("csch", cssid, ssid, schid);
@@ -80,7 +80,7 @@ void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("hsch", cssid, ssid, schid);
@@ -116,7 +116,7 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) {
@@ -125,7 +125,7 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
}
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
!ioinst_schib_valid(&schib)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("msch", cssid, ssid, schid);
@@ -173,7 +173,7 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) {
@@ -183,7 +183,7 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
copy_orb_from_guest(&orb, &orig_orb);
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
!ioinst_orb_valid(&orb)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("ssch", cssid, ssid, schid);
@@ -205,7 +205,7 @@ void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
@@ -236,7 +236,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb,
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
@@ -247,7 +247,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb,
* access execption if it is not) first.
*/
if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
} else {
s390_cpu_virt_mem_handle_exc(cpu, ra);
}
@@ -299,13 +299,13 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
uint8_t ar;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EIO;
}
trace_ioinst_sch_id("tsch", cssid, ssid, schid);
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return -EIO;
}
@@ -613,7 +613,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
addr = env->regs[reg];
/* Page boundary? */
if (addr & 0xfff) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
/*
@@ -629,7 +629,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
len = be16_to_cpu(req->len);
/* Length field valid? */
if ((len < 16) || (len > 4088) || (len & 7)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
@@ -678,7 +678,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2,
trace_ioinst("schm");
if (SCHM_REG1_RES(reg1)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
@@ -687,7 +687,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2,
dct = SCHM_REG1_DCT(reg1);
if (update && (reg2 & 0x000000000000001f)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
@@ -700,7 +700,7 @@ void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("rsch", cssid, ssid, schid);
@@ -724,7 +724,7 @@ void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
CPUS390XState *env = &cpu->env;
if (RCHP_REG1_RES(reg1)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
@@ -747,7 +747,7 @@ void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
break;
default:
/* Invalid channel subsystem. */
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
setcc(cpu, cc);
@@ -758,6 +758,6 @@ void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra)
{
/* We do not provide address limit checking, so let's suppress it. */
if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
}
}
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 44e535856d..2325767f17 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
@@ -71,7 +72,7 @@ static inline void check_alignment(CPUS390XState *env, uint64_t v,
int wordsize, uintptr_t ra)
{
if (v % wordsize) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
}
@@ -730,7 +731,7 @@ void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
/* Bits 32-55 must contain all 0. */
if (env->regs[0] & 0xffffff00u) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
str = get_address(env, r2);
@@ -767,7 +768,7 @@ void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2)
/* Bits 32-47 of R0 must be zero. */
if (env->regs[0] & 0xffff0000u) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
str = get_address(env, r2);
@@ -846,7 +847,7 @@ uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
S390Access srca, desta;
if ((f && s) || extract64(r0, 12, 4)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
r1 = wrap_address(env, r1 & TARGET_PAGE_MASK);
@@ -879,7 +880,7 @@ uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
int i;
if (env->regs[0] & 0xffffff00ull) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
/*
@@ -911,8 +912,7 @@ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
int i;
if (a2 & 0x3) {
- /* we either came here by lam or lamy, which have different lengths */
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -932,7 +932,7 @@ void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
int i;
if (a2 & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1015,6 +1015,7 @@ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
uint64_t src = get_address(env, r2);
uint8_t pad = env->regs[r2 + 1] >> 24;
+ CPUState *cs = env_cpu(env);
S390Access srca, desta;
uint32_t cc, cur_len;
@@ -1065,7 +1066,15 @@ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
set_address_zero(env, r1, dest);
- /* TODO: Deliver interrupts. */
+ /*
+ * MVCL is interruptible. Return to the main loop if requested after
+ * writing back all state to registers. If no interrupt will get
+ * injected, we'll end up back in this handler and continue processing
+ * the remaining parts.
+ */
+ if (destlen && unlikely(cpu_loop_exit_requested(cs))) {
+ cpu_loop_exit_restore(cs, ra);
+ }
}
return cc;
}
@@ -1888,8 +1897,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
return cc;
spec_exception:
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
- g_assert_not_reached();
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)
@@ -1912,7 +1920,7 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
uint32_t i;
if (src & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1945,7 +1953,7 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
uint32_t i;
if (src & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1976,7 +1984,7 @@ void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
uint32_t i;
if (dest & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1996,7 +2004,7 @@ void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
uint32_t i;
if (dest & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -2168,7 +2176,7 @@ uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
psw_as == AS_HOME || psw_as == AS_ACCREG) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
l = wrap_length32(env, l);
@@ -2199,7 +2207,7 @@ uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
psw_as == AS_HOME || psw_as == AS_ACCREG) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
l = wrap_length32(env, l);
@@ -2226,7 +2234,7 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4)
uint16_t entries, i, index = 0;
if (r2 & 0xff000) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
if (!(r2 & 0x800)) {
@@ -2252,9 +2260,9 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4)
/* addresses are not wrapped in 24/31bit mode but table index is */
raddr = table + ((index + i) & 0x7ff) * sizeof(entry);
entry = cpu_ldq_real_ra(env, raddr, ra);
- if (!(entry & REGION_ENTRY_INV)) {
+ if (!(entry & REGION_ENTRY_I)) {
/* we are allowed to not store if already invalid */
- entry |= REGION_ENTRY_INV;
+ entry |= REGION_ENTRY_I;
cpu_stq_real_ra(env, raddr, entry, ra);
}
}
@@ -2279,17 +2287,17 @@ void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr,
/* Compute the page table entry address */
pte_addr = (pto & SEGMENT_ENTRY_ORIGIN);
- pte_addr += (vaddr & VADDR_PX) >> 9;
+ pte_addr += VADDR_PAGE_TX(vaddr) * 8;
/* Mark the page table entry as invalid */
pte = cpu_ldq_real_ra(env, pte_addr, ra);
- pte |= PAGE_INVALID;
+ pte |= PAGE_ENTRY_I;
cpu_stq_real_ra(env, pte_addr, pte, ra);
/* XXX we exploit the fact that Linux passes the exact virtual
address here - it's not obliged to! */
if (m4 & 1) {
- if (vaddr & ~VADDR_PX) {
+ if (vaddr & ~VADDR_PAGE_TX_MASK) {
tlb_flush_page(cs, page);
/* XXX 31-bit hack */
tlb_flush_page(cs, page ^ 0x80000000);
@@ -2298,7 +2306,7 @@ void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr,
tlb_flush(cs);
}
} else {
- if (vaddr & ~VADDR_PX) {
+ if (vaddr & ~VADDR_PAGE_TX_MASK) {
tlb_flush_page_all_cpus_synced(cs, page);
/* XXX 31-bit hack */
tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000);
@@ -2362,27 +2370,23 @@ void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
/* load real address */
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
{
- CPUState *cs = env_cpu(env);
- uint32_t cc = 0;
uint64_t asc = env->psw.mask & PSW_MASK_ASC;
- uint64_t ret;
- int old_exc, flags;
+ uint64_t ret, tec;
+ int flags, exc, cc;
/* XXX incomplete - has more corner cases */
if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 2, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC());
}
- old_exc = cs->exception_index;
- if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
+ exc = mmu_translate(env, addr, 0, asc, &ret, &flags, &tec);
+ if (exc) {
cc = 3;
- }
- if (cs->exception_index == EXCP_PGM) {
- ret = env->int_pgm_code | 0x80000000;
+ ret = exc | 0x80000000;
} else {
+ cc = 0;
ret |= addr & ~TARGET_PAGE_MASK;
}
- cs->exception_index = old_exc;
env->cc_op = cc;
return ret;
@@ -2539,7 +2543,7 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
__func__, dest, src, len);
if (!(env->psw.mask & PSW_MASK_DAT)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
/* OAC (operand access control) for the first operand -> dest */
@@ -2570,14 +2574,14 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
}
if (dest_a && dest_as == AS_HOME && (env->psw.mask & PSW_MASK_PSTATE)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
if (!(env->cregs[0] & CR0_SECONDARY) &&
(dest_as == AS_SECONDARY || src_as == AS_SECONDARY)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
if (!psw_key_valid(env, dest_key) || !psw_key_valid(env, src_key)) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_PRIVILEGED, ra);
}
len = wrap_length32(env, len);
@@ -2591,7 +2595,7 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
(env->psw.mask & PSW_MASK_PSTATE)) {
qemu_log_mask(LOG_UNIMP, "%s: AR-mode and PSTATE support missing\n",
__func__);
- s390_program_interrupt(env, PGM_ADDRESSING, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_ADDRESSING, ra);
}
/* FIXME: Access using correct keys and AR-mode */
diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
index 7530dcb8f3..bfb457fb63 100644
--- a/target/s390x/misc_helper.c
+++ b/target/s390x/misc_helper.c
@@ -106,7 +106,7 @@ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
int r = sclp_service_call(env, r1, r2);
qemu_mutex_unlock_iothread();
if (r < 0) {
- s390_program_interrupt(env, -r, 4, GETPC());
+ tcg_s390_program_interrupt(env, -r, GETPC());
}
return r;
}
@@ -143,7 +143,7 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
}
if (r) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
}
@@ -222,7 +222,7 @@ void HELPER(sckpf)(CPUS390XState *env, uint64_t r0)
uint32_t val = r0;
if (val & 0xffff0000) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 2, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
env->todpr = val;
}
@@ -266,7 +266,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1)
}
if ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
if ((r0 & STSI_R0_FC_MASK) == STSI_R0_FC_CURRENT) {
@@ -276,7 +276,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1)
}
if (a0 & ~TARGET_PAGE_MASK) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
/* count the cpus and split them into configured and reserved ones */
@@ -509,7 +509,7 @@ uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr)
LowCore *lowcore;
if (addr & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
qemu_mutex_lock_iothread();
@@ -573,17 +573,8 @@ void HELPER(chsc)(CPUS390XState *env, uint64_t inst)
#ifndef CONFIG_USER_ONLY
void HELPER(per_check_exception)(CPUS390XState *env)
{
- uint32_t ilen;
-
if (env->per_perc_atmid) {
- /*
- * FIXME: ILEN_AUTO is most probably the right thing to use. ilen
- * always has to match the instruction referenced in the PSW. E.g.
- * if a PER interrupt is triggered via EXECUTE, we have to use ilen
- * of EXECUTE, while per_address contains the target of EXECUTE.
- */
- ilen = get_ilen(cpu_ldub_code(env, env->per_address));
- s390_program_interrupt(env, PGM_PER, ilen, GETPC());
+ tcg_s390_program_interrupt(env, PGM_PER, GETPC());
}
}
@@ -673,7 +664,7 @@ uint32_t HELPER(stfle)(CPUS390XState *env, uint64_t addr)
int i;
if (addr & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
prepare_stfl();
@@ -746,7 +737,7 @@ void HELPER(sic)(CPUS390XState *env, uint64_t r1, uint64_t r3)
qemu_mutex_unlock_iothread();
/* css_do_sic() may actually return a PGM_xxx value to inject */
if (r) {
- s390_program_interrupt(env, -r, 4, GETPC());
+ tcg_s390_program_interrupt(env, -r, GETPC());
}
}
diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c
index 7e6b0d0508..90b81335f9 100644
--- a/target/s390x/mmu_helper.c
+++ b/target/s390x/mmu_helper.c
@@ -28,37 +28,12 @@
#include "hw/hw.h"
#include "hw/s390x/storage-keys.h"
-/* #define DEBUG_S390 */
-/* #define DEBUG_S390_PTE */
-/* #define DEBUG_S390_STDOUT */
-
-#ifdef DEBUG_S390
-#ifdef DEBUG_S390_STDOUT
-#define DPRINTF(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); \
- if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
-#endif
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
-
-#ifdef DEBUG_S390_PTE
-#define PTE_DPRINTF DPRINTF
-#else
-#define PTE_DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
-
/* Fetch/store bits in the translation exception code: */
#define FS_READ 0x800
#define FS_WRITE 0x400
static void trigger_access_exception(CPUS390XState *env, uint32_t type,
- uint32_t ilen, uint64_t tec)
+ uint64_t tec)
{
S390CPU *cpu = env_archcpu(env);
@@ -69,46 +44,8 @@ static void trigger_access_exception(CPUS390XState *env, uint32_t type,
if (type != PGM_ADDRESSING) {
stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), tec);
}
- trigger_pgm_exception(env, type, ilen);
- }
-}
-
-static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, int rw, bool exc)
-{
- uint64_t tec;
-
- tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | 4 | asc >> 46;
-
- DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec);
-
- if (!exc) {
- return;
- }
-
- trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, tec);
-}
-
-static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
- uint32_t type, uint64_t asc, int rw, bool exc)
-{
- int ilen = ILEN_AUTO;
- uint64_t tec;
-
- tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | asc >> 46;
-
- DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec);
-
- if (!exc) {
- return;
- }
-
- /* Code accesses have an undefined ilc. */
- if (rw == MMU_INST_FETCH) {
- ilen = 2;
+ trigger_pgm_exception(env, type);
}
-
- trigger_access_exception(env, type, ilen, tec);
}
/* check whether the address would be proteted by Low-Address Protection */
@@ -156,122 +93,40 @@ target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
return raddr;
}
-/* Decode page table entry (normal 4KB page) */
-static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, uint64_t pt_entry,
- target_ulong *raddr, int *flags, int rw, bool exc)
-{
- if (pt_entry & PAGE_INVALID) {
- DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, pt_entry);
- trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc);
- return -1;
- }
- if (pt_entry & PAGE_RES0) {
- trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
- return -1;
- }
- if (pt_entry & PAGE_RO) {
- *flags &= ~PAGE_WRITE;
- }
-
- *raddr = pt_entry & ASCE_ORIGIN;
-
- PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, pt_entry);
-
- return 0;
-}
-
-/* Decode segment table entry */
-static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, uint64_t st_entry,
- target_ulong *raddr, int *flags, int rw,
- bool exc)
+static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr,
+ uint64_t *entry)
{
CPUState *cs = env_cpu(env);
- uint64_t origin, offs, pt_entry;
- if (st_entry & SEGMENT_ENTRY_RO) {
- *flags &= ~PAGE_WRITE;
- }
-
- if ((st_entry & SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) {
- /* Decode EDAT1 segment frame absolute address (1MB page) */
- *raddr = (st_entry & 0xfffffffffff00000ULL) | (vaddr & 0xfffff);
- PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, st_entry);
- return 0;
- }
-
- /* Look up 4KB page entry */
- origin = st_entry & SEGMENT_ENTRY_ORIGIN;
- offs = (vaddr & VADDR_PX) >> 9;
- pt_entry = ldq_phys(cs->as, origin + offs);
- PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
- __func__, origin, offs, pt_entry);
- return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc);
-}
-
-/* Decode region table entries */
-static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, uint64_t entry, int level,
- target_ulong *raddr, int *flags, int rw,
- bool exc)
-{
- CPUState *cs = env_cpu(env);
- uint64_t origin, offs, new_entry;
- const int pchks[4] = {
- PGM_SEGMENT_TRANS, PGM_REG_THIRD_TRANS,
- PGM_REG_SEC_TRANS, PGM_REG_FIRST_TRANS
- };
-
- PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry);
-
- origin = entry & REGION_ENTRY_ORIGIN;
- offs = (vaddr >> (17 + 11 * level / 4)) & 0x3ff8;
-
- new_entry = ldq_phys(cs->as, origin + offs);
- PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
- __func__, origin, offs, new_entry);
-
- if ((new_entry & REGION_ENTRY_INV) != 0) {
- DPRINTF("%s: invalid region\n", __func__);
- trigger_page_fault(env, vaddr, pchks[level / 4], asc, rw, exc);
- return -1;
- }
-
- if ((new_entry & REGION_ENTRY_TYPE_MASK) != level) {
- trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
- return -1;
- }
-
- if (level == ASCE_TYPE_SEGMENT) {
- return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags,
- rw, exc);
- }
-
- /* Check region table offset and length */
- offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3;
- if (offs < ((new_entry & REGION_ENTRY_TF) >> 6)
- || offs > (new_entry & REGION_ENTRY_LENGTH)) {
- DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry);
- trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc);
- return -1;
- }
-
- if ((env->cregs[0] & CR0_EDAT) && (new_entry & REGION_ENTRY_RO)) {
- *flags &= ~PAGE_WRITE;
+ /*
+ * According to the PoP, these table addresses are "unpredictably real
+ * or absolute". Also, "it is unpredictable whether the address wraps
+ * or an addressing exception is recognized".
+ *
+ * We treat them as absolute addresses and don't wrap them.
+ */
+ if (unlikely(address_space_read(cs->as, gaddr, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)entry, sizeof(*entry)) !=
+ MEMTX_OK)) {
+ return false;
}
-
- /* yet another region */
- return mmu_translate_region(env, vaddr, asc, new_entry, level - 4,
- raddr, flags, rw, exc);
+ *entry = be64_to_cpu(*entry);
+ return true;
}
static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
uint64_t asc, uint64_t asce, target_ulong *raddr,
- int *flags, int rw, bool exc)
+ int *flags, int rw)
{
- int level;
- int r;
+ const bool edat1 = (env->cregs[0] & CR0_EDAT) &&
+ s390_has_feat(S390_FEAT_EDAT);
+ const bool edat2 = edat1 && s390_has_feat(S390_FEAT_EDAT_2);
+ const bool iep = (env->cregs[0] & CR0_IEP) &&
+ s390_has_feat(S390_FEAT_INSTRUCTION_EXEC_PROT);
+ const int asce_tl = asce & ASCE_TABLE_LENGTH;
+ const int asce_p = asce & ASCE_PRIVATE_SPACE;
+ hwaddr gaddr = asce & ASCE_ORIGIN;
+ uint64_t entry;
if (asce & ASCE_REAL_SPACE) {
/* direct mapping */
@@ -279,60 +134,158 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
return 0;
}
- level = asce & ASCE_TYPE_MASK;
- switch (level) {
+ switch (asce & ASCE_TYPE_MASK) {
case ASCE_TYPE_REGION1:
- if ((vaddr >> 62) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TL(vaddr) > asce_tl) {
+ return PGM_REG_FIRST_TRANS;
}
+ gaddr += VADDR_REGION1_TX(vaddr) * 8;
break;
case ASCE_TYPE_REGION2:
- if (vaddr & 0xffe0000000000000ULL) {
- DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
- " 0xffe0000000000000ULL\n", __func__, vaddr);
- trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TX(vaddr)) {
+ return PGM_ASCE_TYPE;
}
- if ((vaddr >> 51 & 3) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_REGION2_TL(vaddr) > asce_tl) {
+ return PGM_REG_SEC_TRANS;
}
+ gaddr += VADDR_REGION2_TX(vaddr) * 8;
break;
case ASCE_TYPE_REGION3:
- if (vaddr & 0xfffffc0000000000ULL) {
- DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
- " 0xfffffc0000000000ULL\n", __func__, vaddr);
- trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TX(vaddr) || VADDR_REGION2_TX(vaddr)) {
+ return PGM_ASCE_TYPE;
}
- if ((vaddr >> 40 & 3) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_REGION3_TL(vaddr) > asce_tl) {
+ return PGM_REG_THIRD_TRANS;
}
+ gaddr += VADDR_REGION3_TX(vaddr) * 8;
break;
case ASCE_TYPE_SEGMENT:
- if (vaddr & 0xffffffff80000000ULL) {
- DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
- " 0xffffffff80000000ULL\n", __func__, vaddr);
- trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TX(vaddr) || VADDR_REGION2_TX(vaddr) ||
+ VADDR_REGION3_TX(vaddr)) {
+ return PGM_ASCE_TYPE;
+ }
+ if (VADDR_SEGMENT_TL(vaddr) > asce_tl) {
+ return PGM_SEGMENT_TRANS;
+ }
+ gaddr += VADDR_SEGMENT_TX(vaddr) * 8;
+ break;
+ }
+
+ switch (asce & ASCE_TYPE_MASK) {
+ case ASCE_TYPE_REGION1:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & REGION_ENTRY_I) {
+ return PGM_REG_FIRST_TRANS;
+ }
+ if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION1) {
+ return PGM_TRANS_SPEC;
+ }
+ if (VADDR_REGION2_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 ||
+ VADDR_REGION2_TL(vaddr) > (entry & REGION_ENTRY_TL)) {
+ return PGM_REG_SEC_TRANS;
+ }
+ if (edat1 && (entry & REGION_ENTRY_P)) {
+ *flags &= ~PAGE_WRITE;
+ }
+ gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_REGION2_TX(vaddr) * 8;
+ /* fall through */
+ case ASCE_TYPE_REGION2:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & REGION_ENTRY_I) {
+ return PGM_REG_SEC_TRANS;
+ }
+ if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION2) {
+ return PGM_TRANS_SPEC;
+ }
+ if (VADDR_REGION3_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 ||
+ VADDR_REGION3_TL(vaddr) > (entry & REGION_ENTRY_TL)) {
+ return PGM_REG_THIRD_TRANS;
+ }
+ if (edat1 && (entry & REGION_ENTRY_P)) {
+ *flags &= ~PAGE_WRITE;
+ }
+ gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_REGION3_TX(vaddr) * 8;
+ /* fall through */
+ case ASCE_TYPE_REGION3:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & REGION_ENTRY_I) {
+ return PGM_REG_THIRD_TRANS;
+ }
+ if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION3) {
+ return PGM_TRANS_SPEC;
+ }
+ if (edat2 && (entry & REGION3_ENTRY_CR) && asce_p) {
+ return PGM_TRANS_SPEC;
+ }
+ if (edat1 && (entry & REGION_ENTRY_P)) {
+ *flags &= ~PAGE_WRITE;
+ }
+ if (edat2 && (entry & REGION3_ENTRY_FC)) {
+ if (iep && (entry & REGION3_ENTRY_IEP)) {
+ *flags &= ~PAGE_EXEC;
+ }
+ *raddr = (entry & REGION3_ENTRY_RFAA) |
+ (vaddr & ~REGION3_ENTRY_RFAA);
+ return 0;
+ }
+ if (VADDR_SEGMENT_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 ||
+ VADDR_SEGMENT_TL(vaddr) > (entry & REGION_ENTRY_TL)) {
+ return PGM_SEGMENT_TRANS;
+ }
+ gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_SEGMENT_TX(vaddr) * 8;
+ /* fall through */
+ case ASCE_TYPE_SEGMENT:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & SEGMENT_ENTRY_I) {
+ return PGM_SEGMENT_TRANS;
+ }
+ if ((entry & SEGMENT_ENTRY_TT) != SEGMENT_ENTRY_TT_SEGMENT) {
+ return PGM_TRANS_SPEC;
+ }
+ if ((entry & SEGMENT_ENTRY_CS) && asce_p) {
+ return PGM_TRANS_SPEC;
}
- if ((vaddr >> 29 & 3) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc);
- return -1;
+ if (entry & SEGMENT_ENTRY_P) {
+ *flags &= ~PAGE_WRITE;
+ }
+ if (edat1 && (entry & SEGMENT_ENTRY_FC)) {
+ if (iep && (entry & SEGMENT_ENTRY_IEP)) {
+ *flags &= ~PAGE_EXEC;
+ }
+ *raddr = (entry & SEGMENT_ENTRY_SFAA) |
+ (vaddr & ~SEGMENT_ENTRY_SFAA);
+ return 0;
}
+ gaddr = (entry & SEGMENT_ENTRY_ORIGIN) + VADDR_PAGE_TX(vaddr) * 8;
break;
}
- r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw,
- exc);
- if (!r && rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE)) {
- trigger_prot_fault(env, vaddr, asc, rw, exc);
- return -1;
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & PAGE_ENTRY_I) {
+ return PGM_PAGE_TRANS;
+ }
+ if (entry & PAGE_ENTRY_0) {
+ return PGM_TRANS_SPEC;
+ }
+ if (entry & PAGE_ENTRY_P) {
+ *flags &= ~PAGE_WRITE;
+ }
+ if (iep && (entry & PAGE_ENTRY_IEP)) {
+ *flags &= ~PAGE_EXEC;
}
- return r;
+ *raddr = entry & TARGET_PAGE_MASK;
+ return 0;
}
static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
@@ -412,16 +365,18 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
* @param raddr the translated address is stored to this pointer
* @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer
* @param exc true = inject a program check if a fault occurred
- * @return 0 if the translation was successful, -1 if a fault occurred
+ * @return 0 = success, != 0, the exception to raise
*/
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
- target_ulong *raddr, int *flags, bool exc)
+ target_ulong *raddr, int *flags, uint64_t *tec)
{
uint64_t asce;
int r;
-
+ *tec = (vaddr & TARGET_PAGE_MASK) | (asc >> 46) |
+ (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ);
*flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+
if (is_low_address(vaddr & TARGET_PAGE_MASK) && lowprot_enabled(env, asc)) {
/*
* If any part of this page is currently protected, make sure the
@@ -433,10 +388,9 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
*/
*flags |= PAGE_WRITE_INV;
if (is_low_address(vaddr) && rw == MMU_DATA_STORE) {
- if (exc) {
- trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, 0);
- }
- return -EACCES;
+ /* LAP sets bit 56 */
+ *tec |= 0x80;
+ return PGM_PROTECTION;
}
}
@@ -449,15 +403,12 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
switch (asc) {
case PSW_ASC_PRIMARY:
- PTE_DPRINTF("%s: asc=primary\n", __func__);
asce = env->cregs[1];
break;
case PSW_ASC_HOME:
- PTE_DPRINTF("%s: asc=home\n", __func__);
asce = env->cregs[13];
break;
case PSW_ASC_SECONDARY:
- PTE_DPRINTF("%s: asc=secondary\n", __func__);
asce = env->cregs[7];
break;
case PSW_ASC_ACCREG:
@@ -467,11 +418,25 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
}
/* perform the DAT translation */
- r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw, exc);
- if (r) {
+ r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw);
+ if (unlikely(r)) {
return r;
}
+ /* check for DAT protection */
+ if (unlikely(rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE))) {
+ /* DAT sets bit 61 only */
+ *tec |= 0x4;
+ return PGM_PROTECTION;
+ }
+
+ /* check for Instruction-Execution-Protection */
+ if (unlikely(rw == MMU_INST_FETCH && !(*flags & PAGE_EXEC))) {
+ /* IEP sets bit 56 and 61 */
+ *tec |= 0x84;
+ return PGM_PROTECTION;
+ }
+
nodat:
/* Convert real address -> absolute address */
*raddr = mmu_real2abs(env, *raddr);
@@ -486,22 +451,22 @@ nodat:
* the MEMOP interface.
*/
static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages,
- target_ulong *pages, bool is_write)
+ target_ulong *pages, bool is_write, uint64_t *tec)
{
uint64_t asc = cpu->env.psw.mask & PSW_MASK_ASC;
CPUS390XState *env = &cpu->env;
int ret, i, pflags;
for (i = 0; i < nr_pages; i++) {
- ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, true);
+ ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, tec);
if (ret) {
return ret;
}
if (!address_space_access_valid(&address_space_memory, pages[i],
TARGET_PAGE_SIZE, is_write,
MEMTXATTRS_UNSPECIFIED)) {
- trigger_access_exception(env, PGM_ADDRESSING, ILEN_AUTO, 0);
- return -EFAULT;
+ *tec = 0; /* unused */
+ return PGM_ADDRESSING;
}
addr += TARGET_PAGE_SIZE;
}
@@ -529,6 +494,7 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
{
int currlen, nr_pages, i;
target_ulong *pages;
+ uint64_t tec;
int ret;
if (kvm_enabled()) {
@@ -542,8 +508,10 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
+ 1;
pages = g_malloc(nr_pages * sizeof(*pages));
- ret = translate_pages(cpu, laddr, nr_pages, pages, is_write);
- if (ret == 0 && hostbuf != NULL) {
+ ret = translate_pages(cpu, laddr, nr_pages, pages, is_write, &tec);
+ if (ret) {
+ trigger_access_exception(&cpu->env, ret, tec);
+ } else if (hostbuf != NULL) {
/* Copy data by stepping through the area page by page */
for (i = 0; i < nr_pages; i++) {
currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE));
@@ -575,10 +543,10 @@ void s390_cpu_virt_mem_handle_exc(S390CPU *cpu, uintptr_t ra)
* @param rw 0 = read, 1 = write, 2 = code fetch
* @param addr the translated address is stored to this pointer
* @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer
- * @return 0 if the translation was successful, < 0 if a fault occurred
+ * @return 0 = success, != 0, the exception to raise
*/
int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
- target_ulong *addr, int *flags)
+ target_ulong *addr, int *flags, uint64_t *tec)
{
const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT;
@@ -587,8 +555,11 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
/* see comment in mmu_translate() how this works */
*flags |= PAGE_WRITE_INV;
if (is_low_address(raddr) && rw == MMU_DATA_STORE) {
- trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, 0);
- return -EACCES;
+ /* LAP sets bit 56 */
+ *tec = (raddr & TARGET_PAGE_MASK)
+ | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ)
+ | 0x80;
+ return PGM_PROTECTION;
}
}
diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c
index 32adb7276a..d22c898802 100644
--- a/target/s390x/tcg-stub.c
+++ b/target/s390x/tcg-stub.c
@@ -18,8 +18,8 @@
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque)
{
}
-void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
- int ilen, uintptr_t ra)
+void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env,
+ uint32_t code, uintptr_t ra)
{
g_assert_not_reached();
}
diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h
index 2813f9d48e..2f54ccb027 100644
--- a/target/s390x/tcg_s390x.h
+++ b/target/s390x/tcg_s390x.h
@@ -14,8 +14,8 @@
#define TCG_S390X_H
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque);
-void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
- int ilen, uintptr_t ra);
+void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env,
+ uint32_t code, uintptr_t ra);
void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc,
uintptr_t ra);
void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc,
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index a3e43ff9ec..151dfa91fb 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -6309,6 +6309,9 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
/* Search for the insn in the table. */
insn = extract_insn(env, s, &f);
+ /* Emit insn_start now that we know the ILEN. */
+ tcg_gen_insn_start(s->base.pc_next, s->cc_op, s->ilen);
+
/* Not found means unimplemented/illegal opcode. */
if (insn == NULL) {
qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n",
@@ -6463,9 +6466,6 @@ static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs)
static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
{
- DisasContext *dc = container_of(dcbase, DisasContext, base);
-
- tcg_gen_insn_start(dc->base.pc_next, dc->cc_op);
}
static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
@@ -6473,6 +6473,14 @@ static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
+ /*
+ * Emit an insn_start to accompany the breakpoint exception.
+ * The ILEN value is a dummy, since this does not result in
+ * an s390x exception, but an internal qemu exception which
+ * brings us back to interact with the gdbstub.
+ */
+ tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 2);
+
dc->base.is_jmp = DISAS_PC_STALE;
dc->do_debug = true;
/* The address covered by the breakpoint must be included in
@@ -6567,8 +6575,14 @@ void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb,
target_ulong *data)
{
int cc_op = data[1];
+
env->psw.addr = data[0];
+
+ /* Update the CC opcode if it is not already up-to-date. */
if ((cc_op != CC_OP_DYNAMIC) && (cc_op != CC_OP_STATIC)) {
env->cc_op = cc_op;
}
+
+ /* Record ILEN. */
+ env->int_pgm_ilen = data[2];
}
diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026
index ffb18ab6b5..3430029ed6 100755
--- a/tests/qemu-iotests/026
+++ b/tests/qemu-iotests/026
@@ -107,7 +107,7 @@ if [ "$event" == "l2_load" ]; then
$QEMU_IO -c "read $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io
fi
-_check_test_img 2>&1 | grep -v "refcount=1 reference=0"
+_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
done
done
@@ -152,7 +152,7 @@ echo
echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate"
$QEMU_IO -c "write $vmstate 0 64M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
-_check_test_img 2>&1 | grep -v "refcount=1 reference=0"
+_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
done
done
@@ -191,7 +191,7 @@ echo
echo "Event: $event; errno: $errno; imm: $imm; once: $once"
$QEMU_IO -c "write -b 0 64k" "$BLKDBG_TEST_IMG" | _filter_qemu_io
-_check_test_img 2>&1 | grep -v "refcount=1 reference=0"
+_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
done
done
diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
index fb89b8480c..ff0817b6f2 100644
--- a/tests/qemu-iotests/026.out
+++ b/tests/qemu-iotests/026.out
@@ -17,18 +17,14 @@ Event: l1_update; errno: 5; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write
@@ -45,18 +41,14 @@ Event: l1_update; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_load; errno: 5; imm: off; once: on; write
@@ -137,18 +129,14 @@ Event: l2_update; errno: 5; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: on; write
@@ -165,18 +153,14 @@ Event: l2_update; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 5; imm: off; once: on; write
@@ -200,9 +184,7 @@ Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 28; imm: off; once: on; write
@@ -226,9 +208,7 @@ Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: write_aio; errno: 5; imm: off; once: on; write
@@ -480,18 +460,14 @@ Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-55 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-251 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write; errno: 28; imm: off; once: on; write
@@ -532,18 +508,14 @@ Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write
@@ -560,18 +532,14 @@ Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write
@@ -588,18 +556,14 @@ Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== L1 growth tests ===
@@ -658,9 +622,7 @@ Event: l1_grow_activate_table; errno: 5; imm: off; once: off
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_grow_activate_table; errno: 28; imm: off; once: on
@@ -672,9 +634,7 @@ Event: l1_grow_activate_table; errno: 28; imm: off; once: off
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== Avoid cluster leaks after temporary failure ===
diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache
index 6dda95dfb4..495d013007 100644
--- a/tests/qemu-iotests/026.out.nocache
+++ b/tests/qemu-iotests/026.out.nocache
@@ -17,18 +17,14 @@ Event: l1_update; errno: 5; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write
@@ -45,18 +41,14 @@ Event: l1_update; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_load; errno: 5; imm: off; once: on; write
@@ -140,9 +132,7 @@ qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: off; write -b
@@ -150,9 +140,7 @@ qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: on; write
@@ -172,9 +160,7 @@ qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: off; write -b
@@ -182,9 +168,7 @@ qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 5; imm: off; once: on; write
@@ -208,9 +192,7 @@ Event: l2_alloc_write; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 28; imm: off; once: on; write
@@ -234,9 +216,7 @@ Event: l2_alloc_write; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: write_aio; errno: 5; imm: off; once: on; write
@@ -488,18 +468,14 @@ Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-55 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-251 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write; errno: 28; imm: off; once: on; write
@@ -540,18 +516,14 @@ Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write
@@ -568,18 +540,14 @@ Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write
@@ -596,18 +564,14 @@ Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== L1 growth tests ===
@@ -666,9 +630,7 @@ Event: l1_grow_activate_table; errno: 5; imm: off; once: off
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_grow_activate_table; errno: 28; imm: off; once: on
@@ -680,9 +642,7 @@ Event: l1_grow_activate_table; errno: 28; imm: off; once: off
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== Avoid cluster leaks after temporary failure ===
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
index 98c55d8e5a..f39287c162 100755
--- a/tests/qemu-iotests/056
+++ b/tests/qemu-iotests/056
@@ -133,6 +133,7 @@ class BackupTest(iotests.QMPTestCase):
self.vm = iotests.VM()
self.test_img = img_create('test')
self.dest_img = img_create('dest')
+ self.dest_img2 = img_create('dest2')
self.ref_img = img_create('ref')
self.vm.add_drive(self.test_img)
self.vm.launch()
@@ -141,6 +142,7 @@ class BackupTest(iotests.QMPTestCase):
self.vm.shutdown()
try_remove(self.test_img)
try_remove(self.dest_img)
+ try_remove(self.dest_img2)
try_remove(self.ref_img)
def hmp_io_writes(self, drive, patterns):
@@ -253,9 +255,9 @@ class BackupTest(iotests.QMPTestCase):
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'concluded')
# Leave zombie job un-dismissed, observe a failure:
- res = self.qmp_backup_and_wait(serror="Node 'drive0' is busy: block device is in use by block job: backup",
+ res = self.qmp_backup_and_wait(serror="Job ID 'drive0' already in use",
device='drive0', format=iotests.imgfmt,
- sync='full', target=self.dest_img,
+ sync='full', target=self.dest_img2,
auto_dismiss=False)
self.assertEqual(res, False)
# OK, dismiss the zombie.
@@ -265,7 +267,7 @@ class BackupTest(iotests.QMPTestCase):
self.assert_qmp(res, 'return', [])
# Ensure it's really gone.
self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
- sync='full', target=self.dest_img,
+ sync='full', target=self.dest_img2,
auto_dismiss=False)
def dismissal_failure(self, dismissal_opt):
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index ca40ba3be2..d3e851e1ae 100755
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -105,7 +105,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase):
# Create a base image with a distinctive patterning
drive0 = self.add_node('drive0')
self.img_create(drive0['file'], drive0['fmt'])
- self.vm.add_drive(drive0['file'])
+ self.vm.add_drive(drive0['file'], opts='node-name=node0')
self.write_default_pattern(drive0['file'])
self.vm.launch()
@@ -348,12 +348,14 @@ class TestIncrementalBackup(TestIncrementalBackupBase):
('0xfe', '16M', '256k'),
('0x64', '32736k', '64k')))
# Check the dirty bitmap stats
- result = self.vm.qmp('query-block')
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/name', 'bitmap0')
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/count', 458752)
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/granularity', 65536)
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/status', 'active')
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/persistent', False)
+ self.assertTrue(self.vm.check_bitmap_status(
+ 'node0', bitmap0.name, {
+ 'name': 'bitmap0',
+ 'count': 458752,
+ 'granularity': 65536,
+ 'status': 'active',
+ 'persistent': False
+ }))
# Prepare a cluster_size=128k backup target without a backing file.
(target, _) = bitmap0.new_target()
@@ -670,9 +672,8 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
"""
drive0 = self.drives[0]
- # NB: The blkdebug script here looks for a "flush, read, read" pattern.
- # The flush occurs in hmp_io_writes, the first read in device_add, and
- # the last read during the block job.
+ # NB: The blkdebug script here looks for a "flush, read" pattern.
+ # The flush occurs in hmp_io_writes, and the read during the block job.
result = self.vm.qmp('blockdev-add',
node_name=drive0['id'],
driver=drive0['fmt'],
@@ -686,15 +687,11 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
'event': 'flush_to_disk',
'state': 1,
'new_state': 2
- },{
- 'event': 'read_aio',
- 'state': 2,
- 'new_state': 3
}],
'inject-error': [{
'event': 'read_aio',
'errno': 5,
- 'state': 3,
+ 'state': 2,
'immediately': False,
'once': True
}],
@@ -708,23 +705,15 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
('0xfe', '16M', '256k'),
('0x64', '32736k', '64k')))
- # For the purposes of query-block visibility of bitmaps, add a drive
- # frontend after we've written data; otherwise we can't use hmp-io
- result = self.vm.qmp("device_add",
- id="device0",
- drive=drive0['id'],
- driver="virtio-blk")
- self.assert_qmp(result, 'return', {})
-
# Bitmap Status Check
- query = self.vm.qmp('query-block')
- ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
- if bmap.get('name') == bitmap.name][0]
- self.assert_qmp(ret, 'count', 458752)
- self.assert_qmp(ret, 'granularity', 65536)
- self.assert_qmp(ret, 'status', 'active')
- self.assert_qmp(ret, 'busy', False)
- self.assert_qmp(ret, 'recording', True)
+ self.assertTrue(self.vm.check_bitmap_status(
+ drive0['id'], bitmap.name, {
+ 'count': 458752,
+ 'granularity': 65536,
+ 'status': 'active',
+ 'busy': False,
+ 'recording': True
+ }))
# Start backup
parent, _ = bitmap.last_target()
@@ -748,14 +737,14 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
'operation': 'read'})
# Bitmap Status Check
- query = self.vm.qmp('query-block')
- ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
- if bmap.get('name') == bitmap.name][0]
- self.assert_qmp(ret, 'count', 458752)
- self.assert_qmp(ret, 'granularity', 65536)
- self.assert_qmp(ret, 'status', 'frozen')
- self.assert_qmp(ret, 'busy', True)
- self.assert_qmp(ret, 'recording', True)
+ self.assertTrue(self.vm.check_bitmap_status(
+ drive0['id'], bitmap.name, {
+ 'count': 458752,
+ 'granularity': 65536,
+ 'status': 'frozen',
+ 'busy': True,
+ 'recording': True
+ }))
# Resume and check incremental backup for consistency
res = self.vm.qmp('block-job-resume', device=bitmap.drive['id'])
@@ -763,14 +752,14 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
self.wait_qmp_backup(bitmap.drive['id'])
# Bitmap Status Check
- query = self.vm.qmp('query-block')
- ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
- if bmap.get('name') == bitmap.name][0]
- self.assert_qmp(ret, 'count', 0)
- self.assert_qmp(ret, 'granularity', 65536)
- self.assert_qmp(ret, 'status', 'active')
- self.assert_qmp(ret, 'busy', False)
- self.assert_qmp(ret, 'recording', True)
+ self.assertTrue(self.vm.check_bitmap_status(
+ drive0['id'], bitmap.name, {
+ 'count': 0,
+ 'granularity': 65536,
+ 'status': 'active',
+ 'busy': False,
+ 'recording': True
+ }))
# Finalize / Cleanup
self.make_reference_backup(bitmap)
diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125
index dc4b8f5fb9..4e31aa4e5f 100755
--- a/tests/qemu-iotests/125
+++ b/tests/qemu-iotests/125
@@ -34,8 +34,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
get_image_size_on_host()
{
- $QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "disk size" \
- | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/'
+ echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE")))
}
# get standard environment and filters
@@ -49,6 +48,46 @@ if [ -z "$TEST_IMG_FILE" ]; then
TEST_IMG_FILE=$TEST_IMG
fi
+# Test whether we are running on a broken XFS version. There is this
+# bug:
+
+# $ rm -f foo
+# $ touch foo
+# $ block_size=4096 # Your FS's block size
+# $ fallocate -o $((block_size / 2)) -l $block_size foo
+# $ LANG=C xfs_bmap foo | grep hole
+# 1: [8..15]: hole
+#
+# The problem is that the XFS driver rounds down the offset and
+# rounds up the length to the block size, but independently. As
+# such, it only allocates the first block in the example above,
+# even though it should allocate the first two blocks (because our
+# request is to fallocate something that touches both the first
+# two blocks).
+#
+# This means that when you then write to the beginning of the
+# second block, the disk usage of the first two blocks grows.
+#
+# That is precisely what fallocate() promises, though: That when you
+# write to an area that you have fallocated, no new blocks will have
+# to be allocated.
+
+touch "$TEST_IMG_FILE"
+# Assuming there is no FS with a block size greater than 64k
+fallocate -o 65535 -l 2 "$TEST_IMG_FILE"
+len0=$(get_image_size_on_host)
+
+# Write to something that in theory we have just fallocated
+# (Thus, the on-disk size should not increase)
+poke_file "$TEST_IMG_FILE" 65536 42
+len1=$(get_image_size_on_host)
+
+if [ $len1 -gt $len0 ]; then
+ _notrun "the test filesystem's fallocate() is broken"
+fi
+
+rm -f "$TEST_IMG_FILE"
+
# Generally, we create some image with or without existing preallocation and
# then resize it. Then we write some data into the image and verify that its
# size does not change if we have used preallocation.
@@ -111,7 +150,7 @@ for GROWTH_SIZE in 16 48 80; do
if [ $file_length_2 -gt $file_length_1 ]; then
echo "ERROR (grow): Image length has grown from $file_length_1 to $file_length_2"
fi
- if [ $create_mode != metadata ]; then
+ if [ $growth_mode != metadata ]; then
# The host size should not have grown either
if [ $host_size_2 -gt $host_size_1 ]; then
echo "ERROR (grow): Host size has grown from $host_size_1 to $host_size_2"
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
index dbd3bdef6c..e3b578282d 100644
--- a/tests/qemu-iotests/141.out
+++ b/tests/qemu-iotests/141.out
@@ -10,7 +10,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}}
diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149
index 4f363f295f..8ab42e94c6 100755
--- a/tests/qemu-iotests/149
+++ b/tests/qemu-iotests/149
@@ -153,7 +153,7 @@ def cryptsetup_format(config):
(password, slot) = config.first_password()
- args = ["luksFormat"]
+ args = ["luksFormat", "--type", "luks1"]
cipher = config.cipher + "-" + config.mode + "-" + config.ivgen
if config.ivgen_hash is not None:
cipher = cipher + ":" + config.ivgen_hash
diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out
index 1407ce6dad..6877ab6c4a 100644
--- a/tests/qemu-iotests/149.out
+++ b/tests/qemu-iotests/149.out
@@ -2,7 +2,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1
# Write test pattern 0xa7
@@ -122,7 +122,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha1.img
# Create image
truncate TEST_DIR/luks-twofish-256-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1
# Write test pattern 0xa7
@@ -242,7 +242,7 @@ unlink TEST_DIR/luks-twofish-256-xts-plain64-sha1.img
# Create image
truncate TEST_DIR/luks-serpent-256-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1
# Write test pattern 0xa7
@@ -362,7 +362,7 @@ unlink TEST_DIR/luks-serpent-256-xts-plain64-sha1.img
# Create image
truncate TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1
# Write test pattern 0xa7
@@ -483,7 +483,7 @@ Skipping cast6-256-xts-plain64-sha1 in blacklist
# Create image
truncate TEST_DIR/luks-aes-256-cbc-plain-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1
# Write test pattern 0xa7
@@ -603,7 +603,7 @@ unlink TEST_DIR/luks-aes-256-cbc-plain-sha1.img
# Create image
truncate TEST_DIR/luks-aes-256-cbc-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1
# Write test pattern 0xa7
@@ -723,7 +723,7 @@ unlink TEST_DIR/luks-aes-256-cbc-plain64-sha1.img
# Create image
truncate TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1
# Write test pattern 0xa7
@@ -843,7 +843,7 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1
# Write test pattern 0xa7
@@ -963,7 +963,7 @@ unlink TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img
# Create image
truncate TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1
# Write test pattern 0xa7
@@ -1083,7 +1083,7 @@ unlink TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img
# Create image
truncate TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1
# Write test pattern 0xa7
@@ -1203,7 +1203,7 @@ unlink TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img
# Create image
truncate TEST_DIR/luks-twofish-128-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1
# Write test pattern 0xa7
@@ -1324,7 +1324,7 @@ Skipping twofish-192-xts-plain64-sha1 in blacklist
# Create image
truncate TEST_DIR/luks-serpent-128-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1
# Write test pattern 0xa7
@@ -1444,7 +1444,7 @@ unlink TEST_DIR/luks-serpent-128-xts-plain64-sha1.img
# Create image
truncate TEST_DIR/luks-serpent-192-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1
# Write test pattern 0xa7
@@ -1566,7 +1566,7 @@ Skipping cast6-192-xts-plain64-sha1 in blacklist
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha224.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha224 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha224.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha224 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha224.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224
# Write test pattern 0xa7
@@ -1686,7 +1686,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha224.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha256.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha256.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha256.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256
# Write test pattern 0xa7
@@ -1806,7 +1806,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha256.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha384.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha384 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha384.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha384 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha384.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384
# Write test pattern 0xa7
@@ -1926,7 +1926,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha384.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha512.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha512 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha512.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha512.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512
# Write test pattern 0xa7
@@ -2046,7 +2046,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha512.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash ripemd160 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash ripemd160 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160
# Write test pattern 0xa7
@@ -2166,7 +2166,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3
# Write test pattern 0xa7
@@ -2226,7 +2226,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img
# Add password slot 1
sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 1 --key-file - --iter-time 10 TEST_DIR/passwd.txt
# Add password slot 2
@@ -2360,7 +2360,7 @@ unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img
# Create image
truncate TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1
# Write test pattern 0xa7
@@ -2480,7 +2480,7 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img
# Create image
truncate TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1
# Write test pattern 0xa7
diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162
index 2d719afbed..c0053ed975 100755
--- a/tests/qemu-iotests/162
+++ b/tests/qemu-iotests/162
@@ -46,7 +46,7 @@ echo '=== NBD ==='
# NBD expects all of its arguments to be strings
# So this should not crash
-$QEMU_IMG info 'json:{"driver": "nbd", "host": 42}'
+$QEMU_IMG info 'json:{"driver": "nbd", "host": -1}'
# And this should not treat @port as if it had not been specified
# (We need to set up a server here, because the error message for "Connection
diff --git a/tests/qemu-iotests/162.out b/tests/qemu-iotests/162.out
index 3c5be2c569..5a00d36d17 100644
--- a/tests/qemu-iotests/162.out
+++ b/tests/qemu-iotests/162.out
@@ -1,7 +1,7 @@
QA output created by 162
=== NBD ===
-qemu-img: Could not open 'json:{"driver": "nbd", "host": 42}': Failed to connect socket: Invalid argument
+qemu-img: Could not open 'json:{"driver": "nbd", "host": -1}': address resolution failed for -1:10809: Name or service not known
image: nbd://localhost:PORT
image: nbd+unix://?socket=42
diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out
index 3dd3ca5708..9c09ee3917 100644
--- a/tests/qemu-iotests/227.out
+++ b/tests/qemu-iotests/227.out
@@ -15,6 +15,8 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio
{
"device": "virtio0",
"stats": {
+ "unmap_operations": 0,
+ "unmap_merged": 0,
"flush_total_time_ns": 0,
"wr_highest_offset": 0,
"wr_total_time_ns": 0,
@@ -24,13 +26,17 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio
"wr_bytes": 0,
"timed_stats": [
],
+ "failed_unmap_operations": 0,
"failed_flush_operations": 0,
"account_invalid": true,
"rd_total_time_ns": 0,
+ "invalid_unmap_operations": 0,
"flush_operations": 0,
"wr_operations": 0,
+ "unmap_bytes": 0,
"rd_merged": 0,
"rd_bytes": 0,
+ "unmap_total_time_ns": 0,
"invalid_flush_operations": 0,
"account_failed": true,
"rd_operations": 0,
@@ -74,6 +80,8 @@ Testing: -drive driver=null-co,if=none
{
"device": "none0",
"stats": {
+ "unmap_operations": 0,
+ "unmap_merged": 0,
"flush_total_time_ns": 0,
"wr_highest_offset": 0,
"wr_total_time_ns": 0,
@@ -83,13 +91,17 @@ Testing: -drive driver=null-co,if=none
"wr_bytes": 0,
"timed_stats": [
],
+ "failed_unmap_operations": 0,
"failed_flush_operations": 0,
"account_invalid": true,
"rd_total_time_ns": 0,
+ "invalid_unmap_operations": 0,
"flush_operations": 0,
"wr_operations": 0,
+ "unmap_bytes": 0,
"rd_merged": 0,
"rd_bytes": 0,
+ "unmap_total_time_ns": 0,
"invalid_flush_operations": 0,
"account_failed": true,
"rd_operations": 0,
@@ -163,6 +175,8 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b
{
"device": "",
"stats": {
+ "unmap_operations": 0,
+ "unmap_merged": 0,
"flush_total_time_ns": 0,
"wr_highest_offset": 0,
"wr_total_time_ns": 0,
@@ -172,13 +186,17 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b
"wr_bytes": 0,
"timed_stats": [
],
+ "failed_unmap_operations": 0,
"failed_flush_operations": 0,
"account_invalid": false,
"rd_total_time_ns": 0,
+ "invalid_unmap_operations": 0,
"flush_operations": 0,
"wr_operations": 0,
+ "unmap_bytes": 0,
"rd_merged": 0,
"rd_bytes": 0,
+ "unmap_total_time_ns": 0,
"invalid_flush_operations": 0,
"account_failed": false,
"rd_operations": 0,
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index 4a636d8ab2..a9828251cf 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -148,11 +148,6 @@ class Drive:
self.fmt = None
self.size = None
self.node = None
- self.device = None
-
- @property
- def name(self):
- return self.node or self.device
def img_create(self, fmt, size):
self.fmt = fmt
@@ -188,25 +183,6 @@ class Drive:
self.size = size
self.node = name
-def query_bitmaps(vm):
- res = vm.qmp("query-block")
- return {"bitmaps": {device['device'] or device['qdev']:
- device.get('dirty-bitmaps', []) for
- device in res['return']}}
-
-def get_bitmap(bitmaps, drivename, name, recording=None):
- """
- get a specific bitmap from the object returned by query_bitmaps.
- :param recording: If specified, filter results by the specified value.
- """
- for bitmap in bitmaps['bitmaps'][drivename]:
- if bitmap.get('name', '') == name:
- if recording is None:
- return bitmap
- elif bitmap.get('recording') == recording:
- return bitmap
- return None
-
def blockdev_backup(vm, device, target, sync, **kwargs):
# Strip any arguments explicitly nulled by the caller:
kwargs = {key: val for key, val in kwargs.items() if val is not None}
@@ -214,13 +190,14 @@ def blockdev_backup(vm, device, target, sync, **kwargs):
device=device,
target=target,
sync=sync,
+ filter_node_name='backup-top',
**kwargs)
return result
def blockdev_backup_mktarget(drive, target_id, filepath, sync, **kwargs):
target_drive = Drive(filepath, vm=drive.vm)
target_drive.create_target(target_id, drive.fmt, drive.size)
- blockdev_backup(drive.vm, drive.name, target_id, sync, **kwargs)
+ blockdev_backup(drive.vm, drive.node, target_id, sync, **kwargs)
def reference_backup(drive, n, filepath):
log("--- Reference Backup #{:d} ---\n".format(n))
@@ -240,7 +217,7 @@ def backup(drive, n, filepath, sync, **kwargs):
job_id=job_id, **kwargs)
return job_id
-def perform_writes(drive, n):
+def perform_writes(drive, n, filter_node_name=None):
log("--- Write #{:d} ---\n".format(n))
for pattern in GROUPS[n].patterns:
cmd = "write -P{:s} 0x{:07x} 0x{:x}".format(
@@ -248,9 +225,9 @@ def perform_writes(drive, n):
pattern.offset,
pattern.size)
log(cmd)
- log(drive.vm.hmp_qemu_io(drive.name, cmd))
- bitmaps = query_bitmaps(drive.vm)
- log(bitmaps, indent=2)
+ log(drive.vm.hmp_qemu_io(filter_node_name or drive.node, cmd))
+ bitmaps = drive.vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
log('')
return bitmaps
@@ -343,26 +320,19 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
}]
}
+ drive0.node = 'drive0'
vm.qmp_log('blockdev-add',
filters=[iotests.filter_qmp_testfiles],
- node_name="drive0",
+ node_name=drive0.node,
driver=drive0.fmt,
file=file_config)
- drive0.node = 'drive0'
- drive0.device = 'device0'
- # Use share-rw to allow writes directly to the node;
- # The anonymous block-backend for this configuration prevents us
- # from using HMP's qemu-io commands to address the device.
- vm.qmp_log("device_add", id=drive0.device,
- drive=drive0.name, driver="scsi-hd",
- share_rw=True)
log('')
# 0 - Writes and Reference Backup
perform_writes(drive0, 0)
reference_backup(drive0, 0, fbackup0)
log('--- Add Bitmap ---\n')
- vm.qmp_log("block-dirty-bitmap-add", node=drive0.name,
+ vm.qmp_log("block-dirty-bitmap-add", node=drive0.node,
name="bitmap0", granularity=GRANULARITY)
log('')
ebitmap = EmulatedBitmap()
@@ -370,14 +340,14 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
# 1 - Writes and Reference Backup
bitmaps = perform_writes(drive0, 1)
ebitmap.dirty_group(1)
- bitmap = get_bitmap(bitmaps, drive0.device, 'bitmap0')
+ bitmap = vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps)
ebitmap.compare(bitmap)
reference_backup(drive0, 1, fbackup1)
# 1 - Test Backup (w/ Optional induced failure)
if failure == 'intermediate':
# Activate blkdebug induced failure for second-to-next read
- log(vm.hmp_qemu_io(drive0.name, 'flush'))
+ log(vm.hmp_qemu_io(drive0.node, 'flush'))
log('')
job = backup(drive0, 1, bsync1, msync_mode,
bitmap="bitmap0", bitmap_mode=bsync_mode)
@@ -386,14 +356,15 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
"""Issue writes while the job is open to test bitmap divergence."""
# Note: when `failure` is 'intermediate', this isn't called.
log('')
- bitmaps = perform_writes(drive0, 2)
+ bitmaps = perform_writes(drive0, 2, filter_node_name='backup-top')
# Named bitmap (static, should be unchanged)
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0',
+ bitmaps=bitmaps))
# Anonymous bitmap (dynamic, shows new writes)
anonymous = EmulatedBitmap()
anonymous.dirty_group(2)
- anonymous.compare(get_bitmap(bitmaps, drive0.device, '',
- recording=True))
+ anonymous.compare(vm.get_bitmap(drive0.node, '', recording=True,
+ bitmaps=bitmaps))
# Simulate the order in which this will happen:
# group 1 gets cleared first, then group two gets written.
@@ -405,8 +376,8 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
vm.run_job(job, auto_dismiss=True, auto_finalize=False,
pre_finalize=_callback,
cancel=(failure == 'simulated'))
- bitmaps = query_bitmaps(vm)
- log(bitmaps, indent=2)
+ bitmaps = vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
log('')
if bsync_mode == 'always' and failure == 'intermediate':
@@ -423,29 +394,30 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
ebitmap.clear()
ebitmap.dirty_bits(range(fail_bit, SIZE // GRANULARITY))
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
# 2 - Writes and Reference Backup
bitmaps = perform_writes(drive0, 3)
ebitmap.dirty_group(3)
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
reference_backup(drive0, 2, fbackup2)
# 2 - Bitmap Backup (In failure modes, this is a recovery.)
job = backup(drive0, 2, bsync2, "bitmap",
bitmap="bitmap0", bitmap_mode=bsync_mode)
vm.run_job(job, auto_dismiss=True, auto_finalize=False)
- bitmaps = query_bitmaps(vm)
- log(bitmaps, indent=2)
+ bitmaps = vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
log('')
if bsync_mode != 'never':
ebitmap.clear()
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
log('--- Cleanup ---\n')
vm.qmp_log("block-dirty-bitmap-remove",
- node=drive0.name, name="bitmap0")
- log(query_bitmaps(vm), indent=2)
+ node=drive0.node, name="bitmap0")
+ bitmaps = vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
vm.shutdown()
log('')
@@ -484,22 +456,19 @@ def test_backup_api():
'filename': drive0.path
}
+ drive0.node = 'drive0'
vm.qmp_log('blockdev-add',
filters=[iotests.filter_qmp_testfiles],
- node_name="drive0",
+ node_name=drive0.node,
driver=drive0.fmt,
file=file_config)
- drive0.node = 'drive0'
- drive0.device = 'device0'
- vm.qmp_log("device_add", id=drive0.device,
- drive=drive0.name, driver="scsi-hd")
log('')
target0 = Drive(backup_path, vm=vm)
target0.create_target("backup_target", drive0.fmt, drive0.size)
log('')
- vm.qmp_log("block-dirty-bitmap-add", node=drive0.name,
+ vm.qmp_log("block-dirty-bitmap-add", node=drive0.node,
name="bitmap0", granularity=GRANULARITY)
log('')
@@ -538,7 +507,7 @@ def test_backup_api():
log("-- Sync mode {:s} tests --\n".format(sync_mode))
for bitmap in (None, 'bitmap404', 'bitmap0'):
for policy in error_cases[sync_mode][bitmap]:
- blockdev_backup(drive0.vm, drive0.name, "backup_target",
+ blockdev_backup(drive0.vm, drive0.node, "backup_target",
sync_mode, job_id='api_job',
bitmap=bitmap, bitmap_mode=policy)
log('')
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
index 84b79d7bfe..64dd460055 100644
--- a/tests/qemu-iotests/257.out
+++ b/tests/qemu-iotests/257.out
@@ -5,8 +5,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -19,9 +17,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -34,7 +30,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -55,7 +51,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -82,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -96,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -111,7 +107,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -153,7 +149,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -182,7 +178,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -209,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -223,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -231,7 +227,7 @@ expecting 15 dirty sectors; have 15. OK!
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -253,9 +249,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -271,8 +265,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -285,9 +277,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -300,7 +290,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -321,7 +311,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -348,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -364,13 +354,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -399,7 +389,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -426,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -440,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -448,7 +438,7 @@ expecting 14 dirty sectors; have 14. OK!
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -470,9 +460,7 @@ expecting 14 dirty sectors; have 14. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -488,8 +476,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -502,9 +488,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -517,7 +501,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -538,7 +522,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -565,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -579,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -594,7 +578,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -636,7 +620,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -665,7 +649,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -692,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -706,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -714,7 +698,7 @@ expecting 15 dirty sectors; have 15. OK!
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -736,9 +720,7 @@ expecting 15 dirty sectors; have 15. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -754,8 +736,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -768,9 +748,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -783,7 +761,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -804,7 +782,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -831,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -845,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -860,7 +838,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -902,7 +880,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -931,7 +909,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -958,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -972,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -980,7 +958,7 @@ expecting 15 dirty sectors; have 15. OK!
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1002,9 +980,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1020,8 +996,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1034,9 +1008,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1049,7 +1021,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1070,7 +1042,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1097,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1113,13 +1085,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1148,7 +1120,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -1175,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1189,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1197,7 +1169,7 @@ expecting 14 dirty sectors; have 14. OK!
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1219,9 +1191,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1237,8 +1207,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1251,9 +1219,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1266,7 +1232,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1287,7 +1253,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1314,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1328,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -1343,7 +1309,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1385,7 +1351,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -1414,7 +1380,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -1441,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1455,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1463,7 +1429,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1485,9 +1451,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1503,8 +1467,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1517,9 +1479,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1532,7 +1492,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1553,7 +1513,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1580,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1594,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -1609,7 +1569,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1651,7 +1611,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -1680,7 +1640,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -1707,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1721,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1729,7 +1689,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1751,9 +1711,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1769,8 +1727,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1783,9 +1739,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1798,7 +1752,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1819,7 +1773,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1846,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1862,13 +1816,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 327680,
@@ -1897,7 +1851,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 851968,
@@ -1924,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1938,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1946,7 +1900,7 @@ expecting 13 dirty sectors; have 13. OK!
{"data": {"device": "backup_2", "len": 851968, "offset": 851968, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1968,9 +1922,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1986,8 +1938,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2000,9 +1950,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2015,7 +1963,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2036,7 +1984,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2063,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2077,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -2092,7 +2040,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2134,7 +2082,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -2163,7 +2111,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -2190,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2204,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2212,7 +2160,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2234,9 +2182,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -2252,8 +2198,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2266,9 +2210,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2281,7 +2223,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2302,7 +2244,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2329,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2343,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -2358,7 +2300,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2400,7 +2342,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -2429,7 +2371,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -2456,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2470,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2478,7 +2420,7 @@ expecting 15 dirty sectors; have 15. OK!
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2500,9 +2442,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -2518,8 +2458,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2532,9 +2470,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2547,7 +2483,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2568,7 +2504,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2595,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2611,13 +2547,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2646,7 +2582,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -2673,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2687,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2695,7 +2631,7 @@ expecting 14 dirty sectors; have 14. OK!
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2717,9 +2653,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -2735,8 +2669,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2749,9 +2681,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2764,7 +2694,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2785,7 +2715,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2812,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2826,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -2841,7 +2771,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2883,7 +2813,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -2912,7 +2842,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -2939,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2953,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2961,7 +2891,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2983,9 +2913,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3001,8 +2929,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3015,9 +2941,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3030,7 +2954,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3051,7 +2975,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3078,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3092,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -3107,7 +3031,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3149,7 +3073,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -3178,7 +3102,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -3205,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3219,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3227,7 +3151,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3249,9 +3173,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3267,8 +3189,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3281,9 +3201,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3296,7 +3214,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3317,7 +3235,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3344,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3360,13 +3278,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 66125824,
@@ -3395,7 +3313,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 66453504,
@@ -3422,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3436,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3444,7 +3362,7 @@ expecting 1014 dirty sectors; have 1014. OK!
{"data": {"device": "backup_2", "len": 66453504, "offset": 66453504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3466,9 +3384,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3484,8 +3400,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3498,9 +3412,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3513,7 +3425,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3534,7 +3446,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3561,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3575,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -3590,7 +3502,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3632,7 +3544,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -3661,7 +3573,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -3688,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3702,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3710,7 +3622,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3732,9 +3644,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3750,8 +3660,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3764,9 +3672,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3779,7 +3685,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3800,7 +3706,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3827,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3841,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -3856,7 +3762,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3898,7 +3804,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -3927,7 +3833,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -3954,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3968,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3976,7 +3882,7 @@ expecting 15 dirty sectors; have 15. OK!
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3998,9 +3904,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4016,8 +3920,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4030,9 +3932,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4045,7 +3945,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4066,7 +3966,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4093,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4109,13 +4009,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4144,7 +4044,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -4171,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4185,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4193,7 +4093,7 @@ expecting 14 dirty sectors; have 14. OK!
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4215,9 +4115,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4233,8 +4131,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4247,9 +4143,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4262,7 +4156,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4283,7 +4177,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4310,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4324,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -4339,7 +4233,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4381,7 +4275,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -4410,7 +4304,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -4437,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4451,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4459,7 +4353,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4481,9 +4375,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4499,8 +4391,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4513,9 +4403,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4528,7 +4416,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4549,7 +4437,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4576,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4590,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -4605,7 +4493,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4647,7 +4535,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -4676,7 +4564,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -4703,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4717,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4725,7 +4613,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4747,9 +4635,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4765,8 +4651,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4779,9 +4663,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4794,7 +4676,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4815,7 +4697,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4842,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4858,13 +4740,13 @@ 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", "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"}}
{"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"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4893,7 +4775,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -4920,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4934,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4942,7 +4824,7 @@ expecting 14 dirty sectors; have 14. OK!
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4964,9 +4846,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4982,8 +4862,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4996,9 +4874,7 @@ write -P0x6f 0x2000000 0x10000
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -5011,7 +4887,7 @@ write -P0x76 0x3ff0000 0x10000
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5032,7 +4908,7 @@ write -P0x69 0x3fe0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -5059,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5073,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", "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"}}
{"return": {}}
--- Write #2 ---
@@ -5088,7 +4964,7 @@ write -P0x67 0x3fe0000 0x20000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -5130,7 +5006,7 @@ expecting 7 dirty sectors; have 7. OK!
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -5159,7 +5035,7 @@ write -P0xdd 0x3fc0000 0x10000
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -5186,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", "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"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5200,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", "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"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -5208,7 +5084,7 @@ expecting 12 dirty sectors; have 12. OK!
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -5230,9 +5106,7 @@ expecting 0 dirty sectors; have 0. OK!
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -5248,8 +5122,6 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0"}}
-{"return": {}}
{}
{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
@@ -5267,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", "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"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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"}}
{"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", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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"}}
{"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", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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"}}
{"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", "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"}}
{"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", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "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"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index e45cdfa66b..12b4751848 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -405,6 +405,23 @@ _check_test_img()
$QEMU_IMG check "$@" -f $IMGFMT "$TEST_IMG" 2>&1
fi
) | _filter_testdir | _filter_qemu_img_check
+
+ # return real qemu_img check status, to analyze in
+ # _check_test_img_ignore_leaks
+ return ${PIPESTATUS[0]}
+}
+
+_check_test_img_ignore_leaks()
+{
+ out=$(_check_test_img "$@")
+ status=$?
+ if [ $status = 3 ]; then
+ # This must correspond to success output in dump_human_image_check()
+ echo "No errors were found on the image."
+ return 0
+ fi
+ echo "$out"
+ return $status
}
_img_info()
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 9fb5181c3d..3a8f378f90 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -641,6 +641,33 @@ class VM(qtest.QEMUQtestMachine):
return x
return None
+ def query_bitmaps(self):
+ res = self.qmp("query-named-block-nodes")
+ return {device['node-name']: device['dirty-bitmaps']
+ for device in res['return'] if 'dirty-bitmaps' in device}
+
+ def get_bitmap(self, node_name, bitmap_name, recording=None, bitmaps=None):
+ """
+ get a specific bitmap from the object returned by query_bitmaps.
+ :param recording: If specified, filter results by the specified value.
+ :param bitmaps: If specified, use it instead of call query_bitmaps()
+ """
+ if bitmaps is None:
+ bitmaps = self.query_bitmaps()
+
+ for bitmap in bitmaps[node_name]:
+ if bitmap.get('name', '') == bitmap_name:
+ if recording is None:
+ return bitmap
+ elif bitmap.get('recording') == recording:
+ return bitmap
+ return None
+
+ def check_bitmap_status(self, node_name, bitmap_name, fields):
+ ret = self.get_bitmap(node_name, bitmap_name)
+
+ return fields.items() <= ret.items()
+
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index e80c4c6143..1e5be1d4ff 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -812,6 +812,423 @@ static void test_load_q(void)
qemu_fclose(fload);
}
+/* interval (key) */
+typedef struct TestGTreeInterval {
+ uint64_t low;
+ uint64_t high;
+} TestGTreeInterval;
+
+#define VMSTATE_INTERVAL \
+{ \
+ .name = "interval", \
+ .version_id = 1, \
+ .minimum_version_id = 1, \
+ .fields = (VMStateField[]) { \
+ VMSTATE_UINT64(low, TestGTreeInterval), \
+ VMSTATE_UINT64(high, TestGTreeInterval), \
+ VMSTATE_END_OF_LIST() \
+ } \
+}
+
+/* mapping (value) */
+typedef struct TestGTreeMapping {
+ uint64_t phys_addr;
+ uint32_t flags;
+} TestGTreeMapping;
+
+#define VMSTATE_MAPPING \
+{ \
+ .name = "mapping", \
+ .version_id = 1, \
+ .minimum_version_id = 1, \
+ .fields = (VMStateField[]) { \
+ VMSTATE_UINT64(phys_addr, TestGTreeMapping), \
+ VMSTATE_UINT32(flags, TestGTreeMapping), \
+ VMSTATE_END_OF_LIST() \
+ }, \
+}
+
+static const VMStateDescription vmstate_interval_mapping[2] = {
+ VMSTATE_MAPPING, /* value */
+ VMSTATE_INTERVAL /* key */
+};
+
+typedef struct TestGTreeDomain {
+ int32_t id;
+ GTree *mappings;
+} TestGTreeDomain;
+
+typedef struct TestGTreeIOMMU {
+ int32_t id;
+ GTree *domains;
+} TestGTreeIOMMU;
+
+/* Interval comparison function */
+static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ TestGTreeInterval *inta = (TestGTreeInterval *)a;
+ TestGTreeInterval *intb = (TestGTreeInterval *)b;
+
+ if (inta->high < intb->low) {
+ return -1;
+ } else if (intb->high < inta->low) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ID comparison function */
+static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ uint ua = GPOINTER_TO_UINT(a);
+ uint ub = GPOINTER_TO_UINT(b);
+ return (ua > ub) - (ua < ub);
+}
+
+static void destroy_domain(gpointer data)
+{
+ TestGTreeDomain *domain = (TestGTreeDomain *)data;
+
+ g_tree_destroy(domain->mappings);
+ g_free(domain);
+}
+
+static int domain_preload(void *opaque)
+{
+ TestGTreeDomain *domain = opaque;
+
+ domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
+ NULL, g_free, g_free);
+ return 0;
+}
+
+static int iommu_preload(void *opaque)
+{
+ TestGTreeIOMMU *iommu = opaque;
+
+ iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
+ NULL, NULL, destroy_domain);
+ return 0;
+}
+
+static const VMStateDescription vmstate_domain = {
+ .name = "domain",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = domain_preload,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(id, TestGTreeDomain),
+ VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1,
+ vmstate_interval_mapping,
+ TestGTreeInterval, TestGTreeMapping),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_iommu = {
+ .name = "iommu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = iommu_preload,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(id, TestGTreeIOMMU),
+ VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1,
+ &vmstate_domain, TestGTreeDomain),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+uint8_t first_domain_dump[] = {
+ /* id */
+ 0x00, 0x0, 0x0, 0x6,
+ 0x00, 0x0, 0x0, 0x2, /* 2 mappings */
+ 0x1, /* start of a */
+ /* a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
+ /* map_a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x1, /* start of b */
+ /* b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF,
+ /* map_b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x0, /* end of gtree */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+};
+
+static TestGTreeDomain *create_first_domain(void)
+{
+ TestGTreeDomain *domain;
+ TestGTreeMapping *map_a, *map_b;
+ TestGTreeInterval *a, *b;
+
+ domain = g_malloc0(sizeof(TestGTreeDomain));
+ domain->id = 6;
+
+ a = g_malloc0(sizeof(TestGTreeInterval));
+ a->low = 0x1000;
+ a->high = 0x1FFF;
+
+ b = g_malloc0(sizeof(TestGTreeInterval));
+ b->low = 0x4000;
+ b->high = 0x4FFF;
+
+ map_a = g_malloc0(sizeof(TestGTreeMapping));
+ map_a->phys_addr = 0xa000;
+ map_a->flags = 1;
+
+ map_b = g_malloc0(sizeof(TestGTreeMapping));
+ map_b->phys_addr = 0xe0000;
+ map_b->flags = 2;
+
+ domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ g_tree_insert(domain->mappings, a, map_a);
+ g_tree_insert(domain->mappings, b, map_b);
+ return domain;
+}
+
+static void test_gtree_save_domain(void)
+{
+ TestGTreeDomain *first_domain = create_first_domain();
+
+ save_vmstate(&vmstate_domain, first_domain);
+ compare_vmstate(first_domain_dump, sizeof(first_domain_dump));
+ destroy_domain(first_domain);
+}
+
+struct match_node_data {
+ GTree *tree;
+ gpointer key;
+ gpointer value;
+};
+
+struct tree_cmp_data {
+ GTree *tree1;
+ GTree *tree2;
+ GTraverseFunc match_node;
+};
+
+static gboolean match_interval_mapping_node(gpointer key,
+ gpointer value, gpointer data)
+{
+ TestGTreeMapping *map_a, *map_b;
+ TestGTreeInterval *a, *b;
+ struct match_node_data *d = (struct match_node_data *)data;
+ char *str = g_strdup_printf("dest");
+
+ g_free(str);
+ a = (TestGTreeInterval *)key;
+ b = (TestGTreeInterval *)d->key;
+
+ map_a = (TestGTreeMapping *)value;
+ map_b = (TestGTreeMapping *)d->value;
+
+ assert(a->low == b->low);
+ assert(a->high == b->high);
+ assert(map_a->phys_addr == map_b->phys_addr);
+ assert(map_a->flags == map_b->flags);
+ g_tree_remove(d->tree, key);
+ return true;
+}
+
+static gboolean diff_tree(gpointer key, gpointer value, gpointer data)
+{
+ struct tree_cmp_data *tp = (struct tree_cmp_data *)data;
+ struct match_node_data d = {tp->tree2, key, value};
+
+ g_tree_foreach(tp->tree2, tp->match_node, &d);
+ g_tree_remove(tp->tree1, key);
+ return false;
+}
+
+static void compare_trees(GTree *tree1, GTree *tree2,
+ GTraverseFunc function)
+{
+ struct tree_cmp_data tp = {tree1, tree2, function};
+
+ g_tree_foreach(tree1, diff_tree, &tp);
+ assert(g_tree_nnodes(tree1) == 0);
+ assert(g_tree_nnodes(tree2) == 0);
+}
+
+static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2)
+{
+ assert(d1->id == d2->id);
+ compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node);
+}
+
+static gboolean match_domain_node(gpointer key, gpointer value, gpointer data)
+{
+ uint64_t id1, id2;
+ TestGTreeDomain *d1, *d2;
+ struct match_node_data *d = (struct match_node_data *)data;
+
+ id1 = (uint64_t)(uintptr_t)key;
+ id2 = (uint64_t)(uintptr_t)d->key;
+ d1 = (TestGTreeDomain *)value;
+ d2 = (TestGTreeDomain *)d->value;
+ assert(id1 == id2);
+ diff_domain(d1, d2);
+ g_tree_remove(d->tree, key);
+ return true;
+}
+
+static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2)
+{
+ assert(iommu1->id == iommu2->id);
+ compare_trees(iommu1->domains, iommu2->domains, match_domain_node);
+}
+
+static void test_gtree_load_domain(void)
+{
+ TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain));
+ TestGTreeDomain *orig_domain = create_first_domain();
+ QEMUFile *fload, *fsave;
+ char eof;
+
+ fsave = open_test_file(true);
+ qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump));
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ fload = open_test_file(false);
+
+ vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
+ eof = qemu_get_byte(fload);
+ g_assert(!qemu_file_get_error(fload));
+ g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
+ g_assert_cmpint(eof, ==, QEMU_VM_EOF);
+
+ diff_domain(orig_domain, dest_domain);
+ destroy_domain(orig_domain);
+ destroy_domain(dest_domain);
+ qemu_fclose(fload);
+}
+
+uint8_t iommu_dump[] = {
+ /* iommu id */
+ 0x00, 0x0, 0x0, 0x7,
+ 0x00, 0x0, 0x0, 0x2, /* 2 domains */
+ 0x1,/* start of domain 5 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5, /* key = 5 */
+ 0x00, 0x0, 0x0, 0x5, /* domain1 id */
+ 0x00, 0x0, 0x0, 0x1, /* 1 mapping */
+ 0x1, /* start of mappings */
+ /* c */
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF,
+ /* map_c */
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0x00, 0x0, 0x0, 0x3,
+ 0x0, /* end of domain1 mappings*/
+ 0x1,/* start of domain 6 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6, /* key = 6 */
+ 0x00, 0x0, 0x0, 0x6, /* domain6 id */
+ 0x00, 0x0, 0x0, 0x2, /* 2 mappings */
+ 0x1, /* start of a */
+ /* a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
+ /* map_a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x1, /* start of b */
+ /* b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF,
+ /* map_b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x0, /* end of domain6 mappings*/
+ 0x0, /* end of domains */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+};
+
+static TestGTreeIOMMU *create_iommu(void)
+{
+ TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU));
+ TestGTreeDomain *first_domain = create_first_domain();
+ TestGTreeDomain *second_domain;
+ TestGTreeMapping *map_c;
+ TestGTreeInterval *c;
+
+ iommu->id = 7;
+ iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL,
+ NULL,
+ destroy_domain);
+
+ second_domain = g_malloc0(sizeof(TestGTreeDomain));
+ second_domain->id = 5;
+ second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
+ NULL,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+
+ g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain);
+ g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain);
+
+ c = g_malloc0(sizeof(TestGTreeInterval));
+ c->low = 0x1000000;
+ c->high = 0x1FFFFFF;
+
+ map_c = g_malloc0(sizeof(TestGTreeMapping));
+ map_c->phys_addr = 0xF000000;
+ map_c->flags = 0x3;
+
+ g_tree_insert(second_domain->mappings, c, map_c);
+ return iommu;
+}
+
+static void destroy_iommu(TestGTreeIOMMU *iommu)
+{
+ g_tree_destroy(iommu->domains);
+ g_free(iommu);
+}
+
+static void test_gtree_save_iommu(void)
+{
+ TestGTreeIOMMU *iommu = create_iommu();
+
+ save_vmstate(&vmstate_iommu, iommu);
+ compare_vmstate(iommu_dump, sizeof(iommu_dump));
+ destroy_iommu(iommu);
+}
+
+static void test_gtree_load_iommu(void)
+{
+ TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU));
+ TestGTreeIOMMU *orig_iommu = create_iommu();
+ QEMUFile *fsave, *fload;
+ char eof;
+ int ret;
+
+ fsave = open_test_file(true);
+ qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump));
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ fload = open_test_file(false);
+ vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
+ ret = qemu_file_get_error(fload);
+ eof = qemu_get_byte(fload);
+ ret = qemu_file_get_error(fload);
+ g_assert(!ret);
+ g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
+ g_assert_cmpint(eof, ==, QEMU_VM_EOF);
+
+ diff_iommu(orig_iommu, dest_iommu);
+ destroy_iommu(orig_iommu);
+ destroy_iommu(dest_iommu);
+ qemu_fclose(fload);
+}
+
typedef struct TmpTestStruct {
TestStruct *parent;
int64_t diff;
@@ -932,6 +1349,10 @@ int main(int argc, char **argv)
test_arr_ptr_prim_0_load);
g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
+ g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain);
+ g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain);
+ g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu);
+ g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu);
g_test_add_func("/vmstate/tmp_struct", test_tmp_struct);
g_test_run();
diff --git a/vl.c b/vl.c
index 002bf4919e..0a295e5d77 100644
--- a/vl.c
+++ b/vl.c
@@ -3335,7 +3335,8 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_virtfs: {
QemuOpts *fsdev;
QemuOpts *device;
- const char *writeout, *sock_fd, *socket, *path, *security_model;
+ const char *writeout, *sock_fd, *socket, *path, *security_model,
+ *multidevs;
olist = qemu_find_opts("virtfs");
if (!olist) {
@@ -3395,6 +3396,10 @@ int main(int argc, char **argv, char **envp)
qemu_opt_set_bool(fsdev, "readonly",
qemu_opt_get_bool(opts, "readonly", 0),
&error_abort);
+ multidevs = qemu_opt_get(opts, "multidevs");
+ if (multidevs) {
+ qemu_opt_set(fsdev, "multidevs", multidevs, &error_abort);
+ }
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
&error_abort);
qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort);