All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup
@ 2019-08-26 16:12 Vladimir Sementsov-Ogievskiy
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster Vladimir Sementsov-Ogievskiy
                   ` (12 more replies)
  0 siblings, 13 replies; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:12 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Hi all!

These series introduce backup-top driver. It's a filter-node, which
do copy-before-write operation. Mirror uses filter-node for handling
guest writes, let's move to filter-node (from write-notifiers) for
backup too.

I decided to postpone all these "preparation" and "improvement"
series, as it seems we will never do the core thing in such way.

v9:

01: rebased, it's from "backup improvements" series

02-05: new patches, introduction of block-copy

06: unchanged

07-08: new iotests preparation

10-11: unchanged

12:
backup-top filter is simplified: it don't do copy operation
by hands but through block-copy, and it don't have second "target"
child anymore. So the state is reduced to only two fields: 
block-copy state and "active".

What else?
1. filter-node-name parameter added
2. "active" parameter. I failed on my way to make bdrv_append
firstly do bdrv_replace_node and then bdrv_set_backing_hd, as it
leads to much problems, block-layer is unprepared for child-less
filters. So, it's a lot simpler to cheat a bit with permissions,
and don't require unshared WRITE during appending.
3. filter is not finished in this patch, as block_copy is not
finished too, you can see a TODO and abort() in backup_top_cbw.

13:
- filter-node-name added
- backup_clean simplified: don't bother with keeping block job
  blk on backup-top filter, it's not necessary.
- locking is all done in block-copy code
- itests changed in different manner
- I'm afraid that's not a full list, patch really changed
  significantly.

The series also needs one small fix:
  "[PATCH] block: fix permission update in bdrv_replace_node"
Based-on: <20190824100740.61635-1-vsementsov@virtuozzo.com>

Vladimir Sementsov-Ogievskiy (13):
  block/backup: fix backup_cow_with_offload for last cluster
  block/backup: split shareable copying part from backup_do_cow
  block/backup: introduce BlockCopyState
  block/backup: adjust block-copy functions style
  block: move block_copy from block/backup.c to separate file
  block: teach bdrv_debug_breakpoint skip filters with backing
  iotests: prepare 124 and 257 bitmap querying for backup-top filter
  iotests: 257: drop unused Drive.device field
  iotests: 257: drop device_add
  block/io: refactor wait_serialising_requests
  block: add lock/unlock range functions
  block: introduce backup-top filter driver
  block/backup: use backup-top instead of write notifiers

 qapi/block-core.json          |   8 +-
 block/backup-top.h            |  37 ++
 include/block/block-copy.h    |  59 +++
 include/block/block_int.h     |   5 +
 block.c                       |  34 +-
 block/backup-top.c            | 241 ++++++++++++
 block/backup.c                | 411 +++----------------
 block/block-copy.c            | 320 +++++++++++++++
 block/io.c                    |  68 +++-
 block/replication.c           |   2 +-
 blockdev.c                    |   1 +
 block/Makefile.objs           |   3 +
 block/trace-events            |  14 +-
 tests/qemu-iotests/056        |   2 +-
 tests/qemu-iotests/124        |   3 +-
 tests/qemu-iotests/257        |  81 ++--
 tests/qemu-iotests/257.out    | 714 ++++++++++++++--------------------
 tests/qemu-iotests/iotests.py |  22 ++
 18 files changed, 1163 insertions(+), 862 deletions(-)
 create mode 100644 block/backup-top.h
 create mode 100644 include/block/block-copy.h
 create mode 100644 block/backup-top.c
 create mode 100644 block/block-copy.c

-- 
2.18.0



^ permalink raw reply	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 14:08   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow Vladimir Sementsov-Ogievskiy
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

We shouldn't try to copy bytes beyond EOF. Fix it.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/backup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/backup.c b/block/backup.c
index 2baf7bed65..33b144305f 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -161,7 +161,7 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
 
     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);
+    nbytes = MIN(job->copy_range_size, MIN(end - start, job->len - start));
     nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
     bdrv_reset_dirty_bitmap(job->copy_bitmap, start,
                             job->cluster_size * nr_clusters);
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 14:22   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState Vladimir Sementsov-Ogievskiy
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Split copying logic which will be shared with backup-top filter.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/backup.c | 47 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 32 insertions(+), 15 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 33b144305f..13a1d80157 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -248,26 +248,18 @@ static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
     return ret;
 }
 
-static int coroutine_fn backup_do_cow(BackupBlockJob *job,
-                                      int64_t offset, uint64_t bytes,
-                                      bool *error_is_read,
-                                      bool is_write_notifier)
+static int coroutine_fn backup_do_copy(BackupBlockJob *job,
+                                       int64_t offset, uint64_t bytes,
+                                       bool *error_is_read,
+                                       bool is_write_notifier)
 {
-    CowRequest cow_request;
     int ret = 0;
-    int64_t start, end; /* bytes */
+    int64_t start = offset, end = bytes + offset; /* 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);
+    assert(QEMU_IS_ALIGNED(start, job->cluster_size));
+    assert(QEMU_IS_ALIGNED(end, job->cluster_size));
 
     while (start < end) {
         int64_t dirty_end;
@@ -326,6 +318,31 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
         qemu_vfree(bounce_buffer);
     }
 
+    return ret;
+}
+
+static int coroutine_fn backup_do_cow(BackupBlockJob *job,
+                                      int64_t offset, uint64_t bytes,
+                                      bool *error_is_read,
+                                      bool is_write_notifier)
+{
+    CowRequest cow_request;
+    int ret = 0;
+    int64_t start, end; /* 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);
+
+    ret = backup_do_copy(job, start, end - start, error_is_read,
+                         is_write_notifier);
+
     cow_request_end(&cow_request);
 
     trace_backup_do_cow_return(job, offset, bytes, ret);
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster Vladimir Sementsov-Ogievskiy
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 15:59   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style Vladimir Sementsov-Ogievskiy
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Split copying code part from backup to "block-copy", including separate
state structure and function renaming. This is needed to share it with
backup-top filter driver in further commits.

Notes:

1. As BlockCopyState keeps own BlockBackend objects, remaining
job->common.blk users only use it to get bs by blk_bs() call, so clear
job->commen.blk permissions set in block_job_create.

2. Rename s/initializing_bitmap/skip_unallocated/ to sound a bit better
as interface to BlockCopyState

3. Split is not very clean: there left some duplicated fields, backup
code uses some BlockCopyState fields directly, let's postpone it for
further improvements and keep this comment simpler for review.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/backup.c     | 324 +++++++++++++++++++++++++++------------------
 block/trace-events |  12 +-
 2 files changed, 200 insertions(+), 136 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 13a1d80157..f52ac622e0 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -35,12 +35,35 @@ typedef struct CowRequest {
     CoQueue wait_queue; /* coroutines blocked on this request */
 } CowRequest;
 
+/*
+ * ProgressCallbackFunc
+ *
+ * Called when some progress is done in context of BlockCopyState:
+ *  1. When some bytes copied, called with @bytes > 0.
+ *  2. When some bytes resetted from copy_bitmap, called with @bytes = 0 (user
+ *     may recalculate remaining bytes from copy_bitmap dirty count.
+ */
+typedef void (*ProgressCallbackFunc)(int64_t bytes, void *opaque);
+typedef struct BlockCopyState {
+    BlockBackend *source;
+    BlockBackend *target;
+    BdrvDirtyBitmap *copy_bitmap;
+    int64_t cluster_size;
+    bool use_copy_range;
+    int64_t copy_range_size;
+    uint64_t len;
+
+    BdrvRequestFlags write_flags;
+    bool skip_unallocated;
+
+    ProgressCallbackFunc progress_callback;
+    void *progress_opaque;
+} BlockCopyState;
+
 typedef struct BackupBlockJob {
     BlockJob common;
-    BlockBackend *target;
 
     BdrvDirtyBitmap *sync_bitmap;
-    BdrvDirtyBitmap *copy_bitmap;
 
     MirrorSyncMode sync_mode;
     BitmapSyncMode bitmap_mode;
@@ -53,11 +76,7 @@ typedef struct BackupBlockJob {
     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;
@@ -99,9 +118,83 @@ static void cow_request_end(CowRequest *req)
     qemu_co_queue_restart_all(&req->wait_queue);
 }
 
+static void block_copy_state_free(BlockCopyState *s)
+{
+    if (!s) {
+        return;
+    }
+
+    bdrv_release_dirty_bitmap(blk_bs(s->source), s->copy_bitmap);
+    blk_unref(s->source);
+    s->source = NULL;
+    blk_unref(s->target);
+    s->target = NULL;
+    g_free(s);
+}
+
+static BlockCopyState *block_copy_state_new(
+        BlockDriverState *source, BlockDriverState *target,
+        int64_t cluster_size, BdrvRequestFlags write_flags,
+        ProgressCallbackFunc progress_callback, void *progress_opaque,
+        Error **errp)
+{
+    BlockCopyState *s;
+    int ret;
+    uint64_t no_resize = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
+                         BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD;
+    BdrvDirtyBitmap *copy_bitmap =
+            bdrv_create_dirty_bitmap(source, cluster_size, NULL, errp);
+
+    if (!copy_bitmap) {
+        return NULL;
+    }
+    bdrv_disable_dirty_bitmap(copy_bitmap);
+
+    s = g_new0(BlockCopyState, 1);
+    *s = (BlockCopyState) {
+        .source = blk_new(bdrv_get_aio_context(source),
+                          BLK_PERM_CONSISTENT_READ, no_resize),
+        .target = blk_new(bdrv_get_aio_context(target),
+                          BLK_PERM_WRITE, no_resize),
+        .copy_bitmap = copy_bitmap,
+        .cluster_size = cluster_size,
+        .len = bdrv_dirty_bitmap_size(copy_bitmap),
+        .write_flags = write_flags,
+        .use_copy_range = !(write_flags & BDRV_REQ_WRITE_COMPRESSED),
+        .progress_callback = progress_callback,
+        .progress_opaque = progress_opaque,
+    };
+
+    s->copy_range_size = QEMU_ALIGN_UP(MIN(blk_get_max_transfer(s->source),
+                                           blk_get_max_transfer(s->target)),
+                                       s->cluster_size),
+
+    blk_set_disable_request_queuing(s->source, true);
+    blk_set_allow_aio_context_change(s->source, true);
+    blk_set_disable_request_queuing(s->target, true);
+    blk_set_allow_aio_context_change(s->target, true);
+
+    ret = blk_insert_bs(s->source, source, errp);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    ret = blk_insert_bs(s->target, target, errp);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    return s;
+
+fail:
+    block_copy_state_free(s);
+
+    return NULL;
+}
+
 /* 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,
+static int coroutine_fn block_copy_with_bounce_buffer(BlockCopyState *s,
                                                       int64_t start,
                                                       int64_t end,
                                                       bool is_write_notifier,
@@ -109,30 +202,29 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
                                                       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);
+    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 = blk_blockalign(blk, job->cluster_size);
+        *bounce_buffer = blk_blockalign(s->source, s->cluster_size);
     }
 
-    ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
+    ret = blk_co_pread(s->source, start, nbytes, *bounce_buffer, read_flags);
     if (ret < 0) {
-        trace_backup_do_cow_read_fail(job, start, ret);
+        trace_block_copy_with_bounce_buffer_read_fail(s, 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);
+    ret = blk_co_pwrite(s->target, start, nbytes, *bounce_buffer,
+                        s->write_flags);
     if (ret < 0) {
-        trace_backup_do_cow_write_fail(job, start, ret);
+        trace_block_copy_with_bounce_buffer_write_fail(s, start, ret);
         if (error_is_read) {
             *error_is_read = false;
         }
@@ -141,36 +233,35 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
 
     return nbytes;
 fail:
-    bdrv_set_dirty_bitmap(job->copy_bitmap, start, job->cluster_size);
+    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 backup_cow_with_offload(BackupBlockJob *job,
+static int coroutine_fn block_copy_with_offload(BlockCopyState *s,
                                                 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, MIN(end - start, job->len - 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);
+    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 - start, 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 = blk_co_copy_range(s->source, start, s->target, start, nbytes,
+                            read_flags, s->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);
+        trace_block_copy_with_offload_fail(s, start, ret);
+        bdrv_set_dirty_bitmap(s->copy_bitmap, start,
+                              s->cluster_size * nr_clusters);
         return ret;
     }
 
@@ -181,10 +272,10 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
  * 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)
+static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
+                                           int64_t *pnum)
 {
-    BlockDriverState *bs = blk_bs(s->common.blk);
+    BlockDriverState *bs = blk_bs(s->source);
     int64_t count, total_count = 0;
     int64_t bytes = s->len - offset;
     int ret;
@@ -225,13 +316,13 @@ static int backup_is_cluster_allocated(BackupBlockJob *s, int64_t offset,
  * @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)
+static int64_t block_copy_reset_unallocated(BlockCopyState *s,
+                                            int64_t offset, int64_t *count)
 {
     int ret;
-    int64_t clusters, bytes, estimate;
+    int64_t clusters, bytes;
 
-    ret = backup_is_cluster_allocated(s, offset, &clusters);
+    ret = block_copy_is_cluster_allocated(s, offset, &clusters);
     if (ret < 0) {
         return ret;
     }
@@ -240,46 +331,45 @@ static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
 
     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);
+        s->progress_callback(0, s->progress_opaque);
     }
 
     *count = bytes;
     return ret;
 }
 
-static int coroutine_fn backup_do_copy(BackupBlockJob *job,
-                                       int64_t offset, uint64_t bytes,
-                                       bool *error_is_read,
-                                       bool is_write_notifier)
+static int coroutine_fn block_copy(BlockCopyState *s,
+                                   int64_t offset, uint64_t bytes,
+                                   bool *error_is_read,
+                                   bool is_write_notifier)
 {
     int ret = 0;
     int64_t start = offset, end = bytes + offset; /* bytes */
     void *bounce_buffer = NULL;
     int64_t status_bytes;
 
-    assert(QEMU_IS_ALIGNED(start, job->cluster_size));
-    assert(QEMU_IS_ALIGNED(end, job->cluster_size));
+    assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+    assert(QEMU_IS_ALIGNED(end, s->cluster_size));
 
     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;
+        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(job->copy_bitmap, start,
+        dirty_end = bdrv_dirty_bitmap_next_zero(s->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 (s->skip_unallocated) {
+            ret = block_copy_reset_unallocated(s, start, &status_bytes);
             if (ret == 0) {
-                trace_backup_do_cow_skip_range(job, start, status_bytes);
+                trace_block_copy_skip_range(s, start, status_bytes);
                 start += status_bytes;
                 continue;
             }
@@ -287,17 +377,17 @@ static int coroutine_fn backup_do_copy(BackupBlockJob *job,
             dirty_end = MIN(dirty_end, start + status_bytes);
         }
 
-        trace_backup_do_cow_process(job, start);
+        trace_block_copy_process(s, start);
 
-        if (job->use_copy_range) {
-            ret = backup_cow_with_offload(job, start, dirty_end,
+        if (s->use_copy_range) {
+            ret = block_copy_with_offload(s, start, dirty_end,
                                           is_write_notifier);
             if (ret < 0) {
-                job->use_copy_range = false;
+                s->use_copy_range = false;
             }
         }
-        if (!job->use_copy_range) {
-            ret = backup_cow_with_bounce_buffer(job, start, dirty_end,
+        if (!s->use_copy_range) {
+            ret = block_copy_with_bounce_buffer(s, start, dirty_end,
                                                 is_write_notifier,
                                                 error_is_read, &bounce_buffer);
         }
@@ -305,12 +395,8 @@ static int coroutine_fn backup_do_copy(BackupBlockJob *job,
             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);
+        s->progress_callback(ret, s->progress_opaque);
         ret = 0;
     }
 
@@ -321,6 +407,20 @@ static int coroutine_fn backup_do_copy(BackupBlockJob *job,
     return ret;
 }
 
+static void backup_progress_callback(int64_t bytes, void *opaque)
+{
+    BackupBlockJob *s = opaque;
+    uint64_t estimate;
+
+    if (bytes > 0) {
+        s->bytes_read += bytes;
+        job_progress_update(&s->common.job, bytes);
+    } else {
+        estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap);
+        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,
@@ -340,8 +440,8 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
     wait_for_overlapping_requests(job, start, end);
     cow_request_begin(&cow_request, job, start, end);
 
-    ret = backup_do_copy(job, start, end - start, error_is_read,
-                         is_write_notifier);
+    ret = block_copy(job->bcs, start, end - start, error_is_read,
+                     is_write_notifier);
 
     cow_request_end(&cow_request);
 
@@ -391,7 +491,7 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
 
     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);
     }
 }
@@ -415,16 +515,9 @@ 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;
+    block_copy_state_free(s->bcs);
+    s->bcs = NULL;
 }
 
 void backup_do_checkpoint(BlockJob *job, Error **errp)
@@ -439,7 +532,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 void backup_drain(BlockJob *job)
@@ -449,8 +542,8 @@ static void backup_drain(BlockJob *job)
     /* Need to keep a reference in case blk_drain triggers execution
      * of backup_complete...
      */
-    if (s->target) {
-        BlockBackend *target = s->target;
+    if (s->bcs && s->bcs->target) {
+        BlockBackend *target = s->bcs->target;
         blk_ref(target);
         blk_drain(target);
         blk_unref(target);
@@ -497,7 +590,7 @@ 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)) {
@@ -524,7 +617,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);
@@ -534,12 +627,12 @@ 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);
 }
 
@@ -567,14 +660,14 @@ 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) {
@@ -663,9 +756,7 @@ 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;
 
     assert(bs);
     assert(target);
@@ -730,57 +821,34 @@ 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) {
-        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, bs, 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->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;
 
-    /*
-     * 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);
+    job->bcs = block_copy_state_new(
+            bs, target, cluster_size,
+            /*
+             * Set write flags:
+             * 1. Detect image-fleecing (and similar) schemes
+             * 2. Handle compression
+             */
+            (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
+            (compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
+            backup_progress_callback, job, errp);
+    if (!job->bcs) {
+        goto error;
+    }
 
     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() */
     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
@@ -790,10 +858,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
     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);
     }
diff --git a/block/trace-events b/block/trace-events
index 04209f058d..ad1454f539 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -40,12 +40,12 @@ 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_skip(void *job, int64_t start) "job %p start %"PRId64
+block_copy_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
+block_copy_process(void *job, int64_t start) "job %p start %"PRId64
+block_copy_with_bounce_buffer_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
+block_copy_with_bounce_buffer_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
+block_copy_with_offload_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
 
 # ../blockdev.c
 qmp_block_job_cancel(void *job) "job %p"
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (2 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 16:06   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file Vladimir Sementsov-Ogievskiy
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Fix comment style and reflow arguments in same manner like
block_copy_state_new.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/backup.c | 40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index f52ac622e0..d9192ac778 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -192,14 +192,13 @@ fail:
     return NULL;
 }
 
-/* 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 is_write_notifier,
-                                                      bool *error_is_read,
-                                                      void **bounce_buffer)
+/*
+ * 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 is_write_notifier,
+        bool *error_is_read, void **bounce_buffer)
 {
     int ret;
     int nbytes;
@@ -238,12 +237,12 @@ fail:
 
 }
 
-/* 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,
-                                                bool is_write_notifier)
+/*
+ * 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, bool is_write_notifier)
 {
     int ret;
     int nr_clusters;
@@ -310,14 +309,14 @@ static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
     }
 }
 
-/**
+/*
  * 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 block_copy_reset_unallocated(BlockCopyState *s,
-                                            int64_t offset, int64_t *count)
+static int64_t block_copy_reset_unallocated(
+        BlockCopyState *s, int64_t offset, int64_t *count)
 {
     int ret;
     int64_t clusters, bytes;
@@ -338,10 +337,9 @@ static int64_t block_copy_reset_unallocated(BlockCopyState *s,
     return ret;
 }
 
-static int coroutine_fn block_copy(BlockCopyState *s,
-                                   int64_t offset, uint64_t bytes,
-                                   bool *error_is_read,
-                                   bool is_write_notifier)
+static int coroutine_fn block_copy(
+        BlockCopyState *s, int64_t offset, uint64_t bytes, bool *error_is_read,
+        bool is_write_notifier)
 {
     int ret = 0;
     int64_t start = offset, end = bytes + offset; /* bytes */
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (3 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 16:16   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 06/13] block: teach bdrv_debug_breakpoint skip filters with backing Vladimir Sementsov-Ogievskiy
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Split block_copy to separate file, to be cleanly shared with backup-top
filter driver in further commits.

It's a clean movement, the only change is drop "static" from interface
functions.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 include/block/block-copy.h |  59 +++++++
 block/backup.c             | 313 +------------------------------------
 block/block-copy.c         | 307 ++++++++++++++++++++++++++++++++++++
 block/Makefile.objs        |   1 +
 block/trace-events         |   2 +
 5 files changed, 370 insertions(+), 312 deletions(-)
 create mode 100644 include/block/block-copy.h
 create mode 100644 block/block-copy.c

diff --git a/include/block/block-copy.h b/include/block/block-copy.h
new file mode 100644
index 0000000000..0dd7a3f7bf
--- /dev/null
+++ b/include/block/block-copy.h
@@ -0,0 +1,59 @@
+/*
+ * 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"
+
+/*
+ * ProgressCallbackFunc
+ *
+ * Called when some progress is done in context of BlockCopyState:
+ *  1. When some bytes copied, called with @bytes > 0.
+ *  2. When some bytes resetted from copy_bitmap, called with @bytes = 0 (user
+ *     may recalculate remaining bytes from copy_bitmap dirty count.
+ */
+typedef void (*ProgressCallbackFunc)(int64_t bytes, void *opaque);
+typedef struct BlockCopyState {
+    BlockBackend *source;
+    BlockBackend *target;
+    BdrvDirtyBitmap *copy_bitmap;
+    int64_t cluster_size;
+    bool use_copy_range;
+    int64_t copy_range_size;
+    uint64_t len;
+
+    BdrvRequestFlags write_flags;
+    bool skip_unallocated;
+
+    ProgressCallbackFunc progress_callback;
+    void *progress_opaque;
+} BlockCopyState;
+
+BlockCopyState *block_copy_state_new(
+        BlockDriverState *source, BlockDriverState *target,
+        int64_t cluster_size, BdrvRequestFlags write_flags,
+        ProgressCallbackFunc progress_callback, void *progress_opaque,
+        Error **errp);
+
+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 offset, uint64_t bytes,
+                            bool *error_is_read, bool is_write_notifier);
+
+#endif /* BLOCK_COPY_H */
diff --git a/block/backup.c b/block/backup.c
index d9192ac778..d927c63e5a 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -18,6 +18,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"
@@ -35,31 +36,6 @@ typedef struct CowRequest {
     CoQueue wait_queue; /* coroutines blocked on this request */
 } CowRequest;
 
-/*
- * ProgressCallbackFunc
- *
- * Called when some progress is done in context of BlockCopyState:
- *  1. When some bytes copied, called with @bytes > 0.
- *  2. When some bytes resetted from copy_bitmap, called with @bytes = 0 (user
- *     may recalculate remaining bytes from copy_bitmap dirty count.
- */
-typedef void (*ProgressCallbackFunc)(int64_t bytes, void *opaque);
-typedef struct BlockCopyState {
-    BlockBackend *source;
-    BlockBackend *target;
-    BdrvDirtyBitmap *copy_bitmap;
-    int64_t cluster_size;
-    bool use_copy_range;
-    int64_t copy_range_size;
-    uint64_t len;
-
-    BdrvRequestFlags write_flags;
-    bool skip_unallocated;
-
-    ProgressCallbackFunc progress_callback;
-    void *progress_opaque;
-} BlockCopyState;
-
 typedef struct BackupBlockJob {
     BlockJob common;
 
@@ -118,293 +94,6 @@ static void cow_request_end(CowRequest *req)
     qemu_co_queue_restart_all(&req->wait_queue);
 }
 
-static void block_copy_state_free(BlockCopyState *s)
-{
-    if (!s) {
-        return;
-    }
-
-    bdrv_release_dirty_bitmap(blk_bs(s->source), s->copy_bitmap);
-    blk_unref(s->source);
-    s->source = NULL;
-    blk_unref(s->target);
-    s->target = NULL;
-    g_free(s);
-}
-
-static BlockCopyState *block_copy_state_new(
-        BlockDriverState *source, BlockDriverState *target,
-        int64_t cluster_size, BdrvRequestFlags write_flags,
-        ProgressCallbackFunc progress_callback, void *progress_opaque,
-        Error **errp)
-{
-    BlockCopyState *s;
-    int ret;
-    uint64_t no_resize = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
-                         BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD;
-    BdrvDirtyBitmap *copy_bitmap =
-            bdrv_create_dirty_bitmap(source, cluster_size, NULL, errp);
-
-    if (!copy_bitmap) {
-        return NULL;
-    }
-    bdrv_disable_dirty_bitmap(copy_bitmap);
-
-    s = g_new0(BlockCopyState, 1);
-    *s = (BlockCopyState) {
-        .source = blk_new(bdrv_get_aio_context(source),
-                          BLK_PERM_CONSISTENT_READ, no_resize),
-        .target = blk_new(bdrv_get_aio_context(target),
-                          BLK_PERM_WRITE, no_resize),
-        .copy_bitmap = copy_bitmap,
-        .cluster_size = cluster_size,
-        .len = bdrv_dirty_bitmap_size(copy_bitmap),
-        .write_flags = write_flags,
-        .use_copy_range = !(write_flags & BDRV_REQ_WRITE_COMPRESSED),
-        .progress_callback = progress_callback,
-        .progress_opaque = progress_opaque,
-    };
-
-    s->copy_range_size = QEMU_ALIGN_UP(MIN(blk_get_max_transfer(s->source),
-                                           blk_get_max_transfer(s->target)),
-                                       s->cluster_size),
-
-    blk_set_disable_request_queuing(s->source, true);
-    blk_set_allow_aio_context_change(s->source, true);
-    blk_set_disable_request_queuing(s->target, true);
-    blk_set_allow_aio_context_change(s->target, true);
-
-    ret = blk_insert_bs(s->source, source, errp);
-    if (ret < 0) {
-        goto fail;
-    }
-
-    ret = blk_insert_bs(s->target, target, errp);
-    if (ret < 0) {
-        goto fail;
-    }
-
-    return s;
-
-fail:
-    block_copy_state_free(s);
-
-    return NULL;
-}
-
-/*
- * 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 is_write_notifier,
-        bool *error_is_read, void **bounce_buffer)
-{
-    int ret;
-    int nbytes;
-    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
-
-    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 = blk_blockalign(s->source, s->cluster_size);
-    }
-
-    ret = blk_co_pread(s->source, start, nbytes, *bounce_buffer, read_flags);
-    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 = blk_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, bool is_write_notifier)
-{
-    int ret;
-    int nr_clusters;
-    int nbytes;
-    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
-
-    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 - start, 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 = blk_co_copy_range(s->source, start, s->target, start, nbytes,
-                            read_flags, 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 = blk_bs(s->source);
-    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 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_callback(0, s->progress_opaque);
-    }
-
-    *count = bytes;
-    return ret;
-}
-
-static int coroutine_fn block_copy(
-        BlockCopyState *s, int64_t offset, uint64_t bytes, bool *error_is_read,
-        bool is_write_notifier)
-{
-    int ret = 0;
-    int64_t start = offset, end = bytes + offset; /* bytes */
-    void *bounce_buffer = NULL;
-    int64_t status_bytes;
-
-    assert(QEMU_IS_ALIGNED(start, s->cluster_size));
-    assert(QEMU_IS_ALIGNED(end, s->cluster_size));
-
-    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,
-                                          is_write_notifier);
-            if (ret < 0) {
-                s->use_copy_range = false;
-            }
-        }
-        if (!s->use_copy_range) {
-            ret = block_copy_with_bounce_buffer(s, start, dirty_end,
-                                                is_write_notifier,
-                                                error_is_read, &bounce_buffer);
-        }
-        if (ret < 0) {
-            break;
-        }
-
-        start += ret;
-        s->progress_callback(ret, s->progress_opaque);
-        ret = 0;
-    }
-
-    if (bounce_buffer) {
-        qemu_vfree(bounce_buffer);
-    }
-
-    return ret;
-}
-
 static void backup_progress_callback(int64_t bytes, void *opaque)
 {
     BackupBlockJob *s = opaque;
diff --git a/block/block-copy.c b/block/block-copy.c
new file mode 100644
index 0000000000..6828c46ba0
--- /dev/null
+++ b/block/block-copy.c
@@ -0,0 +1,307 @@
+/*
+ * 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"
+
+void block_copy_state_free(BlockCopyState *s)
+{
+    if (!s) {
+        return;
+    }
+
+    bdrv_release_dirty_bitmap(blk_bs(s->source), s->copy_bitmap);
+    blk_unref(s->source);
+    s->source = NULL;
+    blk_unref(s->target);
+    s->target = NULL;
+    g_free(s);
+}
+
+BlockCopyState *block_copy_state_new(
+        BlockDriverState *source, BlockDriverState *target,
+        int64_t cluster_size, BdrvRequestFlags write_flags,
+        ProgressCallbackFunc progress_callback, void *progress_opaque,
+        Error **errp)
+{
+    BlockCopyState *s;
+    int ret;
+    uint64_t no_resize = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
+                         BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD;
+    BdrvDirtyBitmap *copy_bitmap =
+            bdrv_create_dirty_bitmap(source, cluster_size, NULL, errp);
+
+    if (!copy_bitmap) {
+        return NULL;
+    }
+    bdrv_disable_dirty_bitmap(copy_bitmap);
+
+    s = g_new0(BlockCopyState, 1);
+    *s = (BlockCopyState) {
+        .source = blk_new(bdrv_get_aio_context(source),
+                          BLK_PERM_CONSISTENT_READ, no_resize),
+        .target = blk_new(bdrv_get_aio_context(target),
+                          BLK_PERM_WRITE, no_resize),
+        .copy_bitmap = copy_bitmap,
+        .cluster_size = cluster_size,
+        .len = bdrv_dirty_bitmap_size(copy_bitmap),
+        .write_flags = write_flags,
+        .use_copy_range = !(write_flags & BDRV_REQ_WRITE_COMPRESSED),
+        .progress_callback = progress_callback,
+        .progress_opaque = progress_opaque,
+    };
+
+    s->copy_range_size = QEMU_ALIGN_UP(MIN(blk_get_max_transfer(s->source),
+                                           blk_get_max_transfer(s->target)),
+                                       s->cluster_size),
+
+    blk_set_disable_request_queuing(s->source, true);
+    blk_set_allow_aio_context_change(s->source, true);
+    blk_set_disable_request_queuing(s->target, true);
+    blk_set_allow_aio_context_change(s->target, true);
+
+    ret = blk_insert_bs(s->source, source, errp);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    ret = blk_insert_bs(s->target, target, errp);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    return s;
+
+fail:
+    block_copy_state_free(s);
+
+    return NULL;
+}
+
+/*
+ * 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 is_write_notifier,
+        bool *error_is_read, void **bounce_buffer)
+{
+    int ret;
+    int nbytes;
+    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
+
+    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 = blk_blockalign(s->source, s->cluster_size);
+    }
+
+    ret = blk_co_pread(s->source, start, nbytes, *bounce_buffer, read_flags);
+    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 = blk_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, bool is_write_notifier)
+{
+    int ret;
+    int nr_clusters;
+    int nbytes;
+    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
+
+    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 - start, 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 = blk_co_copy_range(s->source, start, s->target, start, nbytes,
+                            read_flags, 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 = blk_bs(s->source);
+    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_callback(0, s->progress_opaque);
+    }
+
+    *count = bytes;
+    return ret;
+}
+
+int coroutine_fn block_copy(
+        BlockCopyState *s, int64_t offset, uint64_t bytes, bool *error_is_read,
+        bool is_write_notifier)
+{
+    int ret = 0;
+    int64_t start = offset, end = bytes + offset; /* bytes */
+    void *bounce_buffer = NULL;
+    int64_t status_bytes;
+
+    assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+    assert(QEMU_IS_ALIGNED(end, s->cluster_size));
+
+    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,
+                                          is_write_notifier);
+            if (ret < 0) {
+                s->use_copy_range = false;
+            }
+        }
+        if (!s->use_copy_range) {
+            ret = block_copy_with_bounce_buffer(s, start, dirty_end,
+                                                is_write_notifier,
+                                                error_is_read, &bounce_buffer);
+        }
+        if (ret < 0) {
+            break;
+        }
+
+        start += ret;
+        s->progress_callback(ret, s->progress_opaque);
+        ret = 0;
+    }
+
+    if (bounce_buffer) {
+        qemu_vfree(bounce_buffer);
+    }
+
+    return ret;
+}
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 35f3bca4d9..0b5c635fb2 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -37,6 +37,7 @@ 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
 
diff --git a/block/trace-events b/block/trace-events
index ad1454f539..ad5261836a 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -40,6 +40,8 @@ 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"
+
+# block-copy.c
 block_copy_skip(void *job, int64_t start) "job %p start %"PRId64
 block_copy_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
 block_copy_process(void *job, int64_t start) "job %p start %"PRId64
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 06/13] block: teach bdrv_debug_breakpoint skip filters with backing
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (4 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 16:21   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter Vladimir Sementsov-Ogievskiy
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Teach bdrv_debug_breakpoint and bdrv_debug_remove_breakpoint skip
filters with backing. This is needed to implement and use in backup job
it's own backup_top filter driver (like mirror already has one), and
without this improvement, breakpoint removal will fail at least in 55
iotest.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block.c | 34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/block.c b/block.c
index 5944124845..1c7c199849 100644
--- a/block.c
+++ b/block.c
@@ -5164,14 +5164,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 +5201,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);
     }
 
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (5 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 06/13] block: teach bdrv_debug_breakpoint skip filters with backing Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 16:40   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 08/13] iotests: 257: drop unused Drive.device field Vladimir Sementsov-Ogievskiy
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

After backup-top filter appearing it's not possible to see dirty
bitmaps in top node, so use node-name instead.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/124        |   3 +-
 tests/qemu-iotests/257        |  39 +---
 tests/qemu-iotests/257.out    | 364 +++++++++++++---------------------
 tests/qemu-iotests/iotests.py |  22 ++
 4 files changed, 173 insertions(+), 255 deletions(-)

diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 3440f54781..8b6024769c 100755
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -749,8 +749,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
 
         # 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]
+        ret = self.vm.get_bitmap(None, drive0['id'], bitmap.name)
         self.assert_qmp(ret, 'count', 458752)
         self.assert_qmp(ret, 'granularity', 65536)
         self.assert_qmp(ret, 'status', 'frozen')
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index c2a72c577a..8c3e96a7fc 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -188,25 +188,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}
@@ -249,7 +230,7 @@ def perform_writes(drive, n):
             pattern.size)
         log(cmd)
         log(drive.vm.hmp_qemu_io(drive.name, cmd))
-    bitmaps = query_bitmaps(drive.vm)
+    bitmaps = drive.vm.query_bitmaps()
     log(bitmaps, indent=2)
     log('')
     return bitmaps
@@ -370,7 +351,7 @@ 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(bitmaps, drive0.node, 'bitmap0')
         ebitmap.compare(bitmap)
         reference_backup(drive0, 1, fbackup1)
 
@@ -388,11 +369,11 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
             log('')
             bitmaps = perform_writes(drive0, 2)
             # Named bitmap (static, should be unchanged)
-            ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+            ebitmap.compare(vm.get_bitmap(bitmaps, drive0.node, 'bitmap0'))
             # Anonymous bitmap (dynamic, shows new writes)
             anonymous = EmulatedBitmap()
             anonymous.dirty_group(2)
-            anonymous.compare(get_bitmap(bitmaps, drive0.device, '',
+            anonymous.compare(vm.get_bitmap(bitmaps, drive0.node, '',
                                          recording=True))
 
             # Simulate the order in which this will happen:
@@ -405,7 +386,7 @@ 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)
+        bitmaps = vm.query_bitmaps()
         log(bitmaps, indent=2)
         log('')
 
@@ -423,29 +404,29 @@ 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(bitmaps, drive0.node, 'bitmap0'))
 
         # 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(bitmaps, drive0.node, 'bitmap0'))
         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)
+        bitmaps = vm.query_bitmaps()
         log(bitmaps, indent=2)
         log('')
         if bsync_mode != 'never':
             ebitmap.clear()
-        ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+        ebitmap.compare(vm.get_bitmap(bitmaps, drive0.node, 'bitmap0'))
 
         log('--- Cleanup ---\n')
         vm.qmp_log("block-dirty-bitmap-remove",
                    node=drive0.name, name="bitmap0")
-        log(query_bitmaps(vm), indent=2)
+        log(vm.query_bitmaps(), indent=2)
         vm.shutdown()
         log('')
 
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
index 84b79d7bfe..c9b4b68232 100644
--- a/tests/qemu-iotests/257.out
+++ b/tests/qemu-iotests/257.out
@@ -19,9 +19,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -55,7 +53,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -111,7 +109,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -153,7 +151,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 +180,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 983040,
@@ -231,7 +229,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 +251,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -285,9 +281,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -321,7 +315,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -370,7 +364,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +393,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 917504,
@@ -448,7 +442,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 +464,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -502,9 +494,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -538,7 +528,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -594,7 +584,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -636,7 +626,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 +655,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 983040,
@@ -714,7 +704,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 +726,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -768,9 +756,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -804,7 +790,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -860,7 +846,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -902,7 +888,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 +917,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 983040,
@@ -980,7 +966,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 +988,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -1034,9 +1018,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -1070,7 +1052,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -1119,7 +1101,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +1130,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 917504,
@@ -1197,7 +1179,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 +1201,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -1251,9 +1231,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -1287,7 +1265,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -1343,7 +1321,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -1385,7 +1363,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 +1392,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -1463,7 +1441,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 +1463,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -1517,9 +1493,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -1553,7 +1527,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -1609,7 +1583,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -1651,7 +1625,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 +1654,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -1729,7 +1703,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 +1725,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -1783,9 +1755,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -1819,7 +1789,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -1868,7 +1838,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +1867,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 851968,
@@ -1946,7 +1916,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 +1938,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -2000,9 +1968,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -2036,7 +2002,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -2092,7 +2058,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -2134,7 +2100,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 +2129,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -2212,7 +2178,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 +2200,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -2266,9 +2230,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -2302,7 +2264,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -2358,7 +2320,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -2400,7 +2362,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 +2391,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 983040,
@@ -2478,7 +2440,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 +2462,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -2532,9 +2492,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -2568,7 +2526,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -2617,7 +2575,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +2604,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 917504,
@@ -2695,7 +2653,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 +2675,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -2749,9 +2705,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -2785,7 +2739,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -2841,7 +2795,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -2883,7 +2837,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 +2866,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -2961,7 +2915,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 +2937,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -3015,9 +2967,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -3051,7 +3001,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -3107,7 +3057,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -3149,7 +3099,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 +3128,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -3227,7 +3177,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 +3199,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -3281,9 +3229,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -3317,7 +3263,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -3366,7 +3312,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +3341,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 66453504,
@@ -3444,7 +3390,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 +3412,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -3498,9 +3442,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -3534,7 +3476,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -3590,7 +3532,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -3632,7 +3574,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 +3603,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -3710,7 +3652,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 +3674,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -3764,9 +3704,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -3800,7 +3738,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -3856,7 +3794,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -3898,7 +3836,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 +3865,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 983040,
@@ -3976,7 +3914,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 +3936,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -4030,9 +3966,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -4066,7 +4000,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -4115,7 +4049,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +4078,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 917504,
@@ -4193,7 +4127,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 +4149,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -4247,9 +4179,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -4283,7 +4213,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -4339,7 +4269,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -4381,7 +4311,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 +4340,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -4459,7 +4389,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 +4411,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -4513,9 +4441,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -4549,7 +4475,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -4605,7 +4531,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -4647,7 +4573,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 +4602,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -4725,7 +4651,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 +4673,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -4779,9 +4703,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -4815,7 +4737,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -4864,7 +4786,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"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 +4815,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 917504,
@@ -4942,7 +4864,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 +4886,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
@@ -4996,9 +4916,7 @@ write -P0x6f 0x2000000 0x10000
 write -P0x76 0x3ff0000 0x10000
 {"return": ""}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Reference Backup #0 ---
@@ -5032,7 +4950,7 @@ write -P0x69 0x3fe0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 393216,
@@ -5088,7 +5006,7 @@ write -P0x67 0x3fe0000 0x20000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 0,
@@ -5130,7 +5048,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 +5077,7 @@ write -P0xdd 0x3fc0000 0x10000
 {"return": ""}
 {
   "bitmaps": {
-    "device0": [
+    "drive0": [
       {
         "busy": false,
         "count": 786432,
@@ -5208,7 +5126,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 +5148,7 @@ expecting 0 dirty sectors; have 0. OK!
 {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
 {"return": {}}
 {
-  "bitmaps": {
-    "device0": []
-  }
+  "bitmaps": {}
 }
 
 --- Verification ---
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 84438e837c..9381964d9f 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -643,6 +643,28 @@ class VM(qtest.QEMUQtestMachine):
                 return x
         return None
 
+    def query_bitmaps(self):
+        res = self.qmp("query-named-block-nodes")
+        return {"bitmaps": {device['node-name']: device['dirty-bitmaps']
+                                for device in res['return']
+                                    if 'dirty-bitmaps' in device}}
+
+    def get_bitmap(self, bitmaps, node_name, name, recording=None):
+        """
+        get a specific bitmap from the object returned by query_bitmaps.
+        :param recording: If specified, filter results by the specified value.
+        """
+        if bitmaps is None:
+            bitmaps = self.query_bitmaps()
+
+        for bitmap in bitmaps['bitmaps'][node_name]:
+            if bitmap.get('name', '') == name:
+                if recording is None:
+                    return bitmap
+                elif bitmap.get('recording') == recording:
+                    return bitmap
+        return None
+
 
 index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
 
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 08/13] iotests: 257: drop unused Drive.device field
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (6 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 16:50   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 09/13] iotests: 257: drop device_add Vladimir Sementsov-Ogievskiy
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

After previous commit Drive.device is actually unused. Drop it together
with .name property.  While being here reuse .node in qmp commands
instead of writing 'drive0' twice.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/257 | 37 +++++++++++++++----------------------
 1 file changed, 15 insertions(+), 22 deletions(-)

diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index 8c3e96a7fc..e0f521272b 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
@@ -201,7 +196,7 @@ def blockdev_backup(vm, device, target, sync, **kwargs):
 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))
@@ -229,7 +224,7 @@ def perform_writes(drive, n):
             pattern.offset,
             pattern.size)
         log(cmd)
-        log(drive.vm.hmp_qemu_io(drive.name, cmd))
+        log(drive.vm.hmp_qemu_io(drive.node, cmd))
     bitmaps = drive.vm.query_bitmaps()
     log(bitmaps, indent=2)
     log('')
@@ -324,18 +319,17 @@ 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",
+        vm.qmp_log("device_add", id='device0',
+                   drive=drive0.node, driver="scsi-hd",
                    share_rw=True)
         log('')
 
@@ -343,7 +337,7 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
         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()
@@ -358,7 +352,7 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
         # 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)
@@ -425,7 +419,7 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
 
         log('--- Cleanup ---\n')
         vm.qmp_log("block-dirty-bitmap-remove",
-                   node=drive0.name, name="bitmap0")
+                   node=drive0.node, name="bitmap0")
         log(vm.query_bitmaps(), indent=2)
         vm.shutdown()
         log('')
@@ -465,22 +459,21 @@ 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")
+        vm.qmp_log("device_add", id='device0',
+                   drive=drive0.node, 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('')
 
@@ -519,7 +512,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('')
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 09/13] iotests: 257: drop device_add
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (7 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 08/13] iotests: 257: drop unused Drive.device field Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 16:54   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 10/13] block/io: refactor wait_serialising_requests Vladimir Sementsov-Ogievskiy
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

SCSI devices are unused in test, drop them.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/257     |  8 -------
 tests/qemu-iotests/257.out | 44 --------------------------------------
 2 files changed, 52 deletions(-)

diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index e0f521272b..7bc6069e1b 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -325,12 +325,6 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
                    node_name=drive0.node,
                    driver=drive0.fmt,
                    file=file_config)
-        # 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='device0',
-                   drive=drive0.node, driver="scsi-hd",
-                   share_rw=True)
         log('')
 
         # 0 - Writes and Reference Backup
@@ -465,8 +459,6 @@ def test_backup_api():
                    node_name=drive0.node,
                    driver=drive0.fmt,
                    file=file_config)
-        vm.qmp_log("device_add", id='device0',
-                   drive=drive0.node, driver="scsi-hd")
         log('')
 
         target0 = Drive(backup_path, vm=vm)
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
index c9b4b68232..ec7e25877b 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 ---
 
@@ -267,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 ---
 
@@ -480,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 ---
 
@@ -742,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 ---
 
@@ -1004,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 ---
 
@@ -1217,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 ---
 
@@ -1479,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 ---
 
@@ -1741,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 ---
 
@@ -1954,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 ---
 
@@ -2216,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 ---
 
@@ -2478,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 ---
 
@@ -2691,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 ---
 
@@ -2953,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 ---
 
@@ -3215,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 ---
 
@@ -3428,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 ---
 
@@ -3690,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 ---
 
@@ -3952,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 ---
 
@@ -4165,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 ---
 
@@ -4427,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 ---
 
@@ -4689,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 ---
 
@@ -4902,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 ---
 
@@ -5164,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"}}
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 10/13] block/io: refactor wait_serialising_requests
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (8 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 09/13] iotests: 257: drop device_add Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions Vladimir Sementsov-Ogievskiy
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Split out do_wait_serialising_requests with additional possibility to
not actually wait but just check, that there is something to wait for.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
 block/io.c | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/block/io.c b/block/io.c
index 56bbf195bb..378180b274 100644
--- a/block/io.c
+++ b/block/io.c
@@ -785,12 +785,13 @@ void bdrv_dec_in_flight(BlockDriverState *bs)
     bdrv_wakeup(bs);
 }
 
-static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
+static bool coroutine_fn do_wait_serialising_requests(BdrvTrackedRequest *self,
+                                                      bool wait)
 {
     BlockDriverState *bs = self->bs;
     BdrvTrackedRequest *req;
     bool retry;
-    bool waited = false;
+    bool found = false;
 
     if (!atomic_read(&bs->serialising_in_flight)) {
         return false;
@@ -816,11 +817,13 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
                  * will wait for us as soon as it wakes up, then just go on
                  * (instead of producing a deadlock in the former case). */
                 if (!req->waiting_for) {
-                    self->waiting_for = req;
-                    qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
-                    self->waiting_for = NULL;
-                    retry = true;
-                    waited = true;
+                    found = true;
+                    if (wait) {
+                        self->waiting_for = req;
+                        qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
+                        self->waiting_for = NULL;
+                        retry = true;
+                    }
                     break;
                 }
             }
@@ -828,7 +831,12 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
         qemu_co_mutex_unlock(&bs->reqs_lock);
     } while (retry);
 
-    return waited;
+    return found;
+}
+
+static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
+{
+    return do_wait_serialising_requests(self, true);
 }
 
 static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (9 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 10/13] block/io: refactor wait_serialising_requests Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 17:02   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 12/13] block: introduce backup-top filter driver Vladimir Sementsov-Ogievskiy
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers Vladimir Sementsov-Ogievskiy
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

From: Vladimir Sementsov-Ogievskiy <etendren@gmail.com>

Introduce lock/unlock range functionality, based on serialized
requests. This is needed to refactor backup, dropping local
tracked-request-like synchronization.

Signed-off-by: Vladimir Sementsov-Ogievskiy <etendren@gmail.com>
---
 include/block/block_int.h |  4 ++++
 block/io.c                | 44 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/include/block/block_int.h b/include/block/block_int.h
index ceec8c2f56..0307cc78c6 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -65,6 +65,7 @@ enum BdrvTrackedRequestType {
     BDRV_TRACKED_WRITE,
     BDRV_TRACKED_DISCARD,
     BDRV_TRACKED_TRUNCATE,
+    BDRV_TRACKED_LOCK,
 };
 
 typedef struct BdrvTrackedRequest {
@@ -947,6 +948,9 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
 int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
     int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
     BdrvRequestFlags flags);
+void *coroutine_fn bdrv_co_try_lock(BlockDriverState *bs,
+                                    int64_t offset, unsigned int bytes);
+void coroutine_fn bdrv_co_unlock(void *opaque);
 
 static inline int coroutine_fn bdrv_co_pread(BdrvChild *child,
     int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags)
diff --git a/block/io.c b/block/io.c
index 378180b274..cd88b17e6b 100644
--- a/block/io.c
+++ b/block/io.c
@@ -785,6 +785,15 @@ void bdrv_dec_in_flight(BlockDriverState *bs)
     bdrv_wakeup(bs);
 }
 
+static bool ignore_intersection(BdrvTrackedRequest *a, BdrvTrackedRequest *b)
+{
+    return a == b || (!a->serialising && !b->serialising) ||
+        (a->type == BDRV_TRACKED_LOCK && b->type == BDRV_TRACKED_READ &&
+         !b->serialising) ||
+        (b->type == BDRV_TRACKED_LOCK && a->type == BDRV_TRACKED_READ &&
+         !a->serialising);
+}
+
 static bool coroutine_fn do_wait_serialising_requests(BdrvTrackedRequest *self,
                                                       bool wait)
 {
@@ -801,7 +810,7 @@ static bool coroutine_fn do_wait_serialising_requests(BdrvTrackedRequest *self,
         retry = false;
         qemu_co_mutex_lock(&bs->reqs_lock);
         QLIST_FOREACH(req, &bs->tracked_requests, list) {
-            if (req == self || (!req->serialising && !self->serialising)) {
+            if (ignore_intersection(self, req)) {
                 continue;
             }
             if (tracked_request_overlaps(req, self->overlap_offset,
@@ -839,6 +848,12 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
     return do_wait_serialising_requests(self, true);
 }
 
+static bool coroutine_fn should_wait_serialising_requests(
+        BdrvTrackedRequest *self)
+{
+    return do_wait_serialising_requests(self, false);
+}
+
 static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
                                    size_t size)
 {
@@ -3271,3 +3286,30 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
 
     return tco.ret;
 }
+
+void *coroutine_fn bdrv_co_try_lock(BlockDriverState *bs,
+                                    int64_t offset, unsigned int bytes)
+{
+    BdrvTrackedRequest *req = g_new(BdrvTrackedRequest, 1);
+
+    tracked_request_begin(req, bs, offset, bytes, BDRV_TRACKED_LOCK);
+    mark_request_serialising(req, bdrv_get_cluster_size(bs));
+
+    if (should_wait_serialising_requests(req)) {
+        tracked_request_end(req);
+        g_free(req);
+        return NULL;
+    }
+
+    return req;
+}
+
+void coroutine_fn bdrv_co_unlock(void *opaque)
+{
+    BdrvTrackedRequest *req = opaque;
+
+    assert(req->type == BDRV_TRACKED_LOCK);
+
+    tracked_request_end(req);
+    g_free(req);
+}
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 12/13] block: introduce backup-top filter driver
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (10 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 17:52   ` Max Reitz
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers Vladimir Sementsov-Ogievskiy
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Backup-top filter caches write operations and does copy-before-write
operations.

The driver will be used in backup instead of write-notifiers.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/backup-top.h  |  37 +++++++
 block/backup-top.c  | 245 ++++++++++++++++++++++++++++++++++++++++++++
 block/Makefile.objs |   2 +
 3 files changed, 284 insertions(+)
 create mode 100644 block/backup-top.h
 create mode 100644 block/backup-top.c

diff --git a/block/backup-top.h b/block/backup-top.h
new file mode 100644
index 0000000000..67de7a9133
--- /dev/null
+++ b/block/backup-top.h
@@ -0,0 +1,37 @@
+/*
+ * 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"
+
+BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
+                                         const char *filter_node_name,
+                                         Error **errp);
+void bdrv_backup_top_set_bcs(BlockDriverState *bs, BlockCopyState *copy_state);
+void bdrv_backup_top_drop(BlockDriverState *bs);
+
+#endif /* BACKUP_TOP_H */
diff --git a/block/backup-top.c b/block/backup-top.c
new file mode 100644
index 0000000000..41cb015452
--- /dev/null
+++ b/block/backup-top.c
@@ -0,0 +1,245 @@
+/*
+ * 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;
+    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)
+{
+    /*
+     * Here we'd like to use block_copy(), but it needs some additional
+     * synchronization mechanism to prevent intersecting guest writes during
+     * copy operation. The will appear in further commit (it should be done
+     * together with moving backup to using of backup-top and to the same
+     * synchronization mechanism), and for now it is a TODO.
+     */
+
+    abort();
+}
+
+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;
+    }
+    bdrv_refresh_filename(bs->backing->bs);
+    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;
+    }
+
+    bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
+                              nperm, nshared);
+
+    *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,
+                                         const char *filter_node_name,
+                                         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_drained_begin(source);
+
+    bdrv_ref(top);
+    bdrv_append(top, source, &local_err);
+    if (local_err) {
+        error_prepend(&local_err, "Cannot append backup-top filter: ");
+    } else {
+        /*
+         * 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) {
+            state->active = false;
+            bdrv_backup_top_drop(top);
+            error_prepend(&local_err,
+                          "Cannot set permissions for backup-top filter: ");
+        }
+    }
+
+    bdrv_drained_end(source);
+
+    if (local_err) {
+        bdrv_unref(top);
+        error_propagate(errp, local_err);
+        return NULL;
+    }
+
+    assert(!(top->backing->shared_perm & BLK_PERM_WRITE));
+
+    return top;
+}
+
+void bdrv_backup_top_set_bcs(BlockDriverState *bs, BlockCopyState *copy_state)
+{
+    BDRVBackupTopState *s = bs->opaque;
+
+    assert(blk_bs(copy_state->source) == bs->backing->bs);
+    s->bcs = copy_state;
+}
+
+void bdrv_backup_top_drop(BlockDriverState *bs)
+{
+    AioContext *aio_context = bdrv_get_aio_context(bs);
+
+    aio_context_acquire(aio_context);
+
+    bdrv_drained_begin(bs);
+
+    bdrv_child_try_set_perm(bs->backing, 0, BLK_PERM_ALL, &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/Makefile.objs b/block/Makefile.objs
index 0b5c635fb2..6f348c56c9 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -41,6 +41,8 @@ block-obj-y += block-copy.o
 
 block-obj-y += crypto.o
 
+block-obj-y += backup-top.o
+
 common-obj-y += stream.o
 
 nfs.o-libs         := $(LIBNFS_LIBS)
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers
  2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
                   ` (11 preceding siblings ...)
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 12/13] block: introduce backup-top filter driver Vladimir Sementsov-Ogievskiy
@ 2019-08-26 16:13 ` Vladimir Sementsov-Ogievskiy
  2019-08-28 19:50   ` Max Reitz
  12 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-26 16:13 UTC (permalink / raw)
  To: qemu-block
  Cc: fam, kwolf, vsementsov, wencongyang2, xiechanglong.d, qemu-devel,
	armbru, jsnow, stefanha, den, mreitz

Drop write notifiers and use filter node instead.

= Changes =

1. add filter-node-name argument for backup qmp api. We have to do it
in this commit, as 257 needs to be fixed.

2. there no move write notifiers here, so is_write_notifier parameter
is dropped from block-copy paths.

3. Intersecting requests handling changed, now synchronization between
backup-top, backup and guest writes are all done in block/block-copy.c
and works as follows:

On copy operation, we work only with dirty areas. If bits are dirty it
means that there are no requests intersecting with this area. We clear
dirty bits and take bdrv range lock (bdrv_co_try_lock) on this area to
prevent further operations from interaction with guest (only with
guest, as neither backup nor backup-top will touch non-dirty area). If
copy-operation failed we set dirty bits back together with releasing
the lock.

The actual difference with old scheme is that on guest writes we
don't lock the whole region but only dirty-parts, and to be more
precise: only dirty-part we are currently operate on. In old scheme
guest write to non-dirty area (which may be safely ignored by backup)
may wait for intersecting request, touching some other area which is
dirty.

4. To sync with in-flight requests at job finish we now have drained
removing of the filter, we don't need rw-lock.

= Notes =

Note the consequence of three objects appearing: backup-top, backup job
and block-copy-state:

1. We want to insert backup-top before job creation, to behave similar
with mirror and commit, where job is started upon filter.

2. We also have to create block-copy-state after filter injection, as
we don't want it's source child be replaced by fitler. Instead we want
to keep BCS.source to be real source node, as we want to use
bdrv_co_try_lock in CBW operations and it can't be used on filter, as
on filter we already have in-flight (write) request from upper layer.

So, we firstly create inject backup-top, then create job and BCS. BCS
is the latest just to not create extra variable for it. Finally we set
bcs for backup-top filter.

= Iotest changes =

56: op-blocker doesn't shot now, as we set it on source, but then check
on filter, when trying to start second backup, so error caught in
test_dismiss_collision is changed. It's OK anyway, as this test-case
seems to test that after some collision we can dismiss first job and
successfully start the second one. So, the change is that collision is
changed from op-blocker to file-posix locks.
However, it's obvious now that we'd better drop this op-blocker at all
and add a test-case for two backups from one node (to different
destinations) actually works. But not in these series.

257: The test wants to emulate guest write during backup. They should
go to filter node, not to original source node, of course. Therefore we
need to specify filter node name and use it.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 qapi/block-core.json       |   8 +-
 include/block/block-copy.h |   2 +-
 include/block/block_int.h  |   1 +
 block/backup-top.c         |  14 +-
 block/backup.c             | 113 +++-----------
 block/block-copy.c         |  55 ++++---
 block/replication.c        |   2 +-
 blockdev.c                 |   1 +
 tests/qemu-iotests/056     |   2 +-
 tests/qemu-iotests/257     |   7 +-
 tests/qemu-iotests/257.out | 306 ++++++++++++++++++-------------------
 11 files changed, 230 insertions(+), 281 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index e6edd641f1..b5cd00c361 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1391,6 +1391,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 +1409,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/include/block/block-copy.h b/include/block/block-copy.h
index 0dd7a3f7bf..c15d47a70d 100644
--- a/include/block/block-copy.h
+++ b/include/block/block-copy.h
@@ -54,6 +54,6 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
                                      int64_t offset, int64_t *count);
 
 int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, uint64_t bytes,
-                            bool *error_is_read, bool is_write_notifier);
+                            bool *error_is_read);
 
 #endif /* BLOCK_COPY_H */
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 0307cc78c6..27c4bf4673 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -1179,6 +1179,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/block/backup-top.c b/block/backup-top.c
index 41cb015452..cac46f05b1 100644
--- a/block/backup-top.c
+++ b/block/backup-top.c
@@ -49,15 +49,11 @@ static coroutine_fn int backup_top_co_preadv(
 static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
                                        uint64_t bytes)
 {
-    /*
-     * Here we'd like to use block_copy(), but it needs some additional
-     * synchronization mechanism to prevent intersecting guest writes during
-     * copy operation. The will appear in further commit (it should be done
-     * together with moving backup to using of backup-top and to the same
-     * synchronization mechanism), and for now it is a TODO.
-     */
-
-    abort();
+    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,
diff --git a/block/backup.c b/block/backup.c
index d927c63e5a..259a165405 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)
@@ -27,14 +28,9 @@
 #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;
@@ -45,55 +41,15 @@ typedef struct BackupBlockJob {
     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;
 
     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)
-{
-    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);
-}
-
-/* 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);
-}
-
-/* Forget about a completed request */
-static void cow_request_end(CowRequest *req)
-{
-    QLIST_REMOVE(req, list);
-    qemu_co_queue_restart_all(&req->wait_queue);
-}
-
 static void backup_progress_callback(int64_t bytes, void *opaque)
 {
     BackupBlockJob *s = opaque;
@@ -110,49 +66,23 @@ static void backup_progress_callback(int64_t bytes, void *opaque)
 
 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 */
 
-    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);
-
-    ret = block_copy(job->bcs, start, end - start, error_is_read,
-                     is_write_notifier);
-
-    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;
@@ -202,7 +132,9 @@ static void backup_abort(Job *job)
 static void backup_clean(Job *job)
 {
     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
+    BlockDriverState *backup_top = blk_bs(s->common.blk);
 
+    bdrv_backup_top_drop(backup_top);
     block_copy_state_free(s->bcs);
     s->bcs = NULL;
 }
@@ -283,8 +215,7 @@ static int coroutine_fn backup_loop(BackupBlockJob *job)
             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)
             {
@@ -326,17 +257,10 @@ static void backup_init_copy_bitmap(BackupBlockJob *job)
 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;
@@ -370,12 +294,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;
 }
 
@@ -435,6 +353,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,
@@ -444,6 +363,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
     int64_t len;
     BackupBlockJob *job = NULL;
     int64_t cluster_size;
+    BlockDriverState *backup_top = NULL;
 
     assert(bs);
     assert(target);
@@ -508,8 +428,14 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         goto error;
     }
 
+    backup_top = bdrv_backup_top_append(bs, filter_node_name, errp);
+    if (!backup_top) {
+        goto error;
+    }
+
     /* job->len is fixed, so we can't allow resize */
-    job = block_job_create(job_id, &backup_job_driver, txn, bs, 0, BLK_PERM_ALL,
+    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;
@@ -537,6 +463,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 
     job->cluster_size = cluster_size;
 
+    bdrv_backup_top_set_bcs(backup_top, job->bcs);
+
     /* Required permissions are already taken with target's blk_new() */
     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
                        &error_abort);
@@ -552,6 +480,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         backup_clean(&job->common.job);
         job_early_fail(&job->common.job);
     }
+    if (backup_top) {
+        bdrv_backup_top_drop(backup_top);
+    }
 
     return NULL;
 }
diff --git a/block/block-copy.c b/block/block-copy.c
index 6828c46ba0..f3102ec3ff 100644
--- a/block/block-copy.c
+++ b/block/block-copy.c
@@ -98,27 +98,32 @@ fail:
  * 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 is_write_notifier,
+        BlockCopyState *s, int64_t start, int64_t end,
         bool *error_is_read, void **bounce_buffer)
 {
     int ret;
-    int nbytes;
-    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
+    int nbytes = MIN(s->cluster_size, s->len - start);
+    void *lock = bdrv_co_try_lock(blk_bs(s->source), start, nbytes);
+
+    /*
+     * Function must be called only on full-dirty region, so nobody touching or
+     * touched these bytes. Therefore, we must successfully get lock.
+     */
+    assert(lock);
 
     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 = blk_blockalign(s->source, s->cluster_size);
     }
 
-    ret = blk_co_pread(s->source, start, nbytes, *bounce_buffer, read_flags);
+    ret = blk_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;
+        goto out;
     }
 
     ret = blk_co_pwrite(s->target, start, nbytes, *bounce_buffer,
@@ -128,13 +133,16 @@ static int coroutine_fn block_copy_with_bounce_buffer(
         if (error_is_read) {
             *error_is_read = false;
         }
-        goto fail;
+        goto out;
     }
 
-    return nbytes;
-fail:
-    bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
-    return ret;
+out:
+    if (ret < 0) {
+        bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
+    }
+    bdrv_co_unlock(lock);
+
+    return ret < 0 ? ret : nbytes;
 
 }
 
@@ -143,29 +151,37 @@ fail:
  * negative error number.
  */
 static int coroutine_fn block_copy_with_offload(
-        BlockCopyState *s, int64_t start, int64_t end, bool is_write_notifier)
+        BlockCopyState *s, int64_t start, int64_t end)
 {
     int ret;
     int nr_clusters;
     int nbytes;
-    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
+    void *lock;
 
     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 - start, s->len - start));
+    lock = bdrv_co_try_lock(blk_bs(s->source), start, nbytes);
+    /*
+     * Function must be called only on full-dirty region, so nobody touching or
+     * touched these bytes. Therefore, we must successfully get lock.
+     */
+    assert(lock);
+
     nr_clusters = DIV_ROUND_UP(nbytes, s->cluster_size);
     bdrv_reset_dirty_bitmap(s->copy_bitmap, start,
                             s->cluster_size * nr_clusters);
     ret = blk_co_copy_range(s->source, start, s->target, start, nbytes,
-                            read_flags, s->write_flags);
+                            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;
+    bdrv_co_unlock(lock);
+
+    return ret < 0 ? ret : nbytes;
 }
 
 /*
@@ -239,8 +255,7 @@ int64_t block_copy_reset_unallocated(
 }
 
 int coroutine_fn block_copy(
-        BlockCopyState *s, int64_t offset, uint64_t bytes, bool *error_is_read,
-        bool is_write_notifier)
+        BlockCopyState *s, int64_t offset, uint64_t bytes, bool *error_is_read)
 {
     int ret = 0;
     int64_t start = offset, end = bytes + offset; /* bytes */
@@ -279,15 +294,13 @@ int coroutine_fn block_copy(
         trace_block_copy_process(s, start);
 
         if (s->use_copy_range) {
-            ret = block_copy_with_offload(s, start, dirty_end,
-                                          is_write_notifier);
+            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,
-                                                is_write_notifier,
                                                 error_is_read, &bounce_buffer);
         }
         if (ret < 0) {
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/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/tests/qemu-iotests/056 b/tests/qemu-iotests/056
index e761e465ae..9c7c653320 100755
--- a/tests/qemu-iotests/056
+++ b/tests/qemu-iotests/056
@@ -253,7 +253,7 @@ 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='Failed to get "write" lock',
                                        device='drive0', format=iotests.imgfmt,
                                        sync='full', target=self.dest_img,
                                        auto_dismiss=False)
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index 7bc6069e1b..2dbcc6e7e1 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -190,6 +190,7 @@ def blockdev_backup(vm, device, target, sync, **kwargs):
                         device=device,
                         target=target,
                         sync=sync,
+                        filter_node_name='backup-top',
                         **kwargs)
     return result
 
@@ -216,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(
@@ -224,7 +225,7 @@ def perform_writes(drive, n):
             pattern.offset,
             pattern.size)
         log(cmd)
-        log(drive.vm.hmp_qemu_io(drive.node, cmd))
+        log(drive.vm.hmp_qemu_io(filter_node_name or drive.node, cmd))
     bitmaps = drive.vm.query_bitmaps()
     log(bitmaps, indent=2)
     log('')
@@ -355,7 +356,7 @@ 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(vm.get_bitmap(bitmaps, drive0.node, 'bitmap0'))
             # Anonymous bitmap (dynamic, shows new writes)
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
index ec7e25877b..64dd460055 100644
--- a/tests/qemu-iotests/257.out
+++ b/tests/qemu-iotests/257.out
@@ -30,7 +30,7 @@ write -P0x76 0x3ff0000 0x10000
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -78,7 +78,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -92,7 +92,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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 ---
@@ -205,7 +205,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -219,7 +219,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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": {}}
@@ -290,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"}}
 
@@ -338,7 +338,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -354,7 +354,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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"}}
@@ -416,7 +416,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -430,7 +430,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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": {}}
@@ -501,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"}}
 
@@ -549,7 +549,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -563,7 +563,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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 ---
@@ -676,7 +676,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -690,7 +690,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "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": {}}
@@ -761,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"}}
 
@@ -809,7 +809,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -823,7 +823,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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 ---
@@ -936,7 +936,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -950,7 +950,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -1021,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"}}
 
@@ -1069,7 +1069,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1085,7 +1085,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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"}}
@@ -1147,7 +1147,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1161,7 +1161,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -1232,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"}}
 
@@ -1280,7 +1280,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1294,7 +1294,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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 ---
@@ -1407,7 +1407,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1421,7 +1421,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -1492,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"}}
 
@@ -1540,7 +1540,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1554,7 +1554,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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 ---
@@ -1667,7 +1667,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1681,7 +1681,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -1752,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"}}
 
@@ -1800,7 +1800,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1816,7 +1816,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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"}}
@@ -1878,7 +1878,7 @@ expecting 13 dirty sectors; have 13. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -1892,7 +1892,7 @@ expecting 13 dirty sectors; have 13. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -1963,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"}}
 
@@ -2011,7 +2011,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2025,7 +2025,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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 ---
@@ -2138,7 +2138,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2152,7 +2152,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -2223,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"}}
 
@@ -2271,7 +2271,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2285,7 +2285,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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 ---
@@ -2398,7 +2398,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2412,7 +2412,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -2483,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"}}
 
@@ -2531,7 +2531,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2547,7 +2547,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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"}}
@@ -2609,7 +2609,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2623,7 +2623,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -2694,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"}}
 
@@ -2742,7 +2742,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2756,7 +2756,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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 ---
@@ -2869,7 +2869,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -2883,7 +2883,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -2954,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"}}
 
@@ -3002,7 +3002,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3016,7 +3016,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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 ---
@@ -3129,7 +3129,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3143,7 +3143,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -3214,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"}}
 
@@ -3262,7 +3262,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3278,7 +3278,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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"}}
@@ -3340,7 +3340,7 @@ expecting 1014 dirty sectors; have 1014. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3354,7 +3354,7 @@ expecting 1014 dirty sectors; have 1014. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -3425,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"}}
 
@@ -3473,7 +3473,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3487,7 +3487,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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 ---
@@ -3600,7 +3600,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3614,7 +3614,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -3685,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"}}
 
@@ -3733,7 +3733,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3747,7 +3747,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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 ---
@@ -3860,7 +3860,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -3874,7 +3874,7 @@ expecting 15 dirty sectors; have 15. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -3945,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"}}
 
@@ -3993,7 +3993,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4009,7 +4009,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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"}}
@@ -4071,7 +4071,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4085,7 +4085,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -4156,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"}}
 
@@ -4204,7 +4204,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4218,7 +4218,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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 ---
@@ -4331,7 +4331,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4345,7 +4345,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "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": {}}
@@ -4416,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"}}
 
@@ -4464,7 +4464,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4478,7 +4478,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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 ---
@@ -4591,7 +4591,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4605,7 +4605,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -4676,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"}}
 
@@ -4724,7 +4724,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4740,7 +4740,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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"}}
@@ -4802,7 +4802,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4816,7 +4816,7 @@ expecting 14 dirty sectors; have 14. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -4887,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"}}
 
@@ -4935,7 +4935,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -4949,7 +4949,7 @@ expecting 6 dirty sectors; have 6. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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 ---
@@ -5062,7 +5062,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "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"}}
 
@@ -5076,7 +5076,7 @@ expecting 12 dirty sectors; have 12. OK!
 {"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
 {"return": {}}
 {}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "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": {}}
@@ -5139,155 +5139,155 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
 
 -- Sync mode incremental tests --
 
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "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"}}
 
-- 
2.18.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster Vladimir Sementsov-Ogievskiy
@ 2019-08-28 14:08   ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-08-28 14:08 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 1093 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> We shouldn't try to copy bytes beyond EOF. Fix it.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block/backup.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/block/backup.c b/block/backup.c
> index 2baf7bed65..33b144305f 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -161,7 +161,7 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
>  
>      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);
> +    nbytes = MIN(job->copy_range_size, MIN(end - start, job->len - start));

Might be easier to read as MIN(end, job->len) - start, but either way:

Reviewed-by: Max Reitz <mreitz@redhat.com>

>      nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
>      bdrv_reset_dirty_bitmap(job->copy_bitmap, start,
>                              job->cluster_size * nr_clusters);
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow Vladimir Sementsov-Ogievskiy
@ 2019-08-28 14:22   ` Max Reitz
  2019-08-28 14:27     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 14:22 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 2073 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Split copying logic which will be shared with backup-top filter.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block/backup.c | 47 ++++++++++++++++++++++++++++++++---------------
>  1 file changed, 32 insertions(+), 15 deletions(-)
> 
> diff --git a/block/backup.c b/block/backup.c
> index 33b144305f..13a1d80157 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -248,26 +248,18 @@ static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
>      return ret;
>  }
>  
> -static int coroutine_fn backup_do_cow(BackupBlockJob *job,
> -                                      int64_t offset, uint64_t bytes,
> -                                      bool *error_is_read,
> -                                      bool is_write_notifier)
> +static int coroutine_fn backup_do_copy(BackupBlockJob *job,
> +                                       int64_t offset, uint64_t bytes,
> +                                       bool *error_is_read,
> +                                       bool is_write_notifier)
>  {
> -    CowRequest cow_request;
>      int ret = 0;
> -    int64_t start, end; /* bytes */
> +    int64_t start = offset, end = bytes + offset; /* bytes */

Maybe just rename the “offset” parameter to “start”, replace the “bytes”
parameter by an “end” parameter, and drop this line?

Max

>      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);
> +    assert(QEMU_IS_ALIGNED(start, job->cluster_size));
> +    assert(QEMU_IS_ALIGNED(end, job->cluster_size));
>  
>      while (start < end) {
>          int64_t dirty_end;


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow
  2019-08-28 14:22   ` Max Reitz
@ 2019-08-28 14:27     ` Vladimir Sementsov-Ogievskiy
  2019-08-28 14:32       ` Max Reitz
  0 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-28 14:27 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

28.08.2019 17:22, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> Split copying logic which will be shared with backup-top filter.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   block/backup.c | 47 ++++++++++++++++++++++++++++++++---------------
>>   1 file changed, 32 insertions(+), 15 deletions(-)
>>
>> diff --git a/block/backup.c b/block/backup.c
>> index 33b144305f..13a1d80157 100644
>> --- a/block/backup.c
>> +++ b/block/backup.c
>> @@ -248,26 +248,18 @@ static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
>>       return ret;
>>   }
>>   
>> -static int coroutine_fn backup_do_cow(BackupBlockJob *job,
>> -                                      int64_t offset, uint64_t bytes,
>> -                                      bool *error_is_read,
>> -                                      bool is_write_notifier)
>> +static int coroutine_fn backup_do_copy(BackupBlockJob *job,
>> +                                       int64_t offset, uint64_t bytes,
>> +                                       bool *error_is_read,
>> +                                       bool is_write_notifier)
>>   {
>> -    CowRequest cow_request;
>>       int ret = 0;
>> -    int64_t start, end; /* bytes */
>> +    int64_t start = offset, end = bytes + offset; /* bytes */
> 
> Maybe just rename the “offset” parameter to “start”, replace the “bytes”
> parameter by an “end” parameter, and drop this line?
> 

I really want final block_copy have more common in block-layer offset+bytes
interface. So better to refactor a bit the function itself, but I'd prefer
to do it as a follow-up and keep this patch simpler..

> 
>>       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);
>> +    assert(QEMU_IS_ALIGNED(start, job->cluster_size));
>> +    assert(QEMU_IS_ALIGNED(end, job->cluster_size));
>>   
>>       while (start < end) {
>>           int64_t dirty_end;
> 


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow
  2019-08-28 14:27     ` Vladimir Sementsov-Ogievskiy
@ 2019-08-28 14:32       ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-08-28 14:32 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha


[-- Attachment #1.1: Type: text/plain, Size: 1893 bytes --]

On 28.08.19 16:27, Vladimir Sementsov-Ogievskiy wrote:
> 28.08.2019 17:22, Max Reitz wrote:
>> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>>> Split copying logic which will be shared with backup-top filter.
>>>
>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>>> ---
>>>   block/backup.c | 47 ++++++++++++++++++++++++++++++++---------------
>>>   1 file changed, 32 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/block/backup.c b/block/backup.c
>>> index 33b144305f..13a1d80157 100644
>>> --- a/block/backup.c
>>> +++ b/block/backup.c
>>> @@ -248,26 +248,18 @@ static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
>>>       return ret;
>>>   }
>>>   
>>> -static int coroutine_fn backup_do_cow(BackupBlockJob *job,
>>> -                                      int64_t offset, uint64_t bytes,
>>> -                                      bool *error_is_read,
>>> -                                      bool is_write_notifier)
>>> +static int coroutine_fn backup_do_copy(BackupBlockJob *job,
>>> +                                       int64_t offset, uint64_t bytes,
>>> +                                       bool *error_is_read,
>>> +                                       bool is_write_notifier)
>>>   {
>>> -    CowRequest cow_request;
>>>       int ret = 0;
>>> -    int64_t start, end; /* bytes */
>>> +    int64_t start = offset, end = bytes + offset; /* bytes */
>>
>> Maybe just rename the “offset” parameter to “start”, replace the “bytes”
>> parameter by an “end” parameter, and drop this line?
>>
> 
> I really want final block_copy have more common in block-layer offset+bytes
> interface. So better to refactor a bit the function itself, but I'd prefer
> to do it as a follow-up and keep this patch simpler..

OK, but s/offset/start/ should still be possible.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState Vladimir Sementsov-Ogievskiy
@ 2019-08-28 15:59   ` Max Reitz
  2019-08-29 10:52     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 15:59 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 10124 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Split copying code part from backup to "block-copy", including separate
> state structure and function renaming. This is needed to share it with
> backup-top filter driver in further commits.
> 
> Notes:
> 
> 1. As BlockCopyState keeps own BlockBackend objects, remaining

I suppose these should be BdrvChild objects at some point, but doing it
now would just mean effectively duplicating code from block-backend.c.
(“now” = before we have a backup-top filter to attach the children to.)

> job->common.blk users only use it to get bs by blk_bs() call, so clear
> job->commen.blk permissions set in block_job_create.
> 
> 2. Rename s/initializing_bitmap/skip_unallocated/ to sound a bit better
> as interface to BlockCopyState
> 
> 3. Split is not very clean: there left some duplicated fields, backup

Are there any but cluster_size and len (and source, in a sense)?

> code uses some BlockCopyState fields directly, let's postpone it for
> further improvements and keep this comment simpler for review.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block/backup.c     | 324 +++++++++++++++++++++++++++------------------
>  block/trace-events |  12 +-
>  2 files changed, 200 insertions(+), 136 deletions(-)
> 
> diff --git a/block/backup.c b/block/backup.c
> index 13a1d80157..f52ac622e0 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -35,12 +35,35 @@ typedef struct CowRequest {
>      CoQueue wait_queue; /* coroutines blocked on this request */
>  } CowRequest;
>  
> +/*
> + * ProgressCallbackFunc
> + *
> + * Called when some progress is done in context of BlockCopyState:
> + *  1. When some bytes copied, called with @bytes > 0.
> + *  2. When some bytes resetted from copy_bitmap, called with @bytes = 0 (user

*reset

> + *     may recalculate remaining bytes from copy_bitmap dirty count.
> + */
> +typedef void (*ProgressCallbackFunc)(int64_t bytes, void *opaque);

Maybe there should be two callbacks instead, one for “We’ve actively
made progress” (bytes > 0) and one for “The expected length has changed”
(bytes == 0)?

> +typedef struct BlockCopyState {
> +    BlockBackend *source;
> +    BlockBackend *target;
> +    BdrvDirtyBitmap *copy_bitmap;
> +    int64_t cluster_size;
> +    bool use_copy_range;
> +    int64_t copy_range_size;
> +    uint64_t len;
> +
> +    BdrvRequestFlags write_flags;
> +    bool skip_unallocated;

The rename seems reasonable, although I think this should get a comment,
because it doesn’t mean just to skip unallocated clusters; it also means
to clear unallocated clusters from the bitmap.

> +
> +    ProgressCallbackFunc progress_callback;
> +    void *progress_opaque;
> +} BlockCopyState;
> +
>  typedef struct BackupBlockJob {
>      BlockJob common;
> -    BlockBackend *target;
>  
>      BdrvDirtyBitmap *sync_bitmap;
> -    BdrvDirtyBitmap *copy_bitmap;
>  
>      MirrorSyncMode sync_mode;
>      BitmapSyncMode bitmap_mode;

[...]

> @@ -99,9 +118,83 @@ static void cow_request_end(CowRequest *req)
>      qemu_co_queue_restart_all(&req->wait_queue);
>  }
>  
> +static void block_copy_state_free(BlockCopyState *s)
> +{
> +    if (!s) {
> +        return;
> +    }
> +
> +    bdrv_release_dirty_bitmap(blk_bs(s->source), s->copy_bitmap);
> +    blk_unref(s->source);
> +    s->source = NULL;
> +    blk_unref(s->target);
> +    s->target = NULL;

I’m not quite sure why you NULL these pointers when you free the whole
object next anyway.

> +    g_free(s);
> +}
> +
> +static BlockCopyState *block_copy_state_new(
> +        BlockDriverState *source, BlockDriverState *target,
> +        int64_t cluster_size, BdrvRequestFlags write_flags,
> +        ProgressCallbackFunc progress_callback, void *progress_opaque,
> +        Error **errp)
> +{
> +    BlockCopyState *s;
> +    int ret;
> +    uint64_t no_resize = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
> +                         BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD;
> +    BdrvDirtyBitmap *copy_bitmap =
> +            bdrv_create_dirty_bitmap(source, cluster_size, NULL, errp);
> +

This probably were easier to read if you didn’t initialize copy_bitmap
with the bdrv_create_dirty_bitmap() call but instead moved that call
right above the if () here (it still fits on a single line).

> +    if (!copy_bitmap) {
> +        return NULL;
> +    }
> +    bdrv_disable_dirty_bitmap(copy_bitmap);
> +
> +    s = g_new0(BlockCopyState, 1);

With the following compound literal, you don’t need to allocate
zero-initialized memory here.

> +    *s = (BlockCopyState) {
> +        .source = blk_new(bdrv_get_aio_context(source),
> +                          BLK_PERM_CONSISTENT_READ, no_resize),
> +        .target = blk_new(bdrv_get_aio_context(target),
> +                          BLK_PERM_WRITE, no_resize),

Maybe we want to assert that source’s and target’s context are the same?

> +        .copy_bitmap = copy_bitmap,
> +        .cluster_size = cluster_size,
> +        .len = bdrv_dirty_bitmap_size(copy_bitmap),
> +        .write_flags = write_flags,
> +        .use_copy_range = !(write_flags & BDRV_REQ_WRITE_COMPRESSED),
> +        .progress_callback = progress_callback,
> +        .progress_opaque = progress_opaque,
> +    };
> +
> +    s->copy_range_size = QEMU_ALIGN_UP(MIN(blk_get_max_transfer(s->source),
> +                                           blk_get_max_transfer(s->target)),
> +                                       s->cluster_size),

Nice simplification.

> +
> +    blk_set_disable_request_queuing(s->source, true);
> +    blk_set_allow_aio_context_change(s->source, true);
> +    blk_set_disable_request_queuing(s->target, true);
> +    blk_set_allow_aio_context_change(s->target, true);

Hm.  Doesn’t creating new BBs here mean that we have to deal with the
fallout of changing the AioContext on either of them somewhere?

[...]

> @@ -449,8 +542,8 @@ static void backup_drain(BlockJob *job)
>      /* Need to keep a reference in case blk_drain triggers execution
>       * of backup_complete...
>       */
> -    if (s->target) {
> -        BlockBackend *target = s->target;
> +    if (s->bcs && s->bcs->target) {

bcs->target should always be non-NULL, shouldn’t it?

> +        BlockBackend *target = s->bcs->target;
>          blk_ref(target);
>          blk_drain(target);
>          blk_unref(target);

[...]

> @@ -730,57 +821,34 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,

[...]

> -    /*
> -     * 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);
> +    job->bcs = block_copy_state_new(
> +            bs, target, cluster_size,
> +            /*
> +             * Set write flags:
> +             * 1. Detect image-fleecing (and similar) schemes
> +             * 2. Handle compression
> +             */
> +            (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
> +            (compress ? BDRV_REQ_WRITE_COMPRESSED : 0),

This is a bit hard to read.  Why not add a dedicated variable for it?

> +            backup_progress_callback, job, errp);
> +    if (!job->bcs) {
> +        goto error;
> +    }
>  
>      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() */
>      block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,

[...]

> diff --git a/block/trace-events b/block/trace-events
> index 04209f058d..ad1454f539 100644
> --- a/block/trace-events
> +++ b/block/trace-events
> @@ -40,12 +40,12 @@ 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_skip(void *job, int64_t start) "job %p start %"PRId64
> +block_copy_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
> +block_copy_process(void *job, int64_t start) "job %p start %"PRId64
> +block_copy_with_bounce_buffer_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
> +block_copy_with_bounce_buffer_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
> +block_copy_with_offload_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"

The pointer is no longer a job pointer, though.

Max

>  
>  # ../blockdev.c
>  qmp_block_job_cancel(void *job) "job %p"
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style Vladimir Sementsov-Ogievskiy
@ 2019-08-28 16:06   ` Max Reitz
  2019-08-29 11:25     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 16:06 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 410 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Fix comment style and reflow arguments in same manner like
> block_copy_state_new.

I like the current function header style better.

Max

> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block/backup.c | 40 +++++++++++++++++++---------------------
>  1 file changed, 19 insertions(+), 21 deletions(-)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file Vladimir Sementsov-Ogievskiy
@ 2019-08-28 16:16   ` Max Reitz
  2019-08-29 12:00     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 16:16 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 884 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Split block_copy to separate file, to be cleanly shared with backup-top
> filter driver in further commits.
> 
> It's a clean movement, the only change is drop "static" from interface
> functions.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  include/block/block-copy.h |  59 +++++++
>  block/backup.c             | 313 +------------------------------------
>  block/block-copy.c         | 307 ++++++++++++++++++++++++++++++++++++
>  block/Makefile.objs        |   1 +
>  block/trace-events         |   2 +
>  5 files changed, 370 insertions(+), 312 deletions(-)
>  create mode 100644 include/block/block-copy.h
>  create mode 100644 block/block-copy.c

May change depending on changes to the preceding patches, but FWIW

Reviewed-by: Max Reitz <mreitz@redhat.com>


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 06/13] block: teach bdrv_debug_breakpoint skip filters with backing
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 06/13] block: teach bdrv_debug_breakpoint skip filters with backing Vladimir Sementsov-Ogievskiy
@ 2019-08-28 16:21   ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-08-28 16:21 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 602 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Teach bdrv_debug_breakpoint and bdrv_debug_remove_breakpoint skip
> filters with backing. This is needed to implement and use in backup job
> it's own backup_top filter driver (like mirror already has one), and
> without this improvement, breakpoint removal will fail at least in 55
> iotest.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block.c | 34 ++++++++++++++++++++++++++--------
>  1 file changed, 26 insertions(+), 8 deletions(-)

FWIW

Reviewed-by: Max Reitz <mreitz@redhat.com>


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter Vladimir Sementsov-Ogievskiy
@ 2019-08-28 16:40   ` Max Reitz
  2019-08-29 13:22     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 16:40 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 2932 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> After backup-top filter appearing it's not possible to see dirty
> bitmaps in top node, so use node-name instead.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  tests/qemu-iotests/124        |   3 +-
>  tests/qemu-iotests/257        |  39 +---
>  tests/qemu-iotests/257.out    | 364 +++++++++++++---------------------
>  tests/qemu-iotests/iotests.py |  22 ++
>  4 files changed, 173 insertions(+), 255 deletions(-)
> 
> diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
> index 3440f54781..8b6024769c 100755
> --- a/tests/qemu-iotests/124
> +++ b/tests/qemu-iotests/124
> @@ -749,8 +749,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
>  
>          # 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]
> +        ret = self.vm.get_bitmap(None, drive0['id'], bitmap.name)
>          self.assert_qmp(ret, 'count', 458752)
>          self.assert_qmp(ret, 'granularity', 65536)
>          self.assert_qmp(ret, 'status', 'frozen')

I see a couple more instances of querying the bitmaps through
query-block here.  Wouldn’t it make sense to replace them all with
get_bitmap()?

[...]

> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
> index 84438e837c..9381964d9f 100644
> --- a/tests/qemu-iotests/iotests.py
> +++ b/tests/qemu-iotests/iotests.py
> @@ -643,6 +643,28 @@ class VM(qtest.QEMUQtestMachine):
>                  return x
>          return None
>  
> +    def query_bitmaps(self):
> +        res = self.qmp("query-named-block-nodes")
> +        return {"bitmaps": {device['node-name']: device['dirty-bitmaps']
> +                                for device in res['return']
> +                                    if 'dirty-bitmaps' in device}}

I’d leave the wrapping in {"bitmaps": x} to the callers, if they need it.

> +
> +    def get_bitmap(self, bitmaps, node_name, name, recording=None):
> +        """
> +        get a specific bitmap from the object returned by query_bitmaps.
> +        :param recording: If specified, filter results by the specified value.
> +        """
> +        if bitmaps is None:
> +            bitmaps = self.query_bitmaps()
> +
> +        for bitmap in bitmaps['bitmaps'][node_name]:
> +            if bitmap.get('name', '') == name:
> +                if recording is None:
> +                    return bitmap
> +                elif bitmap.get('recording') == recording:
> +                    return bitmap

Maybe add a “break” or “return None” here?

(Yes, yes, you just moved existing code.  Still.)

Max

> +        return None
> +
>  
>  index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
>  
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 08/13] iotests: 257: drop unused Drive.device field
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 08/13] iotests: 257: drop unused Drive.device field Vladimir Sementsov-Ogievskiy
@ 2019-08-28 16:50   ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-08-28 16:50 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 496 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> After previous commit Drive.device is actually unused. Drop it together
> with .name property.  While being here reuse .node in qmp commands
> instead of writing 'drive0' twice.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  tests/qemu-iotests/257 | 37 +++++++++++++++----------------------
>  1 file changed, 15 insertions(+), 22 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 09/13] iotests: 257: drop device_add
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 09/13] iotests: 257: drop device_add Vladimir Sementsov-Ogievskiy
@ 2019-08-28 16:54   ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-08-28 16:54 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 392 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> SCSI devices are unused in test, drop them.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  tests/qemu-iotests/257     |  8 -------
>  tests/qemu-iotests/257.out | 44 --------------------------------------
>  2 files changed, 52 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions Vladimir Sementsov-Ogievskiy
@ 2019-08-28 17:02   ` Max Reitz
  2019-08-29  9:17     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 17:02 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 680 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> From: Vladimir Sementsov-Ogievskiy <etendren@gmail.com>

Hm. :-)

Do you want to fix that?

> Introduce lock/unlock range functionality, based on serialized
> requests. This is needed to refactor backup, dropping local
> tracked-request-like synchronization.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <etendren@gmail.com>
> ---
>  include/block/block_int.h |  4 ++++
>  block/io.c                | 44 ++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 47 insertions(+), 1 deletion(-)

Apart from that, I can’t see any changes from v8, so:

Reviewed-by: Max Reitz <mreitz@redhat.com>


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 12/13] block: introduce backup-top filter driver
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 12/13] block: introduce backup-top filter driver Vladimir Sementsov-Ogievskiy
@ 2019-08-28 17:52   ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-08-28 17:52 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 2026 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Backup-top filter caches write operations and does copy-before-write
> operations.
> 
> The driver will be used in backup instead of write-notifiers.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block/backup-top.h  |  37 +++++++
>  block/backup-top.c  | 245 ++++++++++++++++++++++++++++++++++++++++++++
>  block/Makefile.objs |   2 +
>  3 files changed, 284 insertions(+)
>  create mode 100644 block/backup-top.h
>  create mode 100644 block/backup-top.c

[...]

> +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;
> +    }
> +    bdrv_refresh_filename(bs->backing->bs);

bdrv_refresh_filename() should have done this already.

> +    pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
> +            bs->backing->bs->filename);
> +}

[...]

> +BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
> +                                         const char *filter_node_name,
> +                                         Error **errp)
> +{
[...]
> +    assert(!(top->backing->shared_perm & BLK_PERM_WRITE));

Not wrong, but why?

> +
> +    return top;
> +}
> +
> +void bdrv_backup_top_set_bcs(BlockDriverState *bs, BlockCopyState *copy_state)
> +{
> +    BDRVBackupTopState *s = bs->opaque;
> +
> +    assert(blk_bs(copy_state->source) == bs->backing->bs);
> +    s->bcs = copy_state;
> +}
> +
> +void bdrv_backup_top_drop(BlockDriverState *bs)
> +{
> +    AioContext *aio_context = bdrv_get_aio_context(bs);
> +
> +    aio_context_acquire(aio_context);
> +
> +    bdrv_drained_begin(bs);
> +
> +    bdrv_child_try_set_perm(bs->backing, 0, BLK_PERM_ALL, &error_abort);

I would prefer a state->active = false and bdrv_child_refresh_perms().

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers
  2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers Vladimir Sementsov-Ogievskiy
@ 2019-08-28 19:50   ` Max Reitz
  2019-08-29 14:55     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-08-28 19:50 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, wencongyang2, xiechanglong.d, qemu-devel, armbru,
	jsnow, stefanha, den


[-- Attachment #1.1: Type: text/plain, Size: 8464 bytes --]

On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
> Drop write notifiers and use filter node instead.
> 
> = Changes =
> 
> 1. add filter-node-name argument for backup qmp api. We have to do it
> in this commit, as 257 needs to be fixed.

I feel a bit bad about it not being an implicit node.  But I know you
don’t like them, they’re probably just broken altogether and because
libvirt doesn’t use backup (yet), probably nobody cares.

> 2. there no move write notifiers here, so is_write_notifier parameter

s/there/there are/, I suppose?

> is dropped from block-copy paths.
> 
> 3. Intersecting requests handling changed, now synchronization between
> backup-top, backup and guest writes are all done in block/block-copy.c
> and works as follows:
> 
> On copy operation, we work only with dirty areas. If bits are dirty it
> means that there are no requests intersecting with this area. We clear
> dirty bits and take bdrv range lock (bdrv_co_try_lock) on this area to
> prevent further operations from interaction with guest (only with
> guest, as neither backup nor backup-top will touch non-dirty area). If
> copy-operation failed we set dirty bits back together with releasing
> the lock.
> 
> The actual difference with old scheme is that on guest writes we
> don't lock the whole region but only dirty-parts, and to be more
> precise: only dirty-part we are currently operate on. In old scheme
> guest write to non-dirty area (which may be safely ignored by backup)
> may wait for intersecting request, touching some other area which is
> dirty.
> 
> 4. To sync with in-flight requests at job finish we now have drained
> removing of the filter, we don't need rw-lock.
> 
> = Notes =
> 
> Note the consequence of three objects appearing: backup-top, backup job
> and block-copy-state:
> 
> 1. We want to insert backup-top before job creation, to behave similar
> with mirror and commit, where job is started upon filter.
> 
> 2. We also have to create block-copy-state after filter injection, as
> we don't want it's source child be replaced by fitler. Instead we want

s/it's/its/, s/filter/filter/ (although “fitler” does have an amusing
ring to it)

> to keep BCS.source to be real source node, as we want to use
> bdrv_co_try_lock in CBW operations and it can't be used on filter, as
> on filter we already have in-flight (write) request from upper layer.

Reasonable, even more so as I suppose BCS.source should eventually be a
BdrvChild of backup-top.

What looks wrong is that the sync_bitmap is created on the source (“bs”
in backup_job_create()), but backup_cleanup_sync_bitmap() still assumes
it is on blk_bs(job->common.blk) (which is no longer true).

> So, we firstly create inject backup-top, then create job and BCS. BCS
> is the latest just to not create extra variable for it. Finally we set
> bcs for backup-top filter.
> 
> = Iotest changes =
> 
> 56: op-blocker doesn't shot now, as we set it on source, but then check

s/shot/show/?

> on filter, when trying to start second backup, so error caught in
> test_dismiss_collision is changed. It's OK anyway, as this test-case
> seems to test that after some collision we can dismiss first job and
> successfully start the second one. So, the change is that collision is
> changed from op-blocker to file-posix locks.

Well, but the op blocker belongs to the source, which should be covered
by internal permissions.  The fact that it now shows up as a file-posix
error thus shows that the conflict also moves from the source to the
target.  It’s OK because we actually don’t have a conflict on the source.

But I wonder whether it would be better for test_dismiss_collision() to
do a blockdev-backup instead where we can see the collision on the target.

Hm.  On second thought, why do we even get a conflict on the target?
block-copy does share the WRITE permission for it...

> However, it's obvious now that we'd better drop this op-blocker at all
> and add a test-case for two backups from one node (to different
> destinations) actually works. But not in these series.
> 
> 257: The test wants to emulate guest write during backup. They should
> go to filter node, not to original source node, of course. Therefore we
> need to specify filter node name and use it.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  qapi/block-core.json       |   8 +-
>  include/block/block-copy.h |   2 +-
>  include/block/block_int.h  |   1 +
>  block/backup-top.c         |  14 +-
>  block/backup.c             | 113 +++-----------
>  block/block-copy.c         |  55 ++++---
>  block/replication.c        |   2 +-
>  blockdev.c                 |   1 +
>  tests/qemu-iotests/056     |   2 +-
>  tests/qemu-iotests/257     |   7 +-
>  tests/qemu-iotests/257.out | 306 ++++++++++++++++++-------------------
>  11 files changed, 230 insertions(+), 281 deletions(-)

Nice.

I don’t have much to object (for some reason, I feel a bit uneasy about
dropping all the request waiting code, but I suppose that is indeed
taken care of with the range locks now).

[...]

> diff --git a/block/backup.c b/block/backup.c
> index d927c63e5a..259a165405 100644
> --- a/block/backup.c
> +++ b/block/backup.c

[...]

> @@ -552,6 +480,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
>          backup_clean(&job->common.job);
>          job_early_fail(&job->common.job);
>      }
> +    if (backup_top) {
> +        bdrv_backup_top_drop(backup_top);
> +    }

This order is weird because backup-top still has a pointer to the BCS,
but we have already freed it at this point.

>  
>      return NULL;
>  }
> diff --git a/block/block-copy.c b/block/block-copy.c
> index 6828c46ba0..f3102ec3ff 100644
> --- a/block/block-copy.c
> +++ b/block/block-copy.c
> @@ -98,27 +98,32 @@ fail:
>   * 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 is_write_notifier,
> +        BlockCopyState *s, int64_t start, int64_t end,
>          bool *error_is_read, void **bounce_buffer)
>  {
>      int ret;
> -    int nbytes;
> -    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
> +    int nbytes = MIN(s->cluster_size, s->len - start);
> +    void *lock = bdrv_co_try_lock(blk_bs(s->source), start, nbytes);
> +
> +    /*
> +     * Function must be called only on full-dirty region, so nobody touching or
> +     * touched these bytes. Therefore, we must successfully get lock.

Which is OK because backup-top does not share the WRITE permission.  But
it is organized a bit weirdly, because it’s actually block-copy here
that relies on the WRITE permission not to be shared; which it itself
cannot do because backup-top needs it.

This of course results from the fact that backup-top and block-copy
should really use the same object to access the source, namely the
backing BdrvChild of backup-top.

Maybe there should be a comment somewhere that points this out?

> +     */
> +    assert(lock);

[...]

> @@ -128,13 +133,16 @@ static int coroutine_fn block_copy_with_bounce_buffer(
>          if (error_is_read) {
>              *error_is_read = false;
>          }
> -        goto fail;
> +        goto out;
>      }
>  
> -    return nbytes;
> -fail:
> -    bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
> -    return ret;
> +out:
> +    if (ret < 0) {
> +        bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
> +    }
> +    bdrv_co_unlock(lock);
> +
> +    return ret < 0 ? ret : nbytes;
>  

I feel like it would still be simpler to keep the separate fail path and
just duplicate the bdrv_co_unlock(lock) call.

[...]

> 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,

Is this guaranteed to be NULL when not specified by the user?

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions
  2019-08-28 17:02   ` Max Reitz
@ 2019-08-29  9:17     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-29  9:17 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

28.08.2019 20:02, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> From: Vladimir Sementsov-Ogievskiy <etendren@gmail.com>
> 
> Hm. :-)
> 
> Do you want to fix that?

Yes.. Hmm seems like I was working from home at some moment

> 
>> Introduce lock/unlock range functionality, based on serialized
>> requests. This is needed to refactor backup, dropping local
>> tracked-request-like synchronization.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <etendren@gmail.com>
>> ---
>>   include/block/block_int.h |  4 ++++
>>   block/io.c                | 44 ++++++++++++++++++++++++++++++++++++++-
>>   2 files changed, 47 insertions(+), 1 deletion(-)
> 
> Apart from that, I can’t see any changes from v8, so:
> 
> Reviewed-by: Max Reitz <mreitz@redhat.com>
> 


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState
  2019-08-28 15:59   ` Max Reitz
@ 2019-08-29 10:52     ` Vladimir Sementsov-Ogievskiy
  2019-09-02 16:09       ` Max Reitz
  0 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-29 10:52 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

Thanks for reviewing!

28.08.2019 18:59, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> Split copying code part from backup to "block-copy", including separate
>> state structure and function renaming. This is needed to share it with
>> backup-top filter driver in further commits.
>>
>> Notes:
>>
>> 1. As BlockCopyState keeps own BlockBackend objects, remaining
> 
> I suppose these should be BdrvChild objects at some point, but doing it
> now would just mean effectively duplicating code from block-backend.c.
> (“now” = before we have a backup-top filter to attach the children to.)

How much is it bad to not do it, but leave them to be block-backends in block-copy
state? They'll connected anyway through the job, as they all are in job.nodes.

We have block-backends in jobs currently, is it bad?

> 
>> job->common.blk users only use it to get bs by blk_bs() call, so clear
>> job->commen.blk permissions set in block_job_create.
>>
>> 2. Rename s/initializing_bitmap/skip_unallocated/ to sound a bit better
>> as interface to BlockCopyState
>>
>> 3. Split is not very clean: there left some duplicated fields, backup
> 
> Are there any but cluster_size and len (and source, in a sense)?

Seems no more

> 
>> code uses some BlockCopyState fields directly, let's postpone it for
>> further improvements and keep this comment simpler for review.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   block/backup.c     | 324 +++++++++++++++++++++++++++------------------
>>   block/trace-events |  12 +-
>>   2 files changed, 200 insertions(+), 136 deletions(-)
>>
>> diff --git a/block/backup.c b/block/backup.c
>> index 13a1d80157..f52ac622e0 100644
>> --- a/block/backup.c
>> +++ b/block/backup.c
>> @@ -35,12 +35,35 @@ typedef struct CowRequest {
>>       CoQueue wait_queue; /* coroutines blocked on this request */
>>   } CowRequest;
>>   
>> +/*
>> + * ProgressCallbackFunc
>> + *
>> + * Called when some progress is done in context of BlockCopyState:
>> + *  1. When some bytes copied, called with @bytes > 0.
>> + *  2. When some bytes resetted from copy_bitmap, called with @bytes = 0 (user
> 
> *reset
> 
>> + *     may recalculate remaining bytes from copy_bitmap dirty count.
>> + */
>> +typedef void (*ProgressCallbackFunc)(int64_t bytes, void *opaque);
> 
> Maybe there should be two callbacks instead, one for “We’ve actively
> made progress” (bytes > 0) and one for “The expected length has changed”
> (bytes == 0)?

I thought, that there are already too many parameters in block_copy_state_new().
But I agree with you, as actually it led to two callbacks in a one with just
if-else to distinguish them. Will do.

> 
>> +typedef struct BlockCopyState {
>> +    BlockBackend *source;
>> +    BlockBackend *target;
>> +    BdrvDirtyBitmap *copy_bitmap;
>> +    int64_t cluster_size;
>> +    bool use_copy_range;
>> +    int64_t copy_range_size;
>> +    uint64_t len;
>> +
>> +    BdrvRequestFlags write_flags;
>> +    bool skip_unallocated;
> 
> The rename seems reasonable, although I think this should get a comment,
> because it doesn’t mean just to skip unallocated clusters; it also means
> to clear unallocated clusters from the bitmap.
> 
>> +
>> +    ProgressCallbackFunc progress_callback;
>> +    void *progress_opaque;
>> +} BlockCopyState;
>> +
>>   typedef struct BackupBlockJob {
>>       BlockJob common;
>> -    BlockBackend *target;
>>   
>>       BdrvDirtyBitmap *sync_bitmap;
>> -    BdrvDirtyBitmap *copy_bitmap;
>>   
>>       MirrorSyncMode sync_mode;
>>       BitmapSyncMode bitmap_mode;
> 
> [...]
> 
>> @@ -99,9 +118,83 @@ static void cow_request_end(CowRequest *req)
>>       qemu_co_queue_restart_all(&req->wait_queue);
>>   }
>>   
>> +static void block_copy_state_free(BlockCopyState *s)
>> +{
>> +    if (!s) {
>> +        return;
>> +    }
>> +
>> +    bdrv_release_dirty_bitmap(blk_bs(s->source), s->copy_bitmap);
>> +    blk_unref(s->source);
>> +    s->source = NULL;
>> +    blk_unref(s->target);
>> +    s->target = NULL;
> 
> I’m not quite sure why you NULL these pointers when you free the whole
> object next anyway.

it is for backup_drain, I'm afraid of some yield during blk_unref (and seems it's unsafe
anyway, as I zero reference after calling blk_unref). Anyway,
backup_drain will be dropped in "[PATCH v3] job: drop job_drain", I'll drop
"= NULL" here now and workaround backup_drain in backup_clean with corresponding
comment.

> 
>> +    g_free(s);
>> +}
>> +
>> +static BlockCopyState *block_copy_state_new(
>> +        BlockDriverState *source, BlockDriverState *target,
>> +        int64_t cluster_size, BdrvRequestFlags write_flags,
>> +        ProgressCallbackFunc progress_callback, void *progress_opaque,
>> +        Error **errp)
>> +{
>> +    BlockCopyState *s;
>> +    int ret;
>> +    uint64_t no_resize = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
>> +                         BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD;
>> +    BdrvDirtyBitmap *copy_bitmap =
>> +            bdrv_create_dirty_bitmap(source, cluster_size, NULL, errp);
>> +
> 
> This probably were easier to read if you didn’t initialize copy_bitmap
> with the bdrv_create_dirty_bitmap() call but instead moved that call
> right above the if () here (it still fits on a single line).
> 
>> +    if (!copy_bitmap) {
>> +        return NULL;
>> +    }
>> +    bdrv_disable_dirty_bitmap(copy_bitmap);
>> +
>> +    s = g_new0(BlockCopyState, 1);
> 
> With the following compound literal, you don’t need to allocate
> zero-initialized memory here.
> 
>> +    *s = (BlockCopyState) {
>> +        .source = blk_new(bdrv_get_aio_context(source),
>> +                          BLK_PERM_CONSISTENT_READ, no_resize),
>> +        .target = blk_new(bdrv_get_aio_context(target),
>> +                          BLK_PERM_WRITE, no_resize),
> 
> Maybe we want to assert that source’s and target’s context are the same?

Context may change, so no reason to check it here. It'd better be asserted in
block_copy() before copying, I wanted to do it, but forget, will add.

> 
>> +        .copy_bitmap = copy_bitmap,
>> +        .cluster_size = cluster_size,
>> +        .len = bdrv_dirty_bitmap_size(copy_bitmap),
>> +        .write_flags = write_flags,
>> +        .use_copy_range = !(write_flags & BDRV_REQ_WRITE_COMPRESSED),
>> +        .progress_callback = progress_callback,
>> +        .progress_opaque = progress_opaque,
>> +    };
>> +
>> +    s->copy_range_size = QEMU_ALIGN_UP(MIN(blk_get_max_transfer(s->source),
>> +                                           blk_get_max_transfer(s->target)),
>> +                                       s->cluster_size),
> 
> Nice simplification. >
>> +
>> +    blk_set_disable_request_queuing(s->source, true);
>> +    blk_set_allow_aio_context_change(s->source, true);
>> +    blk_set_disable_request_queuing(s->target, true);
>> +    blk_set_allow_aio_context_change(s->target, true);
> 
> Hm.  Doesn’t creating new BBs here mean that we have to deal with the
> fallout of changing the AioContext on either of them somewhere?

In backup context, backup job is responsible for keeping source and target bs
in same context, so I think allowing blk to change aio context and assert in
block_copy() that context is the same should be enough for now.

I'll add a comment on this here.

> 
> [...]
> 
>> @@ -449,8 +542,8 @@ static void backup_drain(BlockJob *job)
>>       /* Need to keep a reference in case blk_drain triggers execution
>>        * of backup_complete...
>>        */
>> -    if (s->target) {
>> -        BlockBackend *target = s->target;
>> +    if (s->bcs && s->bcs->target) {
> 
> bcs->target should always be non-NULL, shouldn’t it?

But if we intersect with some yield in cleanu-up procedure... Anyway, backup_drain
will be dropped soon I hope.

> 
>> +        BlockBackend *target = s->bcs->target;
>>           blk_ref(target);
>>           blk_drain(target);
>>           blk_unref(target);
> 
> [...]
> 
>> @@ -730,57 +821,34 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
> 
> [...]
> 
>> -    /*
>> -     * 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);
>> +    job->bcs = block_copy_state_new(
>> +            bs, target, cluster_size,
>> +            /*
>> +             * Set write flags:
>> +             * 1. Detect image-fleecing (and similar) schemes
>> +             * 2. Handle compression
>> +             */
>> +            (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
>> +            (compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
> 
> This is a bit hard to read.  Why not add a dedicated variable for it?
> 
>> +            backup_progress_callback, job, errp);
>> +    if (!job->bcs) {
>> +        goto error;
>> +    }
>>   
>>       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() */
>>       block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
> 
> [...]
> 
>> diff --git a/block/trace-events b/block/trace-events
>> index 04209f058d..ad1454f539 100644
>> --- a/block/trace-events
>> +++ b/block/trace-events
>> @@ -40,12 +40,12 @@ 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_skip(void *job, int64_t start) "job %p start %"PRId64
>> +block_copy_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
>> +block_copy_process(void *job, int64_t start) "job %p start %"PRId64
>> +block_copy_with_bounce_buffer_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
>> +block_copy_with_bounce_buffer_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
>> +block_copy_with_offload_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
> 
> The pointer is no longer a job pointer, though.
> 
> Max
> 
>>   
>>   # ../blockdev.c
>>   qmp_block_job_cancel(void *job) "job %p"
>>
> 
> 


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style
  2019-08-28 16:06   ` Max Reitz
@ 2019-08-29 11:25     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-29 11:25 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

28.08.2019 19:06, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> Fix comment style and reflow arguments in same manner like
>> block_copy_state_new.
> 
> I like the current function header style better.
> 

OK, not critical at all for me, let's keep current. I'll instead fix all comments
style here.


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file
  2019-08-28 16:16   ` Max Reitz
@ 2019-08-29 12:00     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-29 12:00 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

28.08.2019 19:16, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> Split block_copy to separate file, to be cleanly shared with backup-top
>> filter driver in further commits.
>>
>> It's a clean movement, the only change is drop "static" from interface
>> functions.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   include/block/block-copy.h |  59 +++++++
>>   block/backup.c             | 313 +------------------------------------
>>   block/block-copy.c         | 307 ++++++++++++++++++++++++++++++++++++
>>   block/Makefile.objs        |   1 +
>>   block/trace-events         |   2 +
>>   5 files changed, 370 insertions(+), 312 deletions(-)
>>   create mode 100644 include/block/block-copy.h
>>   create mode 100644 block/block-copy.c
> 
> May change depending on changes to the preceding patches, but FWIW
> 
> Reviewed-by: Max Reitz <mreitz@redhat.com>
> 

If you don't mind, I'll keep it until the patch doing simple nochange movement.

-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter
  2019-08-28 16:40   ` Max Reitz
@ 2019-08-29 13:22     ` Vladimir Sementsov-Ogievskiy
  2019-09-02 16:10       ` Max Reitz
  0 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-29 13:22 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

28.08.2019 19:40, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> After backup-top filter appearing it's not possible to see dirty
>> bitmaps in top node, so use node-name instead.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   tests/qemu-iotests/124        |   3 +-
>>   tests/qemu-iotests/257        |  39 +---
>>   tests/qemu-iotests/257.out    | 364 +++++++++++++---------------------
>>   tests/qemu-iotests/iotests.py |  22 ++
>>   4 files changed, 173 insertions(+), 255 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
>> index 3440f54781..8b6024769c 100755
>> --- a/tests/qemu-iotests/124
>> +++ b/tests/qemu-iotests/124
>> @@ -749,8 +749,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
>>   
>>           # 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]
>> +        ret = self.vm.get_bitmap(None, drive0['id'], bitmap.name)
>>           self.assert_qmp(ret, 'count', 458752)
>>           self.assert_qmp(ret, 'granularity', 65536)
>>           self.assert_qmp(ret, 'status', 'frozen')
> 
> I see a couple more instances of querying the bitmaps through
> query-block here.  Wouldn’t it make sense to replace them all with
> get_bitmap()?
> 
> [...]
> 
>> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
>> index 84438e837c..9381964d9f 100644
>> --- a/tests/qemu-iotests/iotests.py
>> +++ b/tests/qemu-iotests/iotests.py
>> @@ -643,6 +643,28 @@ class VM(qtest.QEMUQtestMachine):
>>                   return x
>>           return None
>>   
>> +    def query_bitmaps(self):
>> +        res = self.qmp("query-named-block-nodes")
>> +        return {"bitmaps": {device['node-name']: device['dirty-bitmaps']
>> +                                for device in res['return']
>> +                                    if 'dirty-bitmaps' in device}}
> 
> I’d leave the wrapping in {"bitmaps": x} to the callers, if they need it.
> 
>> +
>> +    def get_bitmap(self, bitmaps, node_name, name, recording=None):
>> +        """
>> +        get a specific bitmap from the object returned by query_bitmaps.
>> +        :param recording: If specified, filter results by the specified value.
>> +        """
>> +        if bitmaps is None:
>> +            bitmaps = self.query_bitmaps()
>> +
>> +        for bitmap in bitmaps['bitmaps'][node_name]:
>> +            if bitmap.get('name', '') == name:
>> +                if recording is None:
>> +                    return bitmap
>> +                elif bitmap.get('recording') == recording:
>> +                    return bitmap
> 
> Maybe add a “break” or “return None” here?
> 
> (Yes, yes, you just moved existing code.  Still.)
> 

No, as we may have several unnamed bitmaps, which should be selected by "recording"..

> 
>> +        return None
>> +
>>   
>>   index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
>>   
>>
> 
> 


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers
  2019-08-28 19:50   ` Max Reitz
@ 2019-08-29 14:55     ` Vladimir Sementsov-Ogievskiy
  2019-09-02 16:34       ` Max Reitz
  0 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-08-29 14:55 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

28.08.2019 22:50, Max Reitz wrote:
> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>> Drop write notifiers and use filter node instead.
>>
>> = Changes =
>>
>> 1. add filter-node-name argument for backup qmp api. We have to do it
>> in this commit, as 257 needs to be fixed.
> 
> I feel a bit bad about it not being an implicit node.  But I know you
> don’t like them, they’re probably just broken altogether and because
> libvirt doesn’t use backup (yet), probably nobody cares.
> 
>> 2. there no move write notifiers here, so is_write_notifier parameter
> 
> s/there/there are/, I suppose?
> 
>> is dropped from block-copy paths.
>>
>> 3. Intersecting requests handling changed, now synchronization between
>> backup-top, backup and guest writes are all done in block/block-copy.c
>> and works as follows:
>>
>> On copy operation, we work only with dirty areas. If bits are dirty it
>> means that there are no requests intersecting with this area. We clear
>> dirty bits and take bdrv range lock (bdrv_co_try_lock) on this area to
>> prevent further operations from interaction with guest (only with
>> guest, as neither backup nor backup-top will touch non-dirty area). If
>> copy-operation failed we set dirty bits back together with releasing
>> the lock.
>>
>> The actual difference with old scheme is that on guest writes we
>> don't lock the whole region but only dirty-parts, and to be more
>> precise: only dirty-part we are currently operate on. In old scheme
>> guest write to non-dirty area (which may be safely ignored by backup)
>> may wait for intersecting request, touching some other area which is
>> dirty.
>>
>> 4. To sync with in-flight requests at job finish we now have drained
>> removing of the filter, we don't need rw-lock.
>>
>> = Notes =
>>
>> Note the consequence of three objects appearing: backup-top, backup job
>> and block-copy-state:
>>
>> 1. We want to insert backup-top before job creation, to behave similar
>> with mirror and commit, where job is started upon filter.
>>
>> 2. We also have to create block-copy-state after filter injection, as
>> we don't want it's source child be replaced by fitler. Instead we want
> 
> s/it's/its/, s/filter/filter/ (although “fitler” does have an amusing
> ring to it)
> 
>> to keep BCS.source to be real source node, as we want to use
>> bdrv_co_try_lock in CBW operations and it can't be used on filter, as
>> on filter we already have in-flight (write) request from upper layer.
> 
> Reasonable, even more so as I suppose BCS.source should eventually be a
> BdrvChild of backup-top.
> 
> What looks wrong is that the sync_bitmap is created on the source (“bs”
> in backup_job_create()), but backup_cleanup_sync_bitmap() still assumes
> it is on blk_bs(job->common.blk) (which is no longer true).
> 
>> So, we firstly create inject backup-top, then create job and BCS. BCS
>> is the latest just to not create extra variable for it. Finally we set
>> bcs for backup-top filter.
>>
>> = Iotest changes =
>>
>> 56: op-blocker doesn't shot now, as we set it on source, but then check
> 
> s/shot/show/?
> 
>> on filter, when trying to start second backup, so error caught in
>> test_dismiss_collision is changed. It's OK anyway, as this test-case
>> seems to test that after some collision we can dismiss first job and
>> successfully start the second one. So, the change is that collision is
>> changed from op-blocker to file-posix locks.
> 
> Well, but the op blocker belongs to the source, which should be covered
> by internal permissions.  The fact that it now shows up as a file-posix
> error thus shows that the conflict also moves from the source to the
> target.  It’s OK because we actually don’t have a conflict on the source.
> 
> But I wonder whether it would be better for test_dismiss_collision() to
> do a blockdev-backup instead where we can see the collision on the target.
> 
> Hm.  On second thought, why do we even get a conflict on the target?
> block-copy does share the WRITE permission for it...

Not sure, but assume that this is because in file-posix.c in raw_co_create
we do want RESIZE perm.

I can instead move this test to use specified job-id, to move the collision
to "job-id already exists" error. Is it better?

I'm afraid that posix locks will not work if disable them in config.

> 
>> However, it's obvious now that we'd better drop this op-blocker at all
>> and add a test-case for two backups from one node (to different
>> destinations) actually works. But not in these series.
>>
>> 257: The test wants to emulate guest write during backup. They should
>> go to filter node, not to original source node, of course. Therefore we
>> need to specify filter node name and use it.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   qapi/block-core.json       |   8 +-
>>   include/block/block-copy.h |   2 +-
>>   include/block/block_int.h  |   1 +
>>   block/backup-top.c         |  14 +-
>>   block/backup.c             | 113 +++-----------
>>   block/block-copy.c         |  55 ++++---
>>   block/replication.c        |   2 +-
>>   blockdev.c                 |   1 +
>>   tests/qemu-iotests/056     |   2 +-
>>   tests/qemu-iotests/257     |   7 +-
>>   tests/qemu-iotests/257.out | 306 ++++++++++++++++++-------------------
>>   11 files changed, 230 insertions(+), 281 deletions(-)
> 
> Nice.
> 
> I don’t have much to object (for some reason, I feel a bit uneasy about
> dropping all the request waiting code, but I suppose that is indeed
> taken care of with the range locks now).
> 
> [...]
> 
>> diff --git a/block/backup.c b/block/backup.c
>> index d927c63e5a..259a165405 100644
>> --- a/block/backup.c
>> +++ b/block/backup.c
> 
> [...]
> 
>> @@ -552,6 +480,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
>>           backup_clean(&job->common.job);
>>           job_early_fail(&job->common.job);
>>       }

actually, here should be "else if"

>> +    if (backup_top) {
>> +        bdrv_backup_top_drop(backup_top);
>> +    }
> 
> This order is weird because backup-top still has a pointer to the BCS,
> but we have already freed it at this point.
> 
>>   
>>       return NULL;
>>   }
>> diff --git a/block/block-copy.c b/block/block-copy.c
>> index 6828c46ba0..f3102ec3ff 100644
>> --- a/block/block-copy.c
>> +++ b/block/block-copy.c
>> @@ -98,27 +98,32 @@ fail:
>>    * 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 is_write_notifier,
>> +        BlockCopyState *s, int64_t start, int64_t end,
>>           bool *error_is_read, void **bounce_buffer)
>>   {
>>       int ret;
>> -    int nbytes;
>> -    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
>> +    int nbytes = MIN(s->cluster_size, s->len - start);
>> +    void *lock = bdrv_co_try_lock(blk_bs(s->source), start, nbytes);
>> +
>> +    /*
>> +     * Function must be called only on full-dirty region, so nobody touching or
>> +     * touched these bytes. Therefore, we must successfully get lock.
> 
> Which is OK because backup-top does not share the WRITE permission.  But
> it is organized a bit weirdly, because it’s actually block-copy here
> that relies on the WRITE permission not to be shared; which it itself
> cannot do because backup-top needs it.
> 
> This of course results from the fact that backup-top and block-copy
> should really use the same object to access the source, namely the
> backing BdrvChild of backup-top.
> 
> Maybe there should be a comment somewhere that points this out?

But I wanted block-copy not to be directly connected to backup-top.

I think actually we rely on the fact that user of block-copy() guarantees that
nobody is touching dirty areas (except block_copy). And with backup, this is done by
backup-top filter. I'll add a comment on this.

> 
>> +     */
>> +    assert(lock);
> 
> [...]
> 
>> @@ -128,13 +133,16 @@ static int coroutine_fn block_copy_with_bounce_buffer(
>>           if (error_is_read) {
>>               *error_is_read = false;
>>           }
>> -        goto fail;
>> +        goto out;
>>       }
>>   
>> -    return nbytes;
>> -fail:
>> -    bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
>> -    return ret;
>> +out:
>> +    if (ret < 0) {
>> +        bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
>> +    }
>> +    bdrv_co_unlock(lock);
>> +
>> +    return ret < 0 ? ret : nbytes;
>>   
> 
> I feel like it would still be simpler to keep the separate fail path and
> just duplicate the bdrv_co_unlock(lock) call.
> 
> [...]
> 
>> 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,
> 
> Is this guaranteed to be NULL when not specified by the user?
> 

As far as I know - yes, it is.


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState
  2019-08-29 10:52     ` Vladimir Sementsov-Ogievskiy
@ 2019-09-02 16:09       ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-09-02 16:09 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha


[-- Attachment #1.1: Type: text/plain, Size: 4567 bytes --]

On 29.08.19 12:52, Vladimir Sementsov-Ogievskiy wrote:
> Thanks for reviewing!
> 
> 28.08.2019 18:59, Max Reitz wrote:
>> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>>> Split copying code part from backup to "block-copy", including separate
>>> state structure and function renaming. This is needed to share it with
>>> backup-top filter driver in further commits.
>>>
>>> Notes:
>>>
>>> 1. As BlockCopyState keeps own BlockBackend objects, remaining
>>
>> I suppose these should be BdrvChild objects at some point, but doing it
>> now would just mean effectively duplicating code from block-backend.c.
>> (“now” = before we have a backup-top filter to attach the children to.)
> 
> How much is it bad to not do it, but leave them to be block-backends in block-copy
> state? They'll connected anyway through the job, as they all are in job.nodes.
> 
> We have block-backends in jobs currently, is it bad?

Yes.  First of all, it’s simply not how it should be.  It’s ugly.

Second, it does produce tangible problems.  One thing that comes to mind
is that we only need BB.disable_request_queuing because these
BlockBackends do not have a clear connection to the block job, which
means that the job may want to perform requests on drained BBs.

If source and target were children of the filter node, draining them
would first drain the job, and only then would they be marked as
quiesced, thus solving the problem (as far as I remember).

>>> job->common.blk users only use it to get bs by blk_bs() call, so clear
>>> job->commen.blk permissions set in block_job_create.
>>>
>>> 2. Rename s/initializing_bitmap/skip_unallocated/ to sound a bit better
>>> as interface to BlockCopyState
>>>
>>> 3. Split is not very clean: there left some duplicated fields, backup
>>
>> Are there any but cluster_size and len (and source, in a sense)?
> 
> Seems no more

Good, I was just asking because duplicated fields may be difficult to
keep in sync and so on.

[...]

>>> @@ -99,9 +118,83 @@ static void cow_request_end(CowRequest *req)
>>>       qemu_co_queue_restart_all(&req->wait_queue);
>>>   }
>>>   
>>> +static void block_copy_state_free(BlockCopyState *s)
>>> +{
>>> +    if (!s) {
>>> +        return;
>>> +    }
>>> +
>>> +    bdrv_release_dirty_bitmap(blk_bs(s->source), s->copy_bitmap);
>>> +    blk_unref(s->source);
>>> +    s->source = NULL;
>>> +    blk_unref(s->target);
>>> +    s->target = NULL;
>>
>> I’m not quite sure why you NULL these pointers when you free the whole
>> object next anyway.
> 
> it is for backup_drain, I'm afraid of some yield during blk_unref (and seems it's unsafe
> anyway, as I zero reference after calling blk_unref). Anyway,
> backup_drain will be dropped in "[PATCH v3] job: drop job_drain", I'll drop
> "= NULL" here now and workaround backup_drain in backup_clean with corresponding
> comment.

OK.

[...]

>>> +
>>> +    blk_set_disable_request_queuing(s->source, true);
>>> +    blk_set_allow_aio_context_change(s->source, true);
>>> +    blk_set_disable_request_queuing(s->target, true);
>>> +    blk_set_allow_aio_context_change(s->target, true);
>>
>> Hm.  Doesn’t creating new BBs here mean that we have to deal with the
>> fallout of changing the AioContext on either of them somewhere?
> 
> In backup context, backup job is responsible for keeping source and target bs
> in same context, so I think allowing blk to change aio context and assert in
> block_copy() that context is the same should be enough for now.

Hm, OK, and the backup job takes care of that through
child_job_set_aio_ctx() in blockjob.c.

But it should still be noted that on master, if you try to move e.g. the
source to a new context (by attaching a device to it), this happens:

qemu-system-x86_64: Cannot change iothread of active block backend

This comes from blk_root_can_set_aio_ctx(); but at the same time, it
will happily allow the context change if blk->allow_aio_context_change
is set – which is precisely what you do here.

So with this patch, the change is allowed; but the target is moved to
the new context, too.

So it should be noted that this is a change in behavior.  (Well, at
least I wanted to note it here.)

> I'll add a comment on this here.

By the way, this is another problem that we wouldn’t have if source and
target were BdrvChildren of backup-top.

(The problem being that the BBs’ contexts are kept in sync indirectly
through the node list attached to the job.)

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter
  2019-08-29 13:22     ` Vladimir Sementsov-Ogievskiy
@ 2019-09-02 16:10       ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-09-02 16:10 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha


[-- Attachment #1.1: Type: text/plain, Size: 1098 bytes --]

On 29.08.19 15:22, Vladimir Sementsov-Ogievskiy wrote:
> 28.08.2019 19:40, Max Reitz wrote:
>> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:

[...]

>>> +
>>> +    def get_bitmap(self, bitmaps, node_name, name, recording=None):
>>> +        """
>>> +        get a specific bitmap from the object returned by query_bitmaps.
>>> +        :param recording: If specified, filter results by the specified value.
>>> +        """
>>> +        if bitmaps is None:
>>> +            bitmaps = self.query_bitmaps()
>>> +
>>> +        for bitmap in bitmaps['bitmaps'][node_name]:
>>> +            if bitmap.get('name', '') == name:
>>> +                if recording is None:
>>> +                    return bitmap
>>> +                elif bitmap.get('recording') == recording:
>>> +                    return bitmap
>>
>> Maybe add a “break” or “return None” here?
>>
>> (Yes, yes, you just moved existing code.  Still.)
>>
> 
> No, as we may have several unnamed bitmaps, which should be selected by "recording"..

Ah.  OK.  Now it all makes sense...

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers
  2019-08-29 14:55     ` Vladimir Sementsov-Ogievskiy
@ 2019-09-02 16:34       ` Max Reitz
  2019-09-03  8:06         ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 39+ messages in thread
From: Max Reitz @ 2019-09-02 16:34 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha


[-- Attachment #1.1: Type: text/plain, Size: 7735 bytes --]

On 29.08.19 16:55, Vladimir Sementsov-Ogievskiy wrote:
> 28.08.2019 22:50, Max Reitz wrote:
>> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>>> Drop write notifiers and use filter node instead.
>>>
>>> = Changes =
>>>
>>> 1. add filter-node-name argument for backup qmp api. We have to do it
>>> in this commit, as 257 needs to be fixed.
>>
>> I feel a bit bad about it not being an implicit node.  But I know you
>> don’t like them, they’re probably just broken altogether and because
>> libvirt doesn’t use backup (yet), probably nobody cares.
>>
>>> 2. there no move write notifiers here, so is_write_notifier parameter
>>
>> s/there/there are/, I suppose?
>>
>>> is dropped from block-copy paths.
>>>
>>> 3. Intersecting requests handling changed, now synchronization between
>>> backup-top, backup and guest writes are all done in block/block-copy.c
>>> and works as follows:
>>>
>>> On copy operation, we work only with dirty areas. If bits are dirty it
>>> means that there are no requests intersecting with this area. We clear
>>> dirty bits and take bdrv range lock (bdrv_co_try_lock) on this area to
>>> prevent further operations from interaction with guest (only with
>>> guest, as neither backup nor backup-top will touch non-dirty area). If
>>> copy-operation failed we set dirty bits back together with releasing
>>> the lock.
>>>
>>> The actual difference with old scheme is that on guest writes we
>>> don't lock the whole region but only dirty-parts, and to be more
>>> precise: only dirty-part we are currently operate on. In old scheme
>>> guest write to non-dirty area (which may be safely ignored by backup)
>>> may wait for intersecting request, touching some other area which is
>>> dirty.
>>>
>>> 4. To sync with in-flight requests at job finish we now have drained
>>> removing of the filter, we don't need rw-lock.
>>>
>>> = Notes =
>>>
>>> Note the consequence of three objects appearing: backup-top, backup job
>>> and block-copy-state:
>>>
>>> 1. We want to insert backup-top before job creation, to behave similar
>>> with mirror and commit, where job is started upon filter.
>>>
>>> 2. We also have to create block-copy-state after filter injection, as
>>> we don't want it's source child be replaced by fitler. Instead we want
>>
>> s/it's/its/, s/filter/filter/ (although “fitler” does have an amusing
>> ring to it)
>>
>>> to keep BCS.source to be real source node, as we want to use
>>> bdrv_co_try_lock in CBW operations and it can't be used on filter, as
>>> on filter we already have in-flight (write) request from upper layer.
>>
>> Reasonable, even more so as I suppose BCS.source should eventually be a
>> BdrvChild of backup-top.
>>
>> What looks wrong is that the sync_bitmap is created on the source (“bs”
>> in backup_job_create()), but backup_cleanup_sync_bitmap() still assumes
>> it is on blk_bs(job->common.blk) (which is no longer true).
>>
>>> So, we firstly create inject backup-top, then create job and BCS. BCS
>>> is the latest just to not create extra variable for it. Finally we set
>>> bcs for backup-top filter.
>>>
>>> = Iotest changes =
>>>
>>> 56: op-blocker doesn't shot now, as we set it on source, but then check
>>
>> s/shot/show/?
>>
>>> on filter, when trying to start second backup, so error caught in
>>> test_dismiss_collision is changed. It's OK anyway, as this test-case
>>> seems to test that after some collision we can dismiss first job and
>>> successfully start the second one. So, the change is that collision is
>>> changed from op-blocker to file-posix locks.
>>
>> Well, but the op blocker belongs to the source, which should be covered
>> by internal permissions.  The fact that it now shows up as a file-posix
>> error thus shows that the conflict also moves from the source to the
>> target.  It’s OK because we actually don’t have a conflict on the source.
>>
>> But I wonder whether it would be better for test_dismiss_collision() to
>> do a blockdev-backup instead where we can see the collision on the target.
>>
>> Hm.  On second thought, why do we even get a conflict on the target?
>> block-copy does share the WRITE permission for it...
> 
> Not sure, but assume that this is because in file-posix.c in raw_co_create
> we do want RESIZE perm.
> 
> I can instead move this test to use specified job-id, to move the collision
> to "job-id already exists" error. Is it better?

It’s probably the only thing that actually makes sense.

> I'm afraid that posix locks will not work if disable them in config.

Yes (or if the environment doesn’t support them); which is why I
suggested using blockdev-backup.  (But that would have the same problem
of “Where does the permission conflict even come from and is it
inevitable?”)

[...]

>>> diff --git a/block/block-copy.c b/block/block-copy.c
>>> index 6828c46ba0..f3102ec3ff 100644
>>> --- a/block/block-copy.c
>>> +++ b/block/block-copy.c
>>> @@ -98,27 +98,32 @@ fail:
>>>    * 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 is_write_notifier,
>>> +        BlockCopyState *s, int64_t start, int64_t end,
>>>           bool *error_is_read, void **bounce_buffer)
>>>   {
>>>       int ret;
>>> -    int nbytes;
>>> -    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
>>> +    int nbytes = MIN(s->cluster_size, s->len - start);
>>> +    void *lock = bdrv_co_try_lock(blk_bs(s->source), start, nbytes);
>>> +
>>> +    /*
>>> +     * Function must be called only on full-dirty region, so nobody touching or
>>> +     * touched these bytes. Therefore, we must successfully get lock.
>>
>> Which is OK because backup-top does not share the WRITE permission.  But
>> it is organized a bit weirdly, because it’s actually block-copy here
>> that relies on the WRITE permission not to be shared; which it itself
>> cannot do because backup-top needs it.
>>
>> This of course results from the fact that backup-top and block-copy
>> should really use the same object to access the source, namely the
>> backing BdrvChild of backup-top.
>>
>> Maybe there should be a comment somewhere that points this out?
> 
> But I wanted block-copy not to be directly connected to backup-top.

But it doesn’t really work.  There’s an obscure contract that already
binds block-copy to backup-top.

> I think actually we rely on the fact that user of block-copy() guarantees that
> nobody is touching dirty areas (except block_copy). And with backup, this is done by
> backup-top filter. I'll add a comment on this.

Yes, that’s the contract.  And it’s weird, because block-copy has its
own BB, so if it doesn’t want anyone to write to the source outside of
its control, it should just unshare the WRITE permission – but it cannot
do that, because its user needs WRITE access, and so it implicitly
relies on that user to then unshare WRITE.

If both used the same single BdrvChild, it would be clear

(1) why the BdrvChild belongs to block-copy’s user (because block-copy’s
user would need to own a node that then has this BdrvChild),

(2) why block-copy does not unshare the WRITE permission (it cannot,
because it doesn’t create the BdrvChild),

(3) how block-copy could explicitly ensure that WRITE is unshared (by
asserting !(child->shared_perm & WRITE)).


I’m not saying that we need to abandon having BBs right now, but I think
there are a couple of cases which show why I say it’s uglier than using
BdrvChildren instead.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers
  2019-09-02 16:34       ` Max Reitz
@ 2019-09-03  8:06         ` Vladimir Sementsov-Ogievskiy
  2019-09-09  9:12           ` Max Reitz
  0 siblings, 1 reply; 39+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2019-09-03  8:06 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha

02.09.2019 19:34, Max Reitz wrote:
> On 29.08.19 16:55, Vladimir Sementsov-Ogievskiy wrote:
>> 28.08.2019 22:50, Max Reitz wrote:
>>> On 26.08.19 18:13, Vladimir Sementsov-Ogievskiy wrote:
>>>> Drop write notifiers and use filter node instead.
>>>>
>>>> = Changes =
>>>>
>>>> 1. add filter-node-name argument for backup qmp api. We have to do it
>>>> in this commit, as 257 needs to be fixed.
>>>
>>> I feel a bit bad about it not being an implicit node.  But I know you
>>> don’t like them, they’re probably just broken altogether and because
>>> libvirt doesn’t use backup (yet), probably nobody cares.
>>>
>>>> 2. there no move write notifiers here, so is_write_notifier parameter
>>>
>>> s/there/there are/, I suppose?
>>>
>>>> is dropped from block-copy paths.
>>>>
>>>> 3. Intersecting requests handling changed, now synchronization between
>>>> backup-top, backup and guest writes are all done in block/block-copy.c
>>>> and works as follows:
>>>>
>>>> On copy operation, we work only with dirty areas. If bits are dirty it
>>>> means that there are no requests intersecting with this area. We clear
>>>> dirty bits and take bdrv range lock (bdrv_co_try_lock) on this area to
>>>> prevent further operations from interaction with guest (only with
>>>> guest, as neither backup nor backup-top will touch non-dirty area). If
>>>> copy-operation failed we set dirty bits back together with releasing
>>>> the lock.
>>>>
>>>> The actual difference with old scheme is that on guest writes we
>>>> don't lock the whole region but only dirty-parts, and to be more
>>>> precise: only dirty-part we are currently operate on. In old scheme
>>>> guest write to non-dirty area (which may be safely ignored by backup)
>>>> may wait for intersecting request, touching some other area which is
>>>> dirty.
>>>>
>>>> 4. To sync with in-flight requests at job finish we now have drained
>>>> removing of the filter, we don't need rw-lock.
>>>>
>>>> = Notes =
>>>>
>>>> Note the consequence of three objects appearing: backup-top, backup job
>>>> and block-copy-state:
>>>>
>>>> 1. We want to insert backup-top before job creation, to behave similar
>>>> with mirror and commit, where job is started upon filter.
>>>>
>>>> 2. We also have to create block-copy-state after filter injection, as
>>>> we don't want it's source child be replaced by fitler. Instead we want
>>>
>>> s/it's/its/, s/filter/filter/ (although “fitler” does have an amusing
>>> ring to it)
>>>
>>>> to keep BCS.source to be real source node, as we want to use
>>>> bdrv_co_try_lock in CBW operations and it can't be used on filter, as
>>>> on filter we already have in-flight (write) request from upper layer.
>>>
>>> Reasonable, even more so as I suppose BCS.source should eventually be a
>>> BdrvChild of backup-top.
>>>
>>> What looks wrong is that the sync_bitmap is created on the source (“bs”
>>> in backup_job_create()), but backup_cleanup_sync_bitmap() still assumes
>>> it is on blk_bs(job->common.blk) (which is no longer true).
>>>
>>>> So, we firstly create inject backup-top, then create job and BCS. BCS
>>>> is the latest just to not create extra variable for it. Finally we set
>>>> bcs for backup-top filter.
>>>>
>>>> = Iotest changes =
>>>>
>>>> 56: op-blocker doesn't shot now, as we set it on source, but then check
>>>
>>> s/shot/show/?
>>>
>>>> on filter, when trying to start second backup, so error caught in
>>>> test_dismiss_collision is changed. It's OK anyway, as this test-case
>>>> seems to test that after some collision we can dismiss first job and
>>>> successfully start the second one. So, the change is that collision is
>>>> changed from op-blocker to file-posix locks.
>>>
>>> Well, but the op blocker belongs to the source, which should be covered
>>> by internal permissions.  The fact that it now shows up as a file-posix
>>> error thus shows that the conflict also moves from the source to the
>>> target.  It’s OK because we actually don’t have a conflict on the source.
>>>
>>> But I wonder whether it would be better for test_dismiss_collision() to
>>> do a blockdev-backup instead where we can see the collision on the target.
>>>
>>> Hm.  On second thought, why do we even get a conflict on the target?
>>> block-copy does share the WRITE permission for it...
>>
>> Not sure, but assume that this is because in file-posix.c in raw_co_create
>> we do want RESIZE perm.
>>
>> I can instead move this test to use specified job-id, to move the collision
>> to "job-id already exists" error. Is it better?
> 
> It’s probably the only thing that actually makes sense.
> 
>> I'm afraid that posix locks will not work if disable them in config.
> 
> Yes (or if the environment doesn’t support them); which is why I
> suggested using blockdev-backup.  (But that would have the same problem
> of “Where does the permission conflict even come from and is it
> inevitable?”)
> 
> [...]
> 
>>>> diff --git a/block/block-copy.c b/block/block-copy.c
>>>> index 6828c46ba0..f3102ec3ff 100644
>>>> --- a/block/block-copy.c
>>>> +++ b/block/block-copy.c
>>>> @@ -98,27 +98,32 @@ fail:
>>>>     * 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 is_write_notifier,
>>>> +        BlockCopyState *s, int64_t start, int64_t end,
>>>>            bool *error_is_read, void **bounce_buffer)
>>>>    {
>>>>        int ret;
>>>> -    int nbytes;
>>>> -    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
>>>> +    int nbytes = MIN(s->cluster_size, s->len - start);
>>>> +    void *lock = bdrv_co_try_lock(blk_bs(s->source), start, nbytes);
>>>> +
>>>> +    /*
>>>> +     * Function must be called only on full-dirty region, so nobody touching or
>>>> +     * touched these bytes. Therefore, we must successfully get lock.
>>>
>>> Which is OK because backup-top does not share the WRITE permission.  But
>>> it is organized a bit weirdly, because it’s actually block-copy here
>>> that relies on the WRITE permission not to be shared; which it itself
>>> cannot do because backup-top needs it.
>>>
>>> This of course results from the fact that backup-top and block-copy
>>> should really use the same object to access the source, namely the
>>> backing BdrvChild of backup-top.
>>>
>>> Maybe there should be a comment somewhere that points this out?
>>
>> But I wanted block-copy not to be directly connected to backup-top.
> 
> But it doesn’t really work.  There’s an obscure contract that already
> binds block-copy to backup-top.
> 
>> I think actually we rely on the fact that user of block-copy() guarantees that
>> nobody is touching dirty areas (except block_copy). And with backup, this is done by
>> backup-top filter. I'll add a comment on this.
> 
> Yes, that’s the contract.  And it’s weird, because block-copy has its
> own BB, so if it doesn’t want anyone to write

doesn't want anyone to write to dirty areas, and we don't have such option in the permission
system...

> to the source outside of
> its control, it should just unshare the WRITE permission – but it cannot
> do that, because its user needs WRITE access, and so it implicitly
> relies on that user to then unshare WRITE.
> 
> If both used the same single BdrvChild, it would be clear
> 
> (1) why the BdrvChild belongs to block-copy’s user (because block-copy’s
> user would need to own a node that then has this BdrvChild),
> 
> (2) why block-copy does not unshare the WRITE permission (it cannot,
> because it doesn’t create the BdrvChild),
> 
> (3) how block-copy could explicitly ensure that WRITE is unshared (by
> asserting !(child->shared_perm & WRITE)).
> 
> 
> I’m not saying that we need to abandon having BBs right now, but I think
> there are a couple of cases which show why I say it’s uglier than using
> BdrvChildren instead.
> 

OK. I'd prefer to move block-copy to BdrvChildren as follow-up, if you don't mind.


-- 
Best regards,
Vladimir

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers
  2019-09-03  8:06         ` Vladimir Sementsov-Ogievskiy
@ 2019-09-09  9:12           ` Max Reitz
  0 siblings, 0 replies; 39+ messages in thread
From: Max Reitz @ 2019-09-09  9:12 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, Denis Lunev, wencongyang2, xiechanglong.d,
	qemu-devel, armbru, jsnow, stefanha


[-- Attachment #1.1: Type: text/plain, Size: 428 bytes --]

On 03.09.19 10:06, Vladimir Sementsov-Ogievskiy wrote:
> 02.09.2019 19:34, Max Reitz wrote:

[...]

>> I’m not saying that we need to abandon having BBs right now, but I think
>> there are a couple of cases which show why I say it’s uglier than using
>> BdrvChildren instead.
>>
> 
> OK. I'd prefer to move block-copy to BdrvChildren as follow-up, if you don't mind.

Yep, sure, that’s A-OK with me.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2019-09-09  9:13 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-26 16:12 [Qemu-devel] [PATCH v9 00/13] backup-top filter driver for backup Vladimir Sementsov-Ogievskiy
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 01/13] block/backup: fix backup_cow_with_offload for last cluster Vladimir Sementsov-Ogievskiy
2019-08-28 14:08   ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 02/13] block/backup: split shareable copying part from backup_do_cow Vladimir Sementsov-Ogievskiy
2019-08-28 14:22   ` Max Reitz
2019-08-28 14:27     ` Vladimir Sementsov-Ogievskiy
2019-08-28 14:32       ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 03/13] block/backup: introduce BlockCopyState Vladimir Sementsov-Ogievskiy
2019-08-28 15:59   ` Max Reitz
2019-08-29 10:52     ` Vladimir Sementsov-Ogievskiy
2019-09-02 16:09       ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 04/13] block/backup: adjust block-copy functions style Vladimir Sementsov-Ogievskiy
2019-08-28 16:06   ` Max Reitz
2019-08-29 11:25     ` Vladimir Sementsov-Ogievskiy
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 05/13] block: move block_copy from block/backup.c to separate file Vladimir Sementsov-Ogievskiy
2019-08-28 16:16   ` Max Reitz
2019-08-29 12:00     ` Vladimir Sementsov-Ogievskiy
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 06/13] block: teach bdrv_debug_breakpoint skip filters with backing Vladimir Sementsov-Ogievskiy
2019-08-28 16:21   ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 07/13] iotests: prepare 124 and 257 bitmap querying for backup-top filter Vladimir Sementsov-Ogievskiy
2019-08-28 16:40   ` Max Reitz
2019-08-29 13:22     ` Vladimir Sementsov-Ogievskiy
2019-09-02 16:10       ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 08/13] iotests: 257: drop unused Drive.device field Vladimir Sementsov-Ogievskiy
2019-08-28 16:50   ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 09/13] iotests: 257: drop device_add Vladimir Sementsov-Ogievskiy
2019-08-28 16:54   ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 10/13] block/io: refactor wait_serialising_requests Vladimir Sementsov-Ogievskiy
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 11/13] block: add lock/unlock range functions Vladimir Sementsov-Ogievskiy
2019-08-28 17:02   ` Max Reitz
2019-08-29  9:17     ` Vladimir Sementsov-Ogievskiy
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 12/13] block: introduce backup-top filter driver Vladimir Sementsov-Ogievskiy
2019-08-28 17:52   ` Max Reitz
2019-08-26 16:13 ` [Qemu-devel] [PATCH v9 13/13] block/backup: use backup-top instead of write notifiers Vladimir Sementsov-Ogievskiy
2019-08-28 19:50   ` Max Reitz
2019-08-29 14:55     ` Vladimir Sementsov-Ogievskiy
2019-09-02 16:34       ` Max Reitz
2019-09-03  8:06         ` Vladimir Sementsov-Ogievskiy
2019-09-09  9:12           ` Max Reitz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.