1
The following changes since commit 31ee895047bdcf7387e3570cbd2a473c6f744b08:
1
The following changes since commit 474f3938d79ab36b9231c9ad3b5a9314c2aeacde:
2
2
3
Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging (2021-01-25 15:56:13 +0000)
3
Merge remote-tracking branch 'remotes/amarkovic/tags/mips-queue-jun-21-2019' into staging (2019-06-21 15:40:50 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/XanClic/qemu.git tags/pull-block-2021-01-26
7
https://github.com/XanClic/qemu.git tags/pull-block-2019-06-24
8
8
9
for you to fetch changes up to bb24cdc5efee580e81f71c5ff0fd980f2cc179d0:
9
for you to fetch changes up to ab5d4a30f7f3803ca5106b370969c1b7b54136f8:
10
10
11
iotests/178: Pass value to invalid option (2021-01-26 14:36:37 +0100)
11
iotests: Fix 205 for concurrent runs (2019-06-24 16:01:40 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Block patches:
15
- Make backup block jobs use asynchronous requests with the block-copy
15
- The SSH block driver now uses libssh instead of libssh2
16
module
16
- The VMDK block driver gets read-only support for the seSparse
17
- Use COR filter node for stream block jobs
17
subformat
18
- Make coroutine-sigaltstack’s qemu_coroutine_new() function thread-safe
18
- Various fixes
19
- Report error string when file locking fails with an unexpected errno
19
20
- iotest fixes, additions, and some refactoring
20
---
21
22
v2:
23
- Squashed Pino's fix for pre-0.8 libssh into the libssh patch
21
24
22
----------------------------------------------------------------
25
----------------------------------------------------------------
23
Alberto Garcia (1):
26
Anton Nefedov (1):
24
iotests: Add test for the regression fixed in c8bf9a9169
27
iotest 134: test cluster-misaligned encrypted write
25
28
26
Andrey Shinkevich (10):
29
Klaus Birkelund Jensen (1):
27
copy-on-read: support preadv/pwritev_part functions
30
nvme: do not advertise support for unsupported arbitration mechanism
28
block: add API function to insert a node
29
copy-on-read: add filter drop function
30
qapi: add filter-node-name to block-stream
31
qapi: copy-on-read filter: add 'bottom' option
32
iotests: add #310 to test bottom node in COR driver
33
block: include supported_read_flags into BDS structure
34
copy-on-read: skip non-guest reads if no copy needed
35
stream: rework backing-file changing
36
block: apply COR-filter to block-stream jobs
37
31
38
David Edmondson (1):
32
Max Reitz (1):
39
block: report errno when flock fcntl fails
33
iotests: Fix 205 for concurrent runs
40
34
41
Max Reitz (14):
35
Pino Toscano (1):
42
iotests.py: Assume a couple of variables as given
36
ssh: switch from libssh2 to libssh
43
iotests/297: Rewrite in Python and extend reach
44
iotests: Move try_remove to iotests.py
45
iotests/129: Remove test images in tearDown()
46
iotests/129: Do not check @busy
47
iotests/129: Use throttle node
48
iotests/129: Actually test a commit job
49
iotests/129: Limit mirror job's buffer size
50
iotests/129: Clean up pylint and mypy complaints
51
iotests/300: Clean up pylint and mypy complaints
52
coroutine-sigaltstack: Add SIGUSR2 mutex
53
iotests/129: Limit backup's max-chunk/max-workers
54
iotests/118: Drop 'change' test
55
iotests/178: Pass value to invalid option
56
37
57
Vladimir Sementsov-Ogievskiy (27):
38
Sam Eiderman (3):
58
iotests: fix _check_o_direct
39
vmdk: Fix comment regarding max l1_size coverage
59
qapi: block-stream: add "bottom" argument
40
vmdk: Reduce the max bound for L1 table size
60
iotests: 30: prepare to COR filter insertion by stream job
41
vmdk: Add read-only support for seSparse snapshots
61
block/stream: add s->target_bs
62
qapi: backup: add perf.use-copy-range parameter
63
block/block-copy: More explicit call_state
64
block/block-copy: implement block_copy_async
65
block/block-copy: add max_chunk and max_workers parameters
66
block/block-copy: add list of all call-states
67
block/block-copy: add ratelimit to block-copy
68
block/block-copy: add block_copy_cancel
69
blockjob: add set_speed to BlockJobDriver
70
job: call job_enter from job_pause
71
qapi: backup: add max-chunk and max-workers to x-perf struct
72
iotests: 56: prepare for backup over block-copy
73
iotests: 185: prepare for backup over block-copy
74
iotests: 219: prepare for backup over block-copy
75
iotests: 257: prepare for backup over block-copy
76
block/block-copy: make progress_bytes_callback optional
77
block/backup: drop extra gotos from backup_run()
78
backup: move to block-copy
79
qapi: backup: disable copy_range by default
80
block/block-copy: drop unused block_copy_set_progress_callback()
81
block/block-copy: drop unused argument of block_copy()
82
simplebench/bench_block_job: use correct shebang line with python3
83
simplebench: bench_block_job: add cmd_options argument
84
simplebench: add bench-backup.py
85
42
86
qapi/block-core.json | 66 +++++-
43
Vladimir Sementsov-Ogievskiy (1):
87
block/backup-top.h | 1 +
44
blockdev: enable non-root nodes for transaction drive-backup source
88
block/copy-on-read.h | 32 +++
45
89
include/block/block-copy.h | 61 ++++-
46
configure | 65 +-
90
include/block/block.h | 10 +-
47
block/Makefile.objs | 6 +-
91
include/block/block_int.h | 15 +-
48
block/ssh.c | 652 ++++++++++--------
92
include/block/blockjob_int.h | 2 +
49
block/vmdk.c | 372 +++++++++-
93
block.c | 25 ++
50
blockdev.c | 2 +-
94
block/backup-top.c | 6 +-
51
hw/block/nvme.c | 1 -
95
block/backup.c | 233 ++++++++++++-------
52
.travis.yml | 4 +-
96
block/block-copy.c | 227 +++++++++++++++---
53
block/trace-events | 14 +-
97
block/copy-on-read.c | 184 ++++++++++++++-
54
docs/qemu-block-drivers.texi | 2 +-
98
block/file-posix.c | 38 ++-
55
.../dockerfiles/debian-win32-cross.docker | 1 -
99
block/io.c | 10 +-
56
.../dockerfiles/debian-win64-cross.docker | 1 -
100
block/monitor/block-hmp-cmds.c | 7 +-
57
tests/docker/dockerfiles/fedora.docker | 4 +-
101
block/replication.c | 2 +
58
tests/docker/dockerfiles/ubuntu.docker | 2 +-
102
block/stream.c | 185 +++++++++------
59
tests/docker/dockerfiles/ubuntu1804.docker | 2 +-
103
blockdev.c | 83 +++++--
60
tests/qemu-iotests/059.out | 2 +-
104
blockjob.c | 6 +
61
tests/qemu-iotests/134 | 9 +
105
job.c | 3 +
62
tests/qemu-iotests/134.out | 10 +
106
util/coroutine-sigaltstack.c | 9 +
63
tests/qemu-iotests/205 | 2 +-
107
scripts/simplebench/bench-backup.py | 167 ++++++++++++++
64
tests/qemu-iotests/207 | 54 +-
108
scripts/simplebench/bench-example.py | 2 +-
65
tests/qemu-iotests/207.out | 2 +-
109
scripts/simplebench/bench_block_job.py | 13 +-
66
20 files changed, 823 insertions(+), 384 deletions(-)
110
tests/qemu-iotests/030 | 12 +-
111
tests/qemu-iotests/056 | 9 +-
112
tests/qemu-iotests/109.out | 24 ++
113
tests/qemu-iotests/118 | 20 +-
114
tests/qemu-iotests/118.out | 4 +-
115
tests/qemu-iotests/124 | 8 +-
116
tests/qemu-iotests/129 | 79 ++++---
117
tests/qemu-iotests/141.out | 2 +-
118
tests/qemu-iotests/178 | 2 +-
119
tests/qemu-iotests/178.out.qcow2 | 2 +-
120
tests/qemu-iotests/178.out.raw | 2 +-
121
tests/qemu-iotests/185 | 3 +-
122
tests/qemu-iotests/185.out | 3 +-
123
tests/qemu-iotests/219 | 13 +-
124
tests/qemu-iotests/245 | 20 +-
125
tests/qemu-iotests/257 | 1 +
126
tests/qemu-iotests/257.out | 306 ++++++++++++-------------
127
tests/qemu-iotests/297 | 112 +++++++--
128
tests/qemu-iotests/297.out | 5 +-
129
tests/qemu-iotests/300 | 19 +-
130
tests/qemu-iotests/310 | 117 ++++++++++
131
tests/qemu-iotests/310.out | 15 ++
132
tests/qemu-iotests/313 | 104 +++++++++
133
tests/qemu-iotests/313.out | 29 +++
134
tests/qemu-iotests/common.rc | 7 +-
135
tests/qemu-iotests/group | 2 +
136
tests/qemu-iotests/iotests.py | 37 +--
137
51 files changed, 1797 insertions(+), 547 deletions(-)
138
create mode 100644 block/copy-on-read.h
139
create mode 100755 scripts/simplebench/bench-backup.py
140
create mode 100755 tests/qemu-iotests/310
141
create mode 100644 tests/qemu-iotests/310.out
142
create mode 100755 tests/qemu-iotests/313
143
create mode 100644 tests/qemu-iotests/313.out
144
67
145
--
68
--
146
2.29.2
69
2.21.0
147
70
148
71
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Unfortunately commit "iotests: handle tmpfs" breaks running iotests
4
with -nbd -nocache, as _check_o_direct tries to create
5
$TEST_IMG.test_o_direct, but in case of nbd TEST_IMG is something like
6
nbd+unix:///... , and test fails with message
7
8
qemu-img: nbd+unix:///?socket[...]test_o_direct: Protocol driver
9
'nbd' does not support image creation, and opening the image
10
failed: Failed to connect to '/tmp/tmp.[...]/nbd/test_o_direct': No
11
such file or directory
12
13
Use TEST_DIR instead.
14
15
Fixes: cfdca2b9f9d4ca26bb2b2dfe8de3149092e39170
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Message-Id: <20201218182012.47607-1-vsementsov@virtuozzo.com>
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
---
20
tests/qemu-iotests/common.rc | 7 ++++---
21
1 file changed, 4 insertions(+), 3 deletions(-)
22
23
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
24
index XXXXXXX..XXXXXXX 100644
25
--- a/tests/qemu-iotests/common.rc
26
+++ b/tests/qemu-iotests/common.rc
27
@@ -XXX,XX +XXX,XX @@ _supported_cache_modes()
28
# Check whether the filesystem supports O_DIRECT
29
_check_o_direct()
30
{
31
- $QEMU_IMG create -f raw "$TEST_IMG".test_o_direct 1M > /dev/null
32
- out=$($QEMU_IO -f raw -t none -c quit "$TEST_IMG".test_o_direct 2>&1)
33
- rm -f "$TEST_IMG".test_o_direct
34
+ testfile="$TEST_DIR"/_check_o_direct
35
+ $QEMU_IMG create -f raw "$testfile" 1M > /dev/null
36
+ out=$($QEMU_IO -f raw -t none -c quit "$testfile" 2>&1)
37
+ rm -f "$testfile"
38
39
[[ "$out" != *"O_DIRECT"* ]]
40
}
41
--
42
2.29.2
43
44
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
Add support for the recently introduced functions
4
bdrv_co_preadv_part()
5
and
6
bdrv_co_pwritev_part()
7
to the COR-filter driver.
8
9
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Message-Id: <20201216061703.70908-2-vsementsov@virtuozzo.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
14
block/copy-on-read.c | 28 ++++++++++++++++------------
15
1 file changed, 16 insertions(+), 12 deletions(-)
16
17
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/copy-on-read.c
20
+++ b/block/copy-on-read.c
21
@@ -XXX,XX +XXX,XX @@ static int64_t cor_getlength(BlockDriverState *bs)
22
}
23
24
25
-static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
26
- uint64_t offset, uint64_t bytes,
27
- QEMUIOVector *qiov, int flags)
28
+static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs,
29
+ uint64_t offset, uint64_t bytes,
30
+ QEMUIOVector *qiov,
31
+ size_t qiov_offset,
32
+ int flags)
33
{
34
- return bdrv_co_preadv(bs->file, offset, bytes, qiov,
35
- flags | BDRV_REQ_COPY_ON_READ);
36
+ return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
37
+ flags | BDRV_REQ_COPY_ON_READ);
38
}
39
40
41
-static int coroutine_fn cor_co_pwritev(BlockDriverState *bs,
42
- uint64_t offset, uint64_t bytes,
43
- QEMUIOVector *qiov, int flags)
44
+static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs,
45
+ uint64_t offset,
46
+ uint64_t bytes,
47
+ QEMUIOVector *qiov,
48
+ size_t qiov_offset, int flags)
49
{
50
-
51
- return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
52
+ return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
53
+ flags);
54
}
55
56
57
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
58
59
.bdrv_getlength = cor_getlength,
60
61
- .bdrv_co_preadv = cor_co_preadv,
62
- .bdrv_co_pwritev = cor_co_pwritev,
63
+ .bdrv_co_preadv_part = cor_co_preadv_part,
64
+ .bdrv_co_pwritev_part = cor_co_pwritev_part,
65
.bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes,
66
.bdrv_co_pdiscard = cor_co_pdiscard,
67
.bdrv_co_pwritev_compressed = cor_co_pwritev_compressed,
68
--
69
2.29.2
70
71
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
Provide API for insertion a node to backing chain.
4
5
Suggested-by: Max Reitz <mreitz@redhat.com>
6
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20201216061703.70908-3-vsementsov@virtuozzo.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
include/block/block.h | 2 ++
13
block.c | 25 +++++++++++++++++++++++++
14
2 files changed, 27 insertions(+)
15
16
diff --git a/include/block/block.h b/include/block/block.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block.h
19
+++ b/include/block/block.h
20
@@ -XXX,XX +XXX,XX @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
21
Error **errp);
22
void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
23
Error **errp);
24
+BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
25
+ int flags, Error **errp);
26
27
int bdrv_parse_aio(const char *mode, int *flags);
28
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
29
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
32
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_delete(BlockDriverState *bs)
34
g_free(bs);
35
}
36
37
+BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
38
+ int flags, Error **errp)
39
+{
40
+ BlockDriverState *new_node_bs;
41
+ Error *local_err = NULL;
42
+
43
+ new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp);
44
+ if (new_node_bs == NULL) {
45
+ error_prepend(errp, "Could not create node: ");
46
+ return NULL;
47
+ }
48
+
49
+ bdrv_drained_begin(bs);
50
+ bdrv_replace_node(bs, new_node_bs, &local_err);
51
+ bdrv_drained_end(bs);
52
+
53
+ if (local_err) {
54
+ bdrv_unref(new_node_bs);
55
+ error_propagate(errp, local_err);
56
+ return NULL;
57
+ }
58
+
59
+ return new_node_bs;
60
+}
61
+
62
/*
63
* Run consistency checks on an image
64
*
65
--
66
2.29.2
67
68
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
Provide API for the COR-filter removal. Also, drop the filter child
4
permissions for an inactive state when the filter node is being
5
removed.
6
To insert the filter, the block generic layer function
7
bdrv_insert_node() can be used.
8
The new function bdrv_cor_filter_drop() may be considered as an
9
intermediate solution before the QEMU permission update system has
10
overhauled. Then we are able to implement the API function
11
bdrv_remove_node() on the block generic layer.
12
13
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
16
Message-Id: <20201216061703.70908-4-vsementsov@virtuozzo.com>
17
Signed-off-by: Max Reitz <mreitz@redhat.com>
18
---
19
block/copy-on-read.h | 32 +++++++++++++++++++++++++
20
block/copy-on-read.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
21
2 files changed, 88 insertions(+)
22
create mode 100644 block/copy-on-read.h
23
24
diff --git a/block/copy-on-read.h b/block/copy-on-read.h
25
new file mode 100644
26
index XXXXXXX..XXXXXXX
27
--- /dev/null
28
+++ b/block/copy-on-read.h
29
@@ -XXX,XX +XXX,XX @@
30
+/*
31
+ * Copy-on-read filter block driver
32
+ *
33
+ * The filter driver performs Copy-On-Read (COR) operations
34
+ *
35
+ * Copyright (c) 2018-2020 Virtuozzo International GmbH.
36
+ *
37
+ * Author:
38
+ * Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
39
+ *
40
+ * This program is free software; you can redistribute it and/or modify
41
+ * it under the terms of the GNU General Public License as published by
42
+ * the Free Software Foundation; either version 2 of the License, or
43
+ * (at your option) any later version.
44
+ *
45
+ * This program is distributed in the hope that it will be useful,
46
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
47
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48
+ * GNU General Public License for more details.
49
+ *
50
+ * You should have received a copy of the GNU General Public License
51
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
52
+ */
53
+
54
+#ifndef BLOCK_COPY_ON_READ
55
+#define BLOCK_COPY_ON_READ
56
+
57
+#include "block/block_int.h"
58
+
59
+void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs);
60
+
61
+#endif /* BLOCK_COPY_ON_READ */
62
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
63
index XXXXXXX..XXXXXXX 100644
64
--- a/block/copy-on-read.c
65
+++ b/block/copy-on-read.c
66
@@ -XXX,XX +XXX,XX @@
67
#include "qemu/osdep.h"
68
#include "block/block_int.h"
69
#include "qemu/module.h"
70
+#include "qapi/error.h"
71
+#include "block/copy-on-read.h"
72
+
73
+
74
+typedef struct BDRVStateCOR {
75
+ bool active;
76
+} BDRVStateCOR;
77
78
79
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
80
Error **errp)
81
{
82
+ BDRVStateCOR *state = bs->opaque;
83
+
84
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
85
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
86
false, errp);
87
@@ -XXX,XX +XXX,XX @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
88
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
89
bs->file->bs->supported_zero_flags);
90
91
+ state->active = true;
92
+
93
+ /*
94
+ * We don't need to call bdrv_child_refresh_perms() now as the permissions
95
+ * will be updated later when the filter node gets its parent.
96
+ */
97
+
98
return 0;
99
}
100
101
@@ -XXX,XX +XXX,XX @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
102
uint64_t perm, uint64_t shared,
103
uint64_t *nperm, uint64_t *nshared)
104
{
105
+ BDRVStateCOR *s = bs->opaque;
106
+
107
+ if (!s->active) {
108
+ /*
109
+ * While the filter is being removed
110
+ */
111
+ *nperm = 0;
112
+ *nshared = BLK_PERM_ALL;
113
+ return;
114
+ }
115
+
116
*nperm = perm & PERM_PASSTHROUGH;
117
*nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
118
119
@@ -XXX,XX +XXX,XX @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
120
121
static BlockDriver bdrv_copy_on_read = {
122
.format_name = "copy-on-read",
123
+ .instance_size = sizeof(BDRVStateCOR),
124
125
.bdrv_open = cor_open,
126
.bdrv_child_perm = cor_child_perm,
127
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
128
.is_filter = true,
129
};
130
131
+
132
+void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs)
133
+{
134
+ BdrvChild *child;
135
+ BlockDriverState *bs;
136
+ BDRVStateCOR *s = cor_filter_bs->opaque;
137
+
138
+ child = bdrv_filter_child(cor_filter_bs);
139
+ if (!child) {
140
+ return;
141
+ }
142
+ bs = child->bs;
143
+
144
+ /* Retain the BDS until we complete the graph change. */
145
+ bdrv_ref(bs);
146
+ /* Hold a guest back from writing while permissions are being reset. */
147
+ bdrv_drained_begin(bs);
148
+ /* Drop permissions before the graph change. */
149
+ s->active = false;
150
+ bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort);
151
+ bdrv_replace_node(cor_filter_bs, bs, &error_abort);
152
+
153
+ bdrv_drained_end(bs);
154
+ bdrv_unref(bs);
155
+ bdrv_unref(cor_filter_bs);
156
+}
157
+
158
+
159
static void bdrv_copy_on_read_init(void)
160
{
161
bdrv_register(&bdrv_copy_on_read);
162
--
163
2.29.2
164
165
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
Provide the possibility to pass the 'filter-node-name' parameter to the
4
block-stream job as it is done for the commit block job.
5
6
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
[vsementsov: comment indentation, s/Since: 5.2/Since: 6.0/]
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Message-Id: <20201216061703.70908-5-vsementsov@virtuozzo.com>
11
[mreitz: s/commit/stream/]
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
14
qapi/block-core.json | 6 ++++++
15
include/block/block_int.h | 7 ++++++-
16
block/monitor/block-hmp-cmds.c | 4 ++--
17
block/stream.c | 4 +++-
18
blockdev.c | 4 +++-
19
5 files changed, 20 insertions(+), 5 deletions(-)
20
21
diff --git a/qapi/block-core.json b/qapi/block-core.json
22
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/block-core.json
24
+++ b/qapi/block-core.json
25
@@ -XXX,XX +XXX,XX @@
26
# 'stop' and 'enospc' can only be used if the block device
27
# supports io-status (see BlockInfo). Since 1.3.
28
#
29
+# @filter-node-name: the node name that should be assigned to the
30
+# filter driver that the stream job inserts into the graph
31
+# above @device. If this option is not given, a node name is
32
+# autogenerated. (Since: 6.0)
33
+#
34
# @auto-finalize: When false, this job will wait in a PENDING state after it has
35
# finished its work, waiting for @block-job-finalize before
36
# making any block graph changes.
37
@@ -XXX,XX +XXX,XX @@
38
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
39
'*base-node': 'str', '*backing-file': 'str', '*speed': 'int',
40
'*on-error': 'BlockdevOnError',
41
+ '*filter-node-name': 'str',
42
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
43
44
##
45
diff --git a/include/block/block_int.h b/include/block/block_int.h
46
index XXXXXXX..XXXXXXX 100644
47
--- a/include/block/block_int.h
48
+++ b/include/block/block_int.h
49
@@ -XXX,XX +XXX,XX @@ int is_windows_drive(const char *filename);
50
* See @BlockJobCreateFlags
51
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
52
* @on_error: The action to take upon error.
53
+ * @filter_node_name: The node name that should be assigned to the filter
54
+ * driver that the stream job inserts into the graph above
55
+ * @bs. NULL means that a node name should be autogenerated.
56
* @errp: Error object.
57
*
58
* Start a streaming operation on @bs. Clusters that are unallocated
59
@@ -XXX,XX +XXX,XX @@ int is_windows_drive(const char *filename);
60
void stream_start(const char *job_id, BlockDriverState *bs,
61
BlockDriverState *base, const char *backing_file_str,
62
int creation_flags, int64_t speed,
63
- BlockdevOnError on_error, Error **errp);
64
+ BlockdevOnError on_error,
65
+ const char *filter_node_name,
66
+ Error **errp);
67
68
/**
69
* commit_start:
70
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/block/monitor/block-hmp-cmds.c
73
+++ b/block/monitor/block-hmp-cmds.c
74
@@ -XXX,XX +XXX,XX @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
75
76
qmp_block_stream(true, device, device, base != NULL, base, false, NULL,
77
false, NULL, qdict_haskey(qdict, "speed"), speed, true,
78
- BLOCKDEV_ON_ERROR_REPORT, false, false, false, false,
79
- &error);
80
+ BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false,
81
+ false, &error);
82
83
hmp_handle_error(mon, error);
84
}
85
diff --git a/block/stream.c b/block/stream.c
86
index XXXXXXX..XXXXXXX 100644
87
--- a/block/stream.c
88
+++ b/block/stream.c
89
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
90
void stream_start(const char *job_id, BlockDriverState *bs,
91
BlockDriverState *base, const char *backing_file_str,
92
int creation_flags, int64_t speed,
93
- BlockdevOnError on_error, Error **errp)
94
+ BlockdevOnError on_error,
95
+ const char *filter_node_name,
96
+ Error **errp)
97
{
98
StreamBlockJob *s;
99
BlockDriverState *iter;
100
diff --git a/blockdev.c b/blockdev.c
101
index XXXXXXX..XXXXXXX 100644
102
--- a/blockdev.c
103
+++ b/blockdev.c
104
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
105
bool has_backing_file, const char *backing_file,
106
bool has_speed, int64_t speed,
107
bool has_on_error, BlockdevOnError on_error,
108
+ bool has_filter_node_name, const char *filter_node_name,
109
bool has_auto_finalize, bool auto_finalize,
110
bool has_auto_dismiss, bool auto_dismiss,
111
Error **errp)
112
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
113
}
114
115
stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
116
- job_flags, has_speed ? speed : 0, on_error, &local_err);
117
+ job_flags, has_speed ? speed : 0, on_error,
118
+ filter_node_name, &local_err);
119
if (local_err) {
120
error_propagate(errp, local_err);
121
goto out;
122
--
123
2.29.2
124
125
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
Add an option to limit copy-on-read operations to specified sub-chain
4
of backing-chain, to make copy-on-read filter useful for block-stream
5
job.
6
7
Suggested-by: Max Reitz <mreitz@redhat.com>
8
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
[vsementsov: change subject, modified to freeze the chain,
12
do some fixes]
13
Message-Id: <20201216061703.70908-6-vsementsov@virtuozzo.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
qapi/block-core.json | 20 ++++++++-
17
block/copy-on-read.c | 98 +++++++++++++++++++++++++++++++++++++++++++-
18
2 files changed, 115 insertions(+), 3 deletions(-)
19
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
22
--- a/qapi/block-core.json
23
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
25
'data': { 'throttle-group': 'str',
26
'file' : 'BlockdevRef'
27
} }
28
+
29
+##
30
+# @BlockdevOptionsCor:
31
+#
32
+# Driver specific block device options for the copy-on-read driver.
33
+#
34
+# @bottom: The name of a non-filter node (allocation-bearing layer) that
35
+# limits the COR operations in the backing chain (inclusive), so
36
+# that no data below this node will be copied by this filter.
37
+# If option is absent, the limit is not applied, so that data
38
+# from all backing layers may be copied.
39
+#
40
+# Since: 6.0
41
+##
42
+{ 'struct': 'BlockdevOptionsCor',
43
+ 'base': 'BlockdevOptionsGenericFormat',
44
+ 'data': { '*bottom': 'str' } }
45
+
46
##
47
# @BlockdevOptions:
48
#
49
@@ -XXX,XX +XXX,XX @@
50
'bochs': 'BlockdevOptionsGenericFormat',
51
'cloop': 'BlockdevOptionsGenericFormat',
52
'compress': 'BlockdevOptionsGenericFormat',
53
- 'copy-on-read':'BlockdevOptionsGenericFormat',
54
+ 'copy-on-read':'BlockdevOptionsCor',
55
'dmg': 'BlockdevOptionsGenericFormat',
56
'file': 'BlockdevOptionsFile',
57
'ftp': 'BlockdevOptionsCurlFtp',
58
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
59
index XXXXXXX..XXXXXXX 100644
60
--- a/block/copy-on-read.c
61
+++ b/block/copy-on-read.c
62
@@ -XXX,XX +XXX,XX @@
63
#include "block/block_int.h"
64
#include "qemu/module.h"
65
#include "qapi/error.h"
66
+#include "qapi/qmp/qdict.h"
67
#include "block/copy-on-read.h"
68
69
70
typedef struct BDRVStateCOR {
71
bool active;
72
+ BlockDriverState *bottom_bs;
73
+ bool chain_frozen;
74
} BDRVStateCOR;
75
76
77
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
78
Error **errp)
79
{
80
+ BlockDriverState *bottom_bs = NULL;
81
BDRVStateCOR *state = bs->opaque;
82
+ /* Find a bottom node name, if any */
83
+ const char *bottom_node = qdict_get_try_str(options, "bottom");
84
85
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
86
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
87
@@ -XXX,XX +XXX,XX @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
88
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
89
bs->file->bs->supported_zero_flags);
90
91
+ if (bottom_node) {
92
+ bottom_bs = bdrv_find_node(bottom_node);
93
+ if (!bottom_bs) {
94
+ error_setg(errp, "Bottom node '%s' not found", bottom_node);
95
+ qdict_del(options, "bottom");
96
+ return -EINVAL;
97
+ }
98
+ qdict_del(options, "bottom");
99
+
100
+ if (!bottom_bs->drv) {
101
+ error_setg(errp, "Bottom node '%s' not opened", bottom_node);
102
+ return -EINVAL;
103
+ }
104
+
105
+ if (bottom_bs->drv->is_filter) {
106
+ error_setg(errp, "Bottom node '%s' is a filter", bottom_node);
107
+ return -EINVAL;
108
+ }
109
+
110
+ if (bdrv_freeze_backing_chain(bs, bottom_bs, errp) < 0) {
111
+ return -EINVAL;
112
+ }
113
+ state->chain_frozen = true;
114
+
115
+ /*
116
+ * We do freeze the chain, so it shouldn't be removed. Still, storing a
117
+ * pointer worth bdrv_ref().
118
+ */
119
+ bdrv_ref(bottom_bs);
120
+ }
121
state->active = true;
122
+ state->bottom_bs = bottom_bs;
123
124
/*
125
* We don't need to call bdrv_child_refresh_perms() now as the permissions
126
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs,
127
size_t qiov_offset,
128
int flags)
129
{
130
- return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
131
- flags | BDRV_REQ_COPY_ON_READ);
132
+ int64_t n;
133
+ int local_flags;
134
+ int ret;
135
+ BDRVStateCOR *state = bs->opaque;
136
+
137
+ if (!state->bottom_bs) {
138
+ return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
139
+ flags | BDRV_REQ_COPY_ON_READ);
140
+ }
141
+
142
+ while (bytes) {
143
+ local_flags = flags;
144
+
145
+ /* In case of failure, try to copy-on-read anyway */
146
+ ret = bdrv_is_allocated(bs->file->bs, offset, bytes, &n);
147
+ if (ret <= 0) {
148
+ ret = bdrv_is_allocated_above(bdrv_backing_chain_next(bs->file->bs),
149
+ state->bottom_bs, true, offset,
150
+ n, &n);
151
+ if (ret > 0 || ret < 0) {
152
+ local_flags |= BDRV_REQ_COPY_ON_READ;
153
+ }
154
+ /* Finish earlier if the end of a backing file has been reached */
155
+ if (n == 0) {
156
+ break;
157
+ }
158
+ }
159
+
160
+ ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset,
161
+ local_flags);
162
+ if (ret < 0) {
163
+ return ret;
164
+ }
165
+
166
+ offset += n;
167
+ qiov_offset += n;
168
+ bytes -= n;
169
+ }
170
+
171
+ return 0;
172
}
173
174
175
@@ -XXX,XX +XXX,XX @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
176
}
177
178
179
+static void cor_close(BlockDriverState *bs)
180
+{
181
+ BDRVStateCOR *s = bs->opaque;
182
+
183
+ if (s->chain_frozen) {
184
+ s->chain_frozen = false;
185
+ bdrv_unfreeze_backing_chain(bs, s->bottom_bs);
186
+ }
187
+
188
+ bdrv_unref(s->bottom_bs);
189
+}
190
+
191
+
192
static BlockDriver bdrv_copy_on_read = {
193
.format_name = "copy-on-read",
194
.instance_size = sizeof(BDRVStateCOR),
195
196
.bdrv_open = cor_open,
197
+ .bdrv_close = cor_close,
198
.bdrv_child_perm = cor_child_perm,
199
200
.bdrv_getlength = cor_getlength,
201
@@ -XXX,XX +XXX,XX @@ void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs)
202
bdrv_drained_begin(bs);
203
/* Drop permissions before the graph change. */
204
s->active = false;
205
+ /* unfreeze, as otherwise bdrv_replace_node() will fail */
206
+ if (s->chain_frozen) {
207
+ s->chain_frozen = false;
208
+ bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs);
209
+ }
210
bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort);
211
bdrv_replace_node(cor_filter_bs, bs, &error_abort);
212
213
--
214
2.29.2
215
216
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
The test case #310 is similar to #216 by Max Reitz. The difference is
4
that the test #310 involves a bottom node to the COR filter driver.
5
6
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
[vsementsov: detach backing to test reads from top, limit to qcow2]
9
Message-Id: <20201216061703.70908-7-vsementsov@virtuozzo.com>
10
[mreitz: Add "# group:" line]
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
tests/qemu-iotests/310 | 117 +++++++++++++++++++++++++++++++++++++
14
tests/qemu-iotests/310.out | 15 +++++
15
tests/qemu-iotests/group | 1 +
16
3 files changed, 133 insertions(+)
17
create mode 100755 tests/qemu-iotests/310
18
create mode 100644 tests/qemu-iotests/310.out
19
20
diff --git a/tests/qemu-iotests/310 b/tests/qemu-iotests/310
21
new file mode 100755
22
index XXXXXXX..XXXXXXX
23
--- /dev/null
24
+++ b/tests/qemu-iotests/310
25
@@ -XXX,XX +XXX,XX @@
26
+#!/usr/bin/env python3
27
+# group: rw quick
28
+#
29
+# Copy-on-read tests using a COR filter with a bottom node
30
+#
31
+# Copyright (C) 2018 Red Hat, Inc.
32
+# Copyright (c) 2020 Virtuozzo International GmbH
33
+#
34
+# This program is free software; you can redistribute it and/or modify
35
+# it under the terms of the GNU General Public License as published by
36
+# the Free Software Foundation; either version 2 of the License, or
37
+# (at your option) any later version.
38
+#
39
+# This program is distributed in the hope that it will be useful,
40
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
41
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42
+# GNU General Public License for more details.
43
+#
44
+# You should have received a copy of the GNU General Public License
45
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
46
+#
47
+
48
+import iotests
49
+from iotests import log, qemu_img, qemu_io_silent
50
+
51
+# Need backing file support
52
+iotests.script_initialize(supported_fmts=['qcow2'],
53
+ supported_platforms=['linux'])
54
+
55
+log('')
56
+log('=== Copy-on-read across nodes ===')
57
+log('')
58
+
59
+# This test is similar to the 216 one by Max Reitz <mreitz@redhat.com>
60
+# The difference is that this test case involves a bottom node to the
61
+# COR filter driver.
62
+
63
+with iotests.FilePath('base.img') as base_img_path, \
64
+ iotests.FilePath('mid.img') as mid_img_path, \
65
+ iotests.FilePath('top.img') as top_img_path, \
66
+ iotests.VM() as vm:
67
+
68
+ log('--- Setting up images ---')
69
+ log('')
70
+
71
+ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
72
+ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
73
+ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 3M 1M') == 0
74
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
75
+ '-F', iotests.imgfmt, mid_img_path) == 0
76
+ assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 2M 1M') == 0
77
+ assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 4M 1M') == 0
78
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path,
79
+ '-F', iotests.imgfmt, top_img_path) == 0
80
+ assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
81
+
82
+# 0 1 2 3 4
83
+# top 2
84
+# mid 3 3
85
+# base 1 1
86
+
87
+ log('Done')
88
+
89
+ log('')
90
+ log('--- Doing COR ---')
91
+ log('')
92
+
93
+ vm.launch()
94
+
95
+ log(vm.qmp('blockdev-add',
96
+ node_name='node0',
97
+ driver='copy-on-read',
98
+ bottom='node2',
99
+ file={
100
+ 'driver': iotests.imgfmt,
101
+ 'file': {
102
+ 'driver': 'file',
103
+ 'filename': top_img_path
104
+ },
105
+ 'backing': {
106
+ 'node-name': 'node2',
107
+ 'driver': iotests.imgfmt,
108
+ 'file': {
109
+ 'driver': 'file',
110
+ 'filename': mid_img_path
111
+ },
112
+ 'backing': {
113
+ 'driver': iotests.imgfmt,
114
+ 'file': {
115
+ 'driver': 'file',
116
+ 'filename': base_img_path
117
+ }
118
+ },
119
+ }
120
+ }))
121
+
122
+ # Trigger COR
123
+ log(vm.qmp('human-monitor-command',
124
+ command_line='qemu-io node0 "read 0 5M"'))
125
+
126
+ vm.shutdown()
127
+
128
+ log('')
129
+ log('--- Checking COR result ---')
130
+ log('')
131
+
132
+ # Detach backing to check that we can read the data from the top level now
133
+ assert qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt,
134
+ top_img_path) == 0
135
+
136
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0
137
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
138
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0
139
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0
140
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0
141
+
142
+ log('Done')
143
diff --git a/tests/qemu-iotests/310.out b/tests/qemu-iotests/310.out
144
new file mode 100644
145
index XXXXXXX..XXXXXXX
146
--- /dev/null
147
+++ b/tests/qemu-iotests/310.out
148
@@ -XXX,XX +XXX,XX @@
149
+
150
+=== Copy-on-read across nodes ===
151
+
152
+--- Setting up images ---
153
+
154
+Done
155
+
156
+--- Doing COR ---
157
+
158
+{"return": {}}
159
+{"return": ""}
160
+
161
+--- Checking COR result ---
162
+
163
+Done
164
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
165
index XXXXXXX..XXXXXXX 100644
166
--- a/tests/qemu-iotests/group
167
+++ b/tests/qemu-iotests/group
168
@@ -XXX,XX +XXX,XX @@
169
307 rw quick export
170
308 rw
171
309 rw auto quick
172
+310 rw quick
173
312 rw quick
174
--
175
2.29.2
176
177
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
Add the new member supported_read_flags to the BlockDriverState
4
structure. It will control the flags set for copy-on-read operations.
5
Make the block generic layer evaluate supported read flags before they
6
go to a block driver.
7
8
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
[vsementsov: use assert instead of abort]
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20201216061703.70908-8-vsementsov@virtuozzo.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
include/block/block_int.h | 4 ++++
17
block/io.c | 10 ++++++++--
18
2 files changed, 12 insertions(+), 2 deletions(-)
19
20
diff --git a/include/block/block_int.h b/include/block/block_int.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/block_int.h
23
+++ b/include/block/block_int.h
24
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
25
/* I/O Limits */
26
BlockLimits bl;
27
28
+ /*
29
+ * Flags honored during pread
30
+ */
31
+ unsigned int supported_read_flags;
32
/* Flags honored during pwrite (so far: BDRV_REQ_FUA,
33
* BDRV_REQ_WRITE_UNCHANGED).
34
* If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those
35
diff --git a/block/io.c b/block/io.c
36
index XXXXXXX..XXXXXXX 100644
37
--- a/block/io.c
38
+++ b/block/io.c
39
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
40
if (flags & BDRV_REQ_COPY_ON_READ) {
41
int64_t pnum;
42
43
+ /* The flag BDRV_REQ_COPY_ON_READ has reached its addressee */
44
+ flags &= ~BDRV_REQ_COPY_ON_READ;
45
+
46
ret = bdrv_is_allocated(bs, offset, bytes, &pnum);
47
if (ret < 0) {
48
goto out;
49
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
50
goto out;
51
}
52
53
+ assert(!(flags & ~bs->supported_read_flags));
54
+
55
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
56
if (bytes <= max_bytes && bytes <= max_transfer) {
57
- ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, 0);
58
+ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, flags);
59
goto out;
60
}
61
62
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
63
64
ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
65
num, qiov,
66
- qiov_offset + bytes - bytes_remaining, 0);
67
+ qiov_offset + bytes - bytes_remaining,
68
+ flags);
69
max_bytes -= num;
70
} else {
71
num = bytes_remaining;
72
--
73
2.29.2
74
75
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
If the flag BDRV_REQ_PREFETCH was set, skip idling read/write
4
operations in COR-driver. It can be taken into account for the
5
COR-algorithms optimization. That check is being made during the
6
block stream job by the moment.
7
8
Add the BDRV_REQ_PREFETCH flag to the supported_read_flags of the
9
COR-filter.
10
11
block: Modify the comment for the flag BDRV_REQ_PREFETCH as we are
12
going to use it alone and pass it to the COR-filter driver for further
13
processing.
14
15
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
16
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
18
Message-Id: <20201216061703.70908-9-vsementsov@virtuozzo.com>
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
---
21
include/block/block.h | 8 +++++---
22
block/copy-on-read.c | 14 ++++++++++----
23
2 files changed, 15 insertions(+), 7 deletions(-)
24
25
diff --git a/include/block/block.h b/include/block/block.h
26
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/block.h
28
+++ b/include/block/block.h
29
@@ -XXX,XX +XXX,XX @@ typedef enum {
30
BDRV_REQ_NO_FALLBACK = 0x100,
31
32
/*
33
- * BDRV_REQ_PREFETCH may be used only together with BDRV_REQ_COPY_ON_READ
34
- * on read request and means that caller doesn't really need data to be
35
- * written to qiov parameter which may be NULL.
36
+ * BDRV_REQ_PREFETCH makes sense only in the context of copy-on-read
37
+ * (i.e., together with the BDRV_REQ_COPY_ON_READ flag or when a COR
38
+ * filter is involved), in which case it signals that the COR operation
39
+ * need not read the data into memory (qiov) but only ensure they are
40
+ * copied to the top layer (i.e., that COR operation is done).
41
*/
42
BDRV_REQ_PREFETCH = 0x200,
43
44
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block/copy-on-read.c
47
+++ b/block/copy-on-read.c
48
@@ -XXX,XX +XXX,XX @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
49
return -EINVAL;
50
}
51
52
+ bs->supported_read_flags = BDRV_REQ_PREFETCH;
53
+
54
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
55
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
56
57
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs,
58
}
59
}
60
61
- ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset,
62
- local_flags);
63
- if (ret < 0) {
64
- return ret;
65
+ /* Skip if neither read nor write are needed */
66
+ if ((local_flags & (BDRV_REQ_PREFETCH | BDRV_REQ_COPY_ON_READ)) !=
67
+ BDRV_REQ_PREFETCH) {
68
+ ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset,
69
+ local_flags);
70
+ if (ret < 0) {
71
+ return ret;
72
+ }
73
}
74
75
offset += n;
76
--
77
2.29.2
78
79
diff view generated by jsdifflib
1
From: David Edmondson <david.edmondson@oracle.com>
1
From: Klaus Birkelund Jensen <klaus@birkelund.eu>
2
2
3
When a call to fcntl(2) for the purpose of adding file locks fails
3
The device mistakenly reports that the Weighted Round Robin with Urgent
4
with an error other than EAGAIN or EACCES, report the error returned
4
Priority Class arbitration mechanism is supported.
5
by fcntl.
6
5
7
EAGAIN or EACCES are elided as they are considered to be common
6
It is not.
8
failures, indicating that a conflicting lock is held by another
9
process.
10
7
11
No errors are elided when removing file locks.
8
Signed-off-by: Klaus Birkelund Jensen <klaus.jensen@cnexlabs.com>
12
9
Message-id: 20190606092530.14206-1-klaus@birkelund.eu
13
Signed-off-by: David Edmondson <david.edmondson@oracle.com>
10
Acked-by: Maxim Levitsky <mlevitsk@redhat.com>
14
Message-Id: <20210113164447.2545785-1-david.edmondson@oracle.com>
15
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
---
12
---
18
block/file-posix.c | 38 ++++++++++++++++++++++++++++----------
13
hw/block/nvme.c | 1 -
19
1 file changed, 28 insertions(+), 10 deletions(-)
14
1 file changed, 1 deletion(-)
20
15
21
diff --git a/block/file-posix.c b/block/file-posix.c
16
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
22
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
23
--- a/block/file-posix.c
18
--- a/hw/block/nvme.c
24
+++ b/block/file-posix.c
19
+++ b/hw/block/nvme.c
25
@@ -XXX,XX +XXX,XX @@ typedef struct RawPosixAIOData {
20
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
26
static int cdrom_reopen(BlockDriverState *bs);
21
n->bar.cap = 0;
27
#endif
22
NVME_CAP_SET_MQES(n->bar.cap, 0x7ff);
28
23
NVME_CAP_SET_CQR(n->bar.cap, 1);
29
+/*
24
- NVME_CAP_SET_AMS(n->bar.cap, 1);
30
+ * Elide EAGAIN and EACCES details when failing to lock, as this
25
NVME_CAP_SET_TO(n->bar.cap, 0xf);
31
+ * indicates that the specified file region is already locked by
26
NVME_CAP_SET_CSS(n->bar.cap, 1);
32
+ * another process, which is considered a common scenario.
27
NVME_CAP_SET_MPSMAX(n->bar.cap, 4);
33
+ */
34
+#define raw_lock_error_setg_errno(errp, err, fmt, ...) \
35
+ do { \
36
+ if ((err) == EAGAIN || (err) == EACCES) { \
37
+ error_setg((errp), (fmt), ## __VA_ARGS__); \
38
+ } else { \
39
+ error_setg_errno((errp), (err), (fmt), ## __VA_ARGS__); \
40
+ } \
41
+ } while (0)
42
+
43
#if defined(__NetBSD__)
44
static int raw_normalize_devicepath(const char **filename, Error **errp)
45
{
46
@@ -XXX,XX +XXX,XX @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
47
if ((perm_lock_bits & bit) && !(locked_perm & bit)) {
48
ret = qemu_lock_fd(fd, off, 1, false);
49
if (ret) {
50
- error_setg(errp, "Failed to lock byte %d", off);
51
+ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d",
52
+ off);
53
return ret;
54
} else if (s) {
55
s->locked_perm |= bit;
56
@@ -XXX,XX +XXX,XX @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
57
} else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) {
58
ret = qemu_unlock_fd(fd, off, 1);
59
if (ret) {
60
- error_setg(errp, "Failed to unlock byte %d", off);
61
+ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off);
62
return ret;
63
} else if (s) {
64
s->locked_perm &= ~bit;
65
@@ -XXX,XX +XXX,XX @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
66
if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) {
67
ret = qemu_lock_fd(fd, off, 1, false);
68
if (ret) {
69
- error_setg(errp, "Failed to lock byte %d", off);
70
+ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d",
71
+ off);
72
return ret;
73
} else if (s) {
74
s->locked_shared_perm |= bit;
75
@@ -XXX,XX +XXX,XX @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
76
!(shared_perm_lock_bits & bit)) {
77
ret = qemu_unlock_fd(fd, off, 1);
78
if (ret) {
79
- error_setg(errp, "Failed to unlock byte %d", off);
80
+ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off);
81
return ret;
82
} else if (s) {
83
s->locked_shared_perm &= ~bit;
84
@@ -XXX,XX +XXX,XX @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
85
ret = qemu_lock_fd_test(fd, off, 1, true);
86
if (ret) {
87
char *perm_name = bdrv_perm_names(p);
88
- error_setg(errp,
89
- "Failed to get \"%s\" lock",
90
- perm_name);
91
+
92
+ raw_lock_error_setg_errno(errp, -ret,
93
+ "Failed to get \"%s\" lock",
94
+ perm_name);
95
g_free(perm_name);
96
return ret;
97
}
98
@@ -XXX,XX +XXX,XX @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
99
ret = qemu_lock_fd_test(fd, off, 1, true);
100
if (ret) {
101
char *perm_name = bdrv_perm_names(p);
102
- error_setg(errp,
103
- "Failed to get shared \"%s\" lock",
104
- perm_name);
105
+
106
+ raw_lock_error_setg_errno(errp, -ret,
107
+ "Failed to get shared \"%s\" lock",
108
+ perm_name);
109
g_free(perm_name);
110
return ret;
111
}
112
--
28
--
113
2.29.2
29
2.21.0
114
30
115
31
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The code already don't freeze base node and we try to make it prepared
3
We forget to enable it for transaction .prepare, while it is already
4
for the situation when base node is changed during the operation. In
4
enabled in do_drive_backup since commit a2d665c1bc362
5
other words, block-stream doesn't own base node.
5
"blockdev: loosen restrictions on drive-backup source node"
6
7
Let's introduce a new interface which should replace the current one,
8
which will in better relations with the code. Specifying bottom node
9
instead of base, and requiring it to be non-filter gives us the
10
following benefits:
11
12
- drop difference between above_base and base_overlay, which will be
13
renamed to just bottom, when old interface dropped
14
15
- clean way to work with parallel streams/commits on the same backing
16
chain, which otherwise become a problem when we introduce a filter
17
for stream job
18
19
- cleaner interface. Nobody will surprised the fact that base node may
20
disappear during block-stream, when there is no word about "base" in
21
the interface.
22
6
23
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
24
Message-Id: <20201216061703.70908-11-vsementsov@virtuozzo.com>
8
Message-id: 20190618140804.59214-1-vsementsov@virtuozzo.com
25
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: John Snow <jsnow@redhat.com>
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
---
11
---
28
qapi/block-core.json | 12 ++++---
12
blockdev.c | 2 +-
29
include/block/block_int.h | 1 +
13
1 file changed, 1 insertion(+), 1 deletion(-)
30
block/monitor/block-hmp-cmds.c | 3 +-
31
block/stream.c | 50 +++++++++++++++++++---------
32
blockdev.c | 59 ++++++++++++++++++++++++++++------
33
5 files changed, 94 insertions(+), 31 deletions(-)
34
14
35
diff --git a/qapi/block-core.json b/qapi/block-core.json
36
index XXXXXXX..XXXXXXX 100644
37
--- a/qapi/block-core.json
38
+++ b/qapi/block-core.json
39
@@ -XXX,XX +XXX,XX @@
40
# @device: the device or node name of the top image
41
#
42
# @base: the common backing file name.
43
-# It cannot be set if @base-node is also set.
44
+# It cannot be set if @base-node or @bottom is also set.
45
#
46
# @base-node: the node name of the backing file.
47
-# It cannot be set if @base is also set. (Since 2.8)
48
+# It cannot be set if @base or @bottom is also set. (Since 2.8)
49
+#
50
+# @bottom: the last node in the chain that should be streamed into
51
+# top. It cannot be set if @base or @base-node is also set.
52
+# It cannot be filter node. (Since 6.0)
53
#
54
# @backing-file: The backing file string to write into the top
55
# image. This filename is not validated.
56
@@ -XXX,XX +XXX,XX @@
57
##
58
{ 'command': 'block-stream',
59
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
60
- '*base-node': 'str', '*backing-file': 'str', '*speed': 'int',
61
- '*on-error': 'BlockdevOnError',
62
+ '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str',
63
+ '*speed': 'int', '*on-error': 'BlockdevOnError',
64
'*filter-node-name': 'str',
65
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
66
67
diff --git a/include/block/block_int.h b/include/block/block_int.h
68
index XXXXXXX..XXXXXXX 100644
69
--- a/include/block/block_int.h
70
+++ b/include/block/block_int.h
71
@@ -XXX,XX +XXX,XX @@ int is_windows_drive(const char *filename);
72
*/
73
void stream_start(const char *job_id, BlockDriverState *bs,
74
BlockDriverState *base, const char *backing_file_str,
75
+ BlockDriverState *bottom,
76
int creation_flags, int64_t speed,
77
BlockdevOnError on_error,
78
const char *filter_node_name,
79
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/block/monitor/block-hmp-cmds.c
82
+++ b/block/monitor/block-hmp-cmds.c
83
@@ -XXX,XX +XXX,XX @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
84
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
85
86
qmp_block_stream(true, device, device, base != NULL, base, false, NULL,
87
- false, NULL, qdict_haskey(qdict, "speed"), speed, true,
88
+ false, NULL, false, NULL,
89
+ qdict_haskey(qdict, "speed"), speed, true,
90
BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false,
91
false, &error);
92
93
diff --git a/block/stream.c b/block/stream.c
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/stream.c
96
+++ b/block/stream.c
97
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
98
99
void stream_start(const char *job_id, BlockDriverState *bs,
100
BlockDriverState *base, const char *backing_file_str,
101
+ BlockDriverState *bottom,
102
int creation_flags, int64_t speed,
103
BlockdevOnError on_error,
104
const char *filter_node_name,
105
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
106
BlockDriverState *iter;
107
bool bs_read_only;
108
int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
109
- BlockDriverState *base_overlay = bdrv_find_overlay(bs, base);
110
+ BlockDriverState *base_overlay;
111
BlockDriverState *above_base;
112
113
- if (!base_overlay) {
114
- error_setg(errp, "'%s' is not in the backing chain of '%s'",
115
- base->node_name, bs->node_name);
116
- return;
117
- }
118
+ assert(!(base && bottom));
119
+ assert(!(backing_file_str && bottom));
120
+
121
+ if (bottom) {
122
+ /*
123
+ * New simple interface. The code is written in terms of old interface
124
+ * with @base parameter (still, it doesn't freeze link to base, so in
125
+ * this mean old code is correct for new interface). So, for now, just
126
+ * emulate base_overlay and above_base. Still, when old interface
127
+ * finally removed, we should refactor code to use only "bottom", but
128
+ * not "*base*" things.
129
+ */
130
+ assert(!bottom->drv->is_filter);
131
+ base_overlay = above_base = bottom;
132
+ } else {
133
+ base_overlay = bdrv_find_overlay(bs, base);
134
+ if (!base_overlay) {
135
+ error_setg(errp, "'%s' is not in the backing chain of '%s'",
136
+ base->node_name, bs->node_name);
137
+ return;
138
+ }
139
140
- /*
141
- * Find the node directly above @base. @base_overlay is a COW overlay, so
142
- * it must have a bdrv_cow_child(), but it is the immediate overlay of
143
- * @base, so between the two there can only be filters.
144
- */
145
- above_base = base_overlay;
146
- if (bdrv_cow_bs(above_base) != base) {
147
- above_base = bdrv_cow_bs(above_base);
148
- while (bdrv_filter_bs(above_base) != base) {
149
- above_base = bdrv_filter_bs(above_base);
150
+ /*
151
+ * Find the node directly above @base. @base_overlay is a COW overlay,
152
+ * so it must have a bdrv_cow_child(), but it is the immediate overlay
153
+ * of @base, so between the two there can only be filters.
154
+ */
155
+ above_base = base_overlay;
156
+ if (bdrv_cow_bs(above_base) != base) {
157
+ above_base = bdrv_cow_bs(above_base);
158
+ while (bdrv_filter_bs(above_base) != base) {
159
+ above_base = bdrv_filter_bs(above_base);
160
+ }
161
}
162
}
163
164
diff --git a/blockdev.c b/blockdev.c
15
diff --git a/blockdev.c b/blockdev.c
165
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
166
--- a/blockdev.c
17
--- a/blockdev.c
167
+++ b/blockdev.c
18
+++ b/blockdev.c
168
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
19
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
169
bool has_base, const char *base,
20
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
170
bool has_base_node, const char *base_node,
21
backup = common->action->u.drive_backup.data;
171
bool has_backing_file, const char *backing_file,
22
172
+ bool has_bottom, const char *bottom,
23
- bs = qmp_get_root_bs(backup->device, errp);
173
bool has_speed, int64_t speed,
24
+ bs = bdrv_lookup_bs(backup->device, backup->device, errp);
174
bool has_on_error, BlockdevOnError on_error,
25
if (!bs) {
175
bool has_filter_node_name, const char *filter_node_name,
26
return;
176
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
177
bool has_auto_dismiss, bool auto_dismiss,
178
Error **errp)
179
{
180
- BlockDriverState *bs, *iter;
181
+ BlockDriverState *bs, *iter, *iter_end;
182
BlockDriverState *base_bs = NULL;
183
+ BlockDriverState *bottom_bs = NULL;
184
AioContext *aio_context;
185
Error *local_err = NULL;
186
int job_flags = JOB_DEFAULT;
187
188
+ if (has_base && has_base_node) {
189
+ error_setg(errp, "'base' and 'base-node' cannot be specified "
190
+ "at the same time");
191
+ return;
192
+ }
193
+
194
+ if (has_base && has_bottom) {
195
+ error_setg(errp, "'base' and 'bottom' cannot be specified "
196
+ "at the same time");
197
+ return;
198
+ }
199
+
200
+ if (has_bottom && has_base_node) {
201
+ error_setg(errp, "'bottom' and 'base-node' cannot be specified "
202
+ "at the same time");
203
+ return;
204
+ }
205
+
206
if (!has_on_error) {
207
on_error = BLOCKDEV_ON_ERROR_REPORT;
208
}
27
}
209
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
210
aio_context = bdrv_get_aio_context(bs);
211
aio_context_acquire(aio_context);
212
213
- if (has_base && has_base_node) {
214
- error_setg(errp, "'base' and 'base-node' cannot be specified "
215
- "at the same time");
216
- goto out;
217
- }
218
-
219
if (has_base) {
220
base_bs = bdrv_find_backing_image(bs, base);
221
if (base_bs == NULL) {
222
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
223
bdrv_refresh_filename(base_bs);
224
}
225
226
- /* Check for op blockers in the whole chain between bs and base */
227
- for (iter = bs; iter && iter != base_bs;
228
+ if (has_bottom) {
229
+ bottom_bs = bdrv_lookup_bs(NULL, bottom, errp);
230
+ if (!bottom_bs) {
231
+ goto out;
232
+ }
233
+ if (!bottom_bs->drv) {
234
+ error_setg(errp, "Node '%s' is not open", bottom);
235
+ goto out;
236
+ }
237
+ if (bottom_bs->drv->is_filter) {
238
+ error_setg(errp, "Node '%s' is a filter, use a non-filter node "
239
+ "as 'bottom'", bottom);
240
+ goto out;
241
+ }
242
+ if (!bdrv_chain_contains(bs, bottom_bs)) {
243
+ error_setg(errp, "Node '%s' is not in a chain starting from '%s'",
244
+ bottom, device);
245
+ goto out;
246
+ }
247
+ assert(bdrv_get_aio_context(bottom_bs) == aio_context);
248
+ }
249
+
250
+ /*
251
+ * Check for op blockers in the whole chain between bs and base (or bottom)
252
+ */
253
+ iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
254
+ for (iter = bs; iter && iter != iter_end;
255
iter = bdrv_filter_or_cow_bs(iter))
256
{
257
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
258
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
259
}
260
261
stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
262
- job_flags, has_speed ? speed : 0, on_error,
263
+ bottom_bs, job_flags, has_speed ? speed : 0, on_error,
264
filter_node_name, &local_err);
265
if (local_err) {
266
error_propagate(errp, local_err);
267
--
28
--
268
2.29.2
29
2.21.0
269
30
270
31
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Anton Nefedov <anton.nefedov@virtuozzo.com>
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
3
COW (even empty/zero) areas require encryption too
4
Suggested-by: Maxim Levitsky <mlevitsk@redhat.com>
4
5
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
5
Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com>
6
Message-Id: <20210112170540.2912-1-berto@igalia.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
[mreitz: Add "# group:" line]
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
Message-id: 20190516143028.81155-1-anton.nefedov@virtuozzo.com
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
11
---
10
tests/qemu-iotests/313 | 104 +++++++++++++++++++++++++++++++++++++
12
tests/qemu-iotests/134 | 9 +++++++++
11
tests/qemu-iotests/313.out | 29 +++++++++++
13
tests/qemu-iotests/134.out | 10 ++++++++++
12
tests/qemu-iotests/group | 1 +
14
2 files changed, 19 insertions(+)
13
3 files changed, 134 insertions(+)
14
create mode 100755 tests/qemu-iotests/313
15
create mode 100644 tests/qemu-iotests/313.out
16
15
17
diff --git a/tests/qemu-iotests/313 b/tests/qemu-iotests/313
16
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
18
new file mode 100755
17
index XXXXXXX..XXXXXXX 100755
19
index XXXXXXX..XXXXXXX
18
--- a/tests/qemu-iotests/134
20
--- /dev/null
19
+++ b/tests/qemu-iotests/134
21
+++ b/tests/qemu-iotests/313
20
@@ -XXX,XX +XXX,XX @@ echo
22
@@ -XXX,XX +XXX,XX @@
21
echo "== reading whole image =="
23
+#!/usr/bin/env bash
22
$QEMU_IO --object $SECRET -c "read 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
24
+# group: rw auto quick
23
25
+#
24
+echo
26
+# Test for the regression fixed in commit c8bf9a9169
25
+echo "== rewriting cluster part =="
27
+#
26
+$QEMU_IO --object $SECRET -c "write -P 0xb 512 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
28
+# Copyright (C) 2020 Igalia, S.L.
29
+# Author: Alberto Garcia <berto@igalia.com>
30
+# Based on a test case by Maxim Levitsky <mlevitsk@redhat.com>
31
+#
32
+# This program is free software; you can redistribute it and/or modify
33
+# it under the terms of the GNU General Public License as published by
34
+# the Free Software Foundation; either version 2 of the License, or
35
+# (at your option) any later version.
36
+#
37
+# This program is distributed in the hope that it will be useful,
38
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
39
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40
+# GNU General Public License for more details.
41
+#
42
+# You should have received a copy of the GNU General Public License
43
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
44
+#
45
+
46
+# creator
47
+owner=berto@igalia.com
48
+
49
+seq=`basename $0`
50
+echo "QA output created by $seq"
51
+
52
+status=1 # failure is the default!
53
+
54
+_cleanup()
55
+{
56
+ _cleanup_test_img
57
+}
58
+trap "_cleanup; exit \$status" 0 1 2 3 15
59
+
60
+# get standard environment, filters and checks
61
+. ./common.rc
62
+. ./common.filter
63
+
64
+_supported_fmt qcow2
65
+_supported_proto file
66
+_supported_os Linux
67
+_unsupported_imgopts cluster_size refcount_bits extended_l2 compat=0.10 data_file
68
+
69
+# The cluster size must be at least the granularity of the mirror job (4KB)
70
+# Note that larger cluster sizes will produce very large images (several GBs)
71
+cluster_size=4096
72
+refcount_bits=64 # Make it equal to the L2 entry size for convenience
73
+options="cluster_size=${cluster_size},refcount_bits=${refcount_bits}"
74
+
75
+# Number of refcount entries per refcount blocks
76
+ref_entries=$(( ${cluster_size} * 8 / ${refcount_bits} ))
77
+
78
+# Number of data clusters needed to fill a refcount block
79
+# Equals ${ref_entries} minus two (one L2 table and one refcount block)
80
+data_clusters_per_refblock=$(( ${ref_entries} - 2 ))
81
+
82
+# Number of entries in the refcount cache
83
+ref_blocks=4
84
+
85
+# Write enough data clusters to fill the refcount cache and allocate
86
+# one more refcount block.
87
+# Subtract 3 clusters from the total: qcow2 header, refcount table, L1 table
88
+total_data_clusters=$(( ${data_clusters_per_refblock} * ${ref_blocks} + 1 - 3 ))
89
+
90
+# Total size to write in bytes
91
+total_size=$(( ${total_data_clusters} * ${cluster_size} ))
92
+
27
+
93
+echo
28
+echo
94
+echo '### Create the image'
29
+echo "== verify pattern =="
95
+echo
30
+$QEMU_IO --object $SECRET -c "read -P 0 0 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
96
+TEST_IMG_FILE=$TEST_IMG.base _make_test_img -o $options $total_size | _filter_img_create_size
31
+$QEMU_IO --object $SECRET -c "read -P 0xb 512 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
97
+
32
+
98
+echo
33
echo
99
+echo '### Write data to allocate more refcount blocks than the cache can hold'
34
echo "== rewriting whole image =="
100
+echo
35
$QEMU_IO --object $SECRET -c "write -P 0xa 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
101
+$QEMU_IO -c "write -P 1 0 $total_size" $TEST_IMG.base | _filter_qemu_io
36
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
37
index XXXXXXX..XXXXXXX 100644
38
--- a/tests/qemu-iotests/134.out
39
+++ b/tests/qemu-iotests/134.out
40
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.
41
read 134217728/134217728 bytes at offset 0
42
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
43
44
+== rewriting cluster part ==
45
+wrote 512/512 bytes at offset 512
46
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
102
+
47
+
103
+echo
48
+== verify pattern ==
104
+echo '### Create an overlay'
49
+read 512/512 bytes at offset 0
105
+echo
50
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
106
+_make_test_img -F $IMGFMT -b $TEST_IMG.base -o $options | _filter_img_create_size
51
+read 512/512 bytes at offset 512
52
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
107
+
53
+
108
+echo
54
== rewriting whole image ==
109
+echo '### Fill the overlay with zeroes'
55
wrote 134217728/134217728 bytes at offset 0
110
+echo
56
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
111
+$QEMU_IO -c "write -z 0 $total_size" $TEST_IMG | _filter_qemu_io
112
+
113
+echo
114
+echo '### Commit changes to the base image'
115
+echo
116
+$QEMU_IMG commit $TEST_IMG
117
+
118
+echo
119
+echo '### Check the base image'
120
+echo
121
+$QEMU_IMG check $TEST_IMG.base
122
+
123
+# success, all done
124
+echo "*** done"
125
+rm -f $seq.full
126
+status=0
127
diff --git a/tests/qemu-iotests/313.out b/tests/qemu-iotests/313.out
128
new file mode 100644
129
index XXXXXXX..XXXXXXX
130
--- /dev/null
131
+++ b/tests/qemu-iotests/313.out
132
@@ -XXX,XX +XXX,XX @@
133
+QA output created by 313
134
+
135
+### Create the image
136
+
137
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=SIZE
138
+
139
+### Write data to allocate more refcount blocks than the cache can hold
140
+
141
+wrote 8347648/8347648 bytes at offset 0
142
+7.961 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
143
+
144
+### Create an overlay
145
+
146
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
147
+
148
+### Fill the overlay with zeroes
149
+
150
+wrote 8347648/8347648 bytes at offset 0
151
+7.961 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
152
+
153
+### Commit changes to the base image
154
+
155
+Image committed.
156
+
157
+### Check the base image
158
+
159
+No errors were found on the image.
160
+Image end offset: 8396800
161
+*** done
162
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
163
index XXXXXXX..XXXXXXX 100644
164
--- a/tests/qemu-iotests/group
165
+++ b/tests/qemu-iotests/group
166
@@ -XXX,XX +XXX,XX @@
167
309 rw auto quick
168
310 rw quick
169
312 rw quick
170
+313 rw auto quick
171
--
57
--
172
2.29.2
58
2.21.0
173
59
174
60
diff view generated by jsdifflib
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
This patch completes the series with the COR-filter applied to
3
Commit b0651b8c246d ("vmdk: Move l1_size check into vmdk_add_extent")
4
block-stream operations.
4
extended the l1_size check from VMDK4 to VMDK3 but did not update the
5
default coverage in the moved comment.
5
6
6
Adding the filter makes it possible in future implement discarding
7
The previous vmdk4 calculation:
7
copied regions in backing files during the block-stream job, to reduce
8
the disk overuse (we need control on permissions).
9
8
10
Also, the filter now is smart enough to do copy-on-read with specified
9
(512 * 1024 * 1024) * 512(l2 entries) * 65536(grain) = 16PB
11
base, so we have benefit on guest reads even when doing block-stream of
12
the part of the backing chain.
13
10
14
Several iotests are slightly modified due to filter insertion.
11
The added vmdk3 calculation:
15
12
16
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
13
(512 * 1024 * 1024) * 4096(l2 entries) * 512(grain) = 1PB
17
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
18
Message-Id: <20201216061703.70908-14-vsementsov@virtuozzo.com>
15
Adding the calculation of vmdk3 to the comment.
16
17
In any case, VMware does not offer virtual disks more than 2TB for
18
vmdk4/vmdk3 or 64TB for the new undocumented seSparse format which is
19
not implemented yet in qemu.
20
21
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
22
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
23
Reviewed-by: Liran Alon <liran.alon@oracle.com>
24
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
25
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
26
Message-id: 20190620091057.47441-2-shmuel.eiderman@oracle.com
27
Reviewed-by: yuchenlin <yuchenlin@synology.com>
19
Reviewed-by: Max Reitz <mreitz@redhat.com>
28
Reviewed-by: Max Reitz <mreitz@redhat.com>
20
Signed-off-by: Max Reitz <mreitz@redhat.com>
29
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
---
30
---
22
block/stream.c | 105 ++++++++++++++++++++++---------------
31
block/vmdk.c | 11 ++++++++---
23
tests/qemu-iotests/030 | 8 +--
32
1 file changed, 8 insertions(+), 3 deletions(-)
24
tests/qemu-iotests/141.out | 2 +-
25
tests/qemu-iotests/245 | 20 ++++---
26
4 files changed, 80 insertions(+), 55 deletions(-)
27
33
28
diff --git a/block/stream.c b/block/stream.c
34
diff --git a/block/vmdk.c b/block/vmdk.c
29
index XXXXXXX..XXXXXXX 100644
35
index XXXXXXX..XXXXXXX 100644
30
--- a/block/stream.c
36
--- a/block/vmdk.c
31
+++ b/block/stream.c
37
+++ b/block/vmdk.c
32
@@ -XXX,XX +XXX,XX @@
38
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
33
#include "block/blockjob_int.h"
39
return -EFBIG;
34
#include "qapi/error.h"
35
#include "qapi/qmp/qerror.h"
36
+#include "qapi/qmp/qdict.h"
37
#include "qemu/ratelimit.h"
38
#include "sysemu/block-backend.h"
39
+#include "block/copy-on-read.h"
40
41
enum {
42
/*
43
@@ -XXX,XX +XXX,XX @@ typedef struct StreamBlockJob {
44
BlockJob common;
45
BlockDriverState *base_overlay; /* COW overlay (stream from this) */
46
BlockDriverState *above_base; /* Node directly above the base */
47
+ BlockDriverState *cor_filter_bs;
48
BlockDriverState *target_bs;
49
BlockdevOnError on_error;
50
char *backing_file_str;
51
bool bs_read_only;
52
- bool chain_frozen;
53
} StreamBlockJob;
54
55
static int coroutine_fn stream_populate(BlockBackend *blk,
56
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_populate(BlockBackend *blk,
57
{
58
assert(bytes < SIZE_MAX);
59
60
- return blk_co_preadv(blk, offset, bytes, NULL,
61
- BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH);
62
-}
63
-
64
-static void stream_abort(Job *job)
65
-{
66
- StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
67
-
68
- if (s->chain_frozen) {
69
- bdrv_unfreeze_backing_chain(s->target_bs, s->above_base);
70
- }
71
+ return blk_co_preadv(blk, offset, bytes, NULL, BDRV_REQ_PREFETCH);
72
}
73
74
static int stream_prepare(Job *job)
75
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
76
Error *local_err = NULL;
77
int ret = 0;
78
79
- bdrv_unfreeze_backing_chain(s->target_bs, s->above_base);
80
- s->chain_frozen = false;
81
+ /* We should drop filter at this point, as filter hold the backing chain */
82
+ bdrv_cor_filter_drop(s->cor_filter_bs);
83
+ s->cor_filter_bs = NULL;
84
85
if (bdrv_cow_child(unfiltered_bs)) {
86
const char *base_id = NULL, *base_fmt = NULL;
87
@@ -XXX,XX +XXX,XX @@ static void stream_clean(Job *job)
88
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
89
BlockJob *bjob = &s->common;
90
91
+ if (s->cor_filter_bs) {
92
+ bdrv_cor_filter_drop(s->cor_filter_bs);
93
+ s->cor_filter_bs = NULL;
94
+ }
95
+
96
/* Reopen the image back in read-only mode if necessary */
97
if (s->bs_read_only) {
98
/* Give up write permissions before making it read-only */
99
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
100
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
101
BlockBackend *blk = s->common.blk;
102
BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
103
- bool enable_cor = !bdrv_cow_child(s->base_overlay);
104
int64_t len;
105
int64_t offset = 0;
106
uint64_t delay_ns = 0;
107
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
108
}
40
}
109
job_progress_set_remaining(&s->common.job, len);
41
if (l1_size > 512 * 1024 * 1024) {
110
42
- /* Although with big capacity and small l1_entry_sectors, we can get a
111
- /* Turn on copy-on-read for the whole block device so that guest read
43
+ /*
112
- * requests help us make progress. Only do this when copying the entire
44
+ * Although with big capacity and small l1_entry_sectors, we can get a
113
- * backing chain since the copy-on-read operation does not take base into
45
* big l1_size, we don't want unbounded value to allocate the table.
114
- * account.
46
- * Limit it to 512M, which is 16PB for default cluster and L2 table
115
- */
47
- * size */
116
- if (enable_cor) {
48
+ * Limit it to 512M, which is:
117
- bdrv_enable_copy_on_read(s->target_bs);
49
+ * 16PB - for default "Hosted Sparse Extent" (VMDK4)
118
- }
50
+ * cluster size: 64KB, L2 table size: 512 entries
119
-
51
+ * 1PB - for default "ESXi Host Sparse Extent" (VMDK3/vmfsSparse)
120
for ( ; offset < len; offset += n) {
52
+ * cluster size: 512B, L2 table size: 4096 entries
121
bool copy;
53
+ */
122
int ret;
54
error_setg(errp, "L1 size too big");
123
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
55
return -EFBIG;
124
}
125
}
56
}
126
127
- if (enable_cor) {
128
- bdrv_disable_copy_on_read(s->target_bs);
129
- }
130
-
131
/* Do not remove the backing file if an error was there but ignored. */
132
return error;
133
}
134
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
135
.free = block_job_free,
136
.run = stream_run,
137
.prepare = stream_prepare,
138
- .abort = stream_abort,
139
.clean = stream_clean,
140
.user_resume = block_job_user_resume,
141
},
142
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
143
bool bs_read_only;
144
int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
145
BlockDriverState *base_overlay;
146
+ BlockDriverState *cor_filter_bs = NULL;
147
BlockDriverState *above_base;
148
+ QDict *opts;
149
150
assert(!(base && bottom));
151
assert(!(backing_file_str && bottom));
152
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
153
}
154
}
155
156
- if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) {
157
- return;
158
- }
159
-
160
/* Make sure that the image is opened in read-write mode */
161
bs_read_only = bdrv_is_read_only(bs);
162
if (bs_read_only) {
163
- if (bdrv_reopen_set_read_only(bs, false, errp) != 0) {
164
- bs_read_only = false;
165
- goto fail;
166
+ int ret;
167
+ /* Hold the chain during reopen */
168
+ if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) {
169
+ return;
170
+ }
171
+
172
+ ret = bdrv_reopen_set_read_only(bs, false, errp);
173
+
174
+ /* failure, or cor-filter will hold the chain */
175
+ bdrv_unfreeze_backing_chain(bs, above_base);
176
+
177
+ if (ret < 0) {
178
+ return;
179
}
180
}
181
182
- /* Prevent concurrent jobs trying to modify the graph structure here, we
183
- * already have our own plans. Also don't allow resize as the image size is
184
- * queried only at the job start and then cached. */
185
- s = block_job_create(job_id, &stream_job_driver, NULL, bs,
186
- basic_flags | BLK_PERM_GRAPH_MOD,
187
+ opts = qdict_new();
188
+
189
+ qdict_put_str(opts, "driver", "copy-on-read");
190
+ qdict_put_str(opts, "file", bdrv_get_node_name(bs));
191
+ /* Pass the base_overlay node name as 'bottom' to COR driver */
192
+ qdict_put_str(opts, "bottom", base_overlay->node_name);
193
+ if (filter_node_name) {
194
+ qdict_put_str(opts, "node-name", filter_node_name);
195
+ }
196
+
197
+ cor_filter_bs = bdrv_insert_node(bs, opts, BDRV_O_RDWR, errp);
198
+ if (!cor_filter_bs) {
199
+ goto fail;
200
+ }
201
+
202
+ if (!filter_node_name) {
203
+ cor_filter_bs->implicit = true;
204
+ }
205
+
206
+ s = block_job_create(job_id, &stream_job_driver, NULL, cor_filter_bs,
207
+ BLK_PERM_CONSISTENT_READ,
208
basic_flags | BLK_PERM_WRITE,
209
speed, creation_flags, NULL, NULL, errp);
210
if (!s) {
211
goto fail;
212
}
213
214
+ /*
215
+ * Prevent concurrent jobs trying to modify the graph structure here, we
216
+ * already have our own plans. Also don't allow resize as the image size is
217
+ * queried only at the job start and then cached.
218
+ */
219
+ if (block_job_add_bdrv(&s->common, "active node", bs, 0,
220
+ basic_flags | BLK_PERM_WRITE, &error_abort)) {
221
+ goto fail;
222
+ }
223
+
224
/* Block all intermediate nodes between bs and base, because they will
225
* disappear from the chain after this operation. The streaming job reads
226
* every block only once, assuming that it doesn't change, so forbid writes
227
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
228
s->base_overlay = base_overlay;
229
s->above_base = above_base;
230
s->backing_file_str = g_strdup(backing_file_str);
231
+ s->cor_filter_bs = cor_filter_bs;
232
s->target_bs = bs;
233
s->bs_read_only = bs_read_only;
234
- s->chain_frozen = true;
235
236
s->on_error = on_error;
237
trace_stream_start(bs, base, s);
238
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
239
return;
240
241
fail:
242
+ if (cor_filter_bs) {
243
+ bdrv_cor_filter_drop(cor_filter_bs);
244
+ }
245
if (bs_read_only) {
246
bdrv_reopen_set_read_only(bs, true, NULL);
247
}
248
- bdrv_unfreeze_backing_chain(bs, above_base);
249
}
250
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
251
index XXXXXXX..XXXXXXX 100755
252
--- a/tests/qemu-iotests/030
253
+++ b/tests/qemu-iotests/030
254
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
255
self.assert_no_active_block_jobs()
256
257
# Set a speed limit to make sure that this job blocks the rest
258
- result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4', base=self.imgs[1], speed=1024*1024)
259
+ result = self.vm.qmp('block-stream', device='node4',
260
+ job_id='stream-node4', base=self.imgs[1],
261
+ filter_node_name='stream-filter', speed=1024*1024)
262
self.assert_qmp(result, 'return', {})
263
264
result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2])
265
self.assert_qmp(result, 'error/desc',
266
- "Node 'node4' is busy: block device is in use by block job: stream")
267
+ "Node 'stream-filter' is busy: block device is in use by block job: stream")
268
269
result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3', base=self.imgs[2])
270
self.assert_qmp(result, 'error/desc',
271
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
272
# block-commit should also fail if it touches nodes used by the stream job
273
result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[4], job_id='commit-node4')
274
self.assert_qmp(result, 'error/desc',
275
- "Node 'node4' is busy: block device is in use by block job: stream")
276
+ "Node 'stream-filter' is busy: block device is in use by block job: stream")
277
278
result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[1], top=self.imgs[3], job_id='commit-node1')
279
self.assert_qmp(result, 'error/desc',
280
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
281
index XXXXXXX..XXXXXXX 100644
282
--- a/tests/qemu-iotests/141.out
283
+++ b/tests/qemu-iotests/141.out
284
@@ -XXX,XX +XXX,XX @@ wrote 1048576/1048576 bytes at offset 0
285
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
286
{'execute': 'blockdev-del',
287
'arguments': {'node-name': 'drv0'}}
288
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
289
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}}
290
{'execute': 'block-job-cancel',
291
'arguments': {'device': 'job0'}}
292
{"return": {}}
293
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
294
index XXXXXXX..XXXXXXX 100755
295
--- a/tests/qemu-iotests/245
296
+++ b/tests/qemu-iotests/245
297
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
298
299
# hd1 <- hd0
300
result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
301
- device = 'hd1', auto_finalize = False)
302
+ device = 'hd1', filter_node_name='cor',
303
+ auto_finalize = False)
304
self.assert_qmp(result, 'return', {})
305
306
- # We can't reopen with the original options because that would
307
- # make hd1 read-only and block-stream requires it to be read-write
308
- # (Which error message appears depends on whether the stream job is
309
- # already done with copying at this point.)
310
+ # We can't reopen with the original options because there is a filter
311
+ # inserted by stream job above hd1.
312
self.reopen(opts, {},
313
- ["Can't set node 'hd1' to r/o with copy-on-read enabled",
314
- "Cannot make block node read-only, there is a writer on it"])
315
+ "Cannot change the option 'backing.backing.file.node-name'")
316
+
317
+ # We can't reopen hd1 to read-only, as block-stream requires it to be
318
+ # read-write
319
+ self.reopen(opts['backing'], {'read-only': True},
320
+ "Cannot make block node read-only, there is a writer on it")
321
322
# We can't remove hd2 while the stream job is ongoing
323
opts['backing']['backing'] = None
324
- self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
325
+ self.reopen(opts['backing'], {'read-only': False},
326
+ "Cannot change 'backing' link from 'hd1' to 'hd2'")
327
328
# We can detach hd1 from hd0 because it doesn't affect the stream job
329
opts['backing'] = None
330
--
57
--
331
2.29.2
58
2.21.0
332
59
333
60
diff view generated by jsdifflib
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
Stream in stream_prepare calls bdrv_change_backing_file() to change
3
512M of L1 entries is a very loose bound, only 32M are required to store
4
backing-file in the metadata of bs.
4
the maximal supported VMDK file size of 2TB.
5
5
6
It may use either backing-file parameter given by user or just take
6
Fixed qemu-iotest 59# - now failure occures before on impossible L1
7
filename of base on job start.
7
table size.
8
8
9
Backing file format is determined by base on job finish.
9
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
10
10
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
11
There are some problems with this design, we solve only two by this
11
Reviewed-by: Liran Alon <liran.alon@oracle.com>
12
patch:
12
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
13
13
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
14
1. Consider scenario with backing-file unset. Current concept of stream
14
Message-id: 20190620091057.47441-3-shmuel.eiderman@oracle.com
15
supports changing of the base during the job (we don't freeze link to
16
the base). So, we should not save base filename at job start,
17
18
- let's determine name of the base on job finish.
19
20
2. Using direct base to determine filename and format is not very good:
21
base node may be a filter, so its filename may be JSON, and format_name
22
is not good for storing into qcow2 metadata as backing file format.
23
24
- let's use unfiltered_base
25
26
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
27
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
28
[vsementsov: change commit subject, change logic in stream_prepare]
29
Message-Id: <20201216061703.70908-10-vsementsov@virtuozzo.com>
30
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
31
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
32
---
17
---
33
block/stream.c | 9 +++++----
18
block/vmdk.c | 13 +++++++------
34
blockdev.c | 8 +-------
19
tests/qemu-iotests/059.out | 2 +-
35
2 files changed, 6 insertions(+), 11 deletions(-)
20
2 files changed, 8 insertions(+), 7 deletions(-)
36
21
37
diff --git a/block/stream.c b/block/stream.c
22
diff --git a/block/vmdk.c b/block/vmdk.c
38
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
39
--- a/block/stream.c
24
--- a/block/vmdk.c
40
+++ b/block/stream.c
25
+++ b/block/vmdk.c
41
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
26
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
42
BlockDriverState *bs = blk_bs(bjob->blk);
27
error_setg(errp, "Invalid granularity, image may be corrupt");
43
BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
28
return -EFBIG;
44
BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base);
29
}
45
+ BlockDriverState *unfiltered_base = bdrv_skip_filters(base);
30
- if (l1_size > 512 * 1024 * 1024) {
46
Error *local_err = NULL;
31
+ if (l1_size > 32 * 1024 * 1024) {
47
int ret = 0;
32
/*
48
33
* Although with big capacity and small l1_entry_sectors, we can get a
49
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
34
* big l1_size, we don't want unbounded value to allocate the table.
50
35
- * Limit it to 512M, which is:
51
if (bdrv_cow_child(unfiltered_bs)) {
36
- * 16PB - for default "Hosted Sparse Extent" (VMDK4)
52
const char *base_id = NULL, *base_fmt = NULL;
37
- * cluster size: 64KB, L2 table size: 512 entries
53
- if (base) {
38
- * 1PB - for default "ESXi Host Sparse Extent" (VMDK3/vmfsSparse)
54
- base_id = s->backing_file_str;
39
- * cluster size: 512B, L2 table size: 4096 entries
55
- if (base->drv) {
40
+ * Limit it to 32M, which is enough to store:
56
- base_fmt = base->drv->format_name;
41
+ * 8TB - for both VMDK3 & VMDK4 with
57
+ if (unfiltered_base) {
42
+ * minimal cluster size: 512B
58
+ base_id = s->backing_file_str ?: unfiltered_base->filename;
43
+ * minimal L2 table size: 512 entries
59
+ if (unfiltered_base->drv) {
44
+ * 8 TB is still more than the maximal value supported for
60
+ base_fmt = unfiltered_base->drv->format_name;
45
+ * VMDK3 & VMDK4 which is 2TB.
61
}
46
*/
62
}
47
error_setg(errp, "L1 size too big");
63
bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
48
return -EFBIG;
64
diff --git a/blockdev.c b/blockdev.c
49
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
65
index XXXXXXX..XXXXXXX 100644
50
index XXXXXXX..XXXXXXX 100644
66
--- a/blockdev.c
51
--- a/tests/qemu-iotests/059.out
67
+++ b/blockdev.c
52
+++ b/tests/qemu-iotests/059.out
68
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
53
@@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File
69
BlockDriverState *base_bs = NULL;
54
0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk
70
AioContext *aio_context;
55
71
Error *local_err = NULL;
56
=== Testing afl image with a very large capacity ===
72
- const char *base_name = NULL;
57
-qemu-img: Can't get image size 'TEST_DIR/afl9.IMGFMT': File too large
73
int job_flags = JOB_DEFAULT;
58
+qemu-img: Could not open 'TEST_DIR/afl9.IMGFMT': L1 size too big
74
59
*** done
75
if (!has_on_error) {
76
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
77
goto out;
78
}
79
assert(bdrv_get_aio_context(base_bs) == aio_context);
80
- base_name = base;
81
}
82
83
if (has_base_node) {
84
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
85
}
86
assert(bdrv_get_aio_context(base_bs) == aio_context);
87
bdrv_refresh_filename(base_bs);
88
- base_name = base_bs->filename;
89
}
90
91
/* Check for op blockers in the whole chain between bs and base */
92
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
93
goto out;
94
}
95
96
- /* backing_file string overrides base bs filename */
97
- base_name = has_backing_file ? backing_file : base_name;
98
-
99
if (has_auto_finalize && !auto_finalize) {
100
job_flags |= JOB_MANUAL_FINALIZE;
101
}
102
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
103
job_flags |= JOB_MANUAL_DISMISS;
104
}
105
106
- stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
107
+ stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
108
job_flags, has_speed ? speed : 0, on_error,
109
filter_node_name, &local_err);
110
if (local_err) {
111
--
60
--
112
2.29.2
61
2.21.0
113
62
114
63
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
test_stream_parallel run parallel stream jobs, intersecting so that top
4
of one is base of another. It's OK now, but it would be a problem if
5
insert the filter, as one job will want to use another job's filter as
6
above_base node.
7
8
Correct thing to do is move to new interface: "bottom" argument instead
9
of base. This guarantees that jobs don't intersect by their actions.
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20201216061703.70908-12-vsementsov@virtuozzo.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
tests/qemu-iotests/030 | 4 +++-
17
1 file changed, 3 insertions(+), 1 deletion(-)
18
19
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
20
index XXXXXXX..XXXXXXX 100755
21
--- a/tests/qemu-iotests/030
22
+++ b/tests/qemu-iotests/030
23
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
24
node_name = 'node%d' % i
25
job_id = 'stream-%s' % node_name
26
pending_jobs.append(job_id)
27
- result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=1024)
28
+ result = self.vm.qmp('block-stream', device=node_name,
29
+ job_id=job_id, bottom=f'node{i-1}',
30
+ speed=1024)
31
self.assert_qmp(result, 'return', {})
32
33
for job in pending_jobs:
34
--
35
2.29.2
36
37
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Add a direct link to target bs for convenience and to simplify
4
following commit which will insert COR filter above target bs.
5
6
This is a part of original commit written by Andrey.
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Message-Id: <20201216061703.70908-13-vsementsov@virtuozzo.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
block/stream.c | 23 ++++++++++-------------
14
1 file changed, 10 insertions(+), 13 deletions(-)
15
16
diff --git a/block/stream.c b/block/stream.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/stream.c
19
+++ b/block/stream.c
20
@@ -XXX,XX +XXX,XX @@ typedef struct StreamBlockJob {
21
BlockJob common;
22
BlockDriverState *base_overlay; /* COW overlay (stream from this) */
23
BlockDriverState *above_base; /* Node directly above the base */
24
+ BlockDriverState *target_bs;
25
BlockdevOnError on_error;
26
char *backing_file_str;
27
bool bs_read_only;
28
@@ -XXX,XX +XXX,XX @@ static void stream_abort(Job *job)
29
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
30
31
if (s->chain_frozen) {
32
- BlockJob *bjob = &s->common;
33
- bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->above_base);
34
+ bdrv_unfreeze_backing_chain(s->target_bs, s->above_base);
35
}
36
}
37
38
static int stream_prepare(Job *job)
39
{
40
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
41
- BlockJob *bjob = &s->common;
42
- BlockDriverState *bs = blk_bs(bjob->blk);
43
- BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
44
+ BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
45
BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base);
46
BlockDriverState *unfiltered_base = bdrv_skip_filters(base);
47
Error *local_err = NULL;
48
int ret = 0;
49
50
- bdrv_unfreeze_backing_chain(bs, s->above_base);
51
+ bdrv_unfreeze_backing_chain(s->target_bs, s->above_base);
52
s->chain_frozen = false;
53
54
if (bdrv_cow_child(unfiltered_bs)) {
55
@@ -XXX,XX +XXX,XX @@ static void stream_clean(Job *job)
56
{
57
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
58
BlockJob *bjob = &s->common;
59
- BlockDriverState *bs = blk_bs(bjob->blk);
60
61
/* Reopen the image back in read-only mode if necessary */
62
if (s->bs_read_only) {
63
/* Give up write permissions before making it read-only */
64
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
65
- bdrv_reopen_set_read_only(bs, true, NULL);
66
+ bdrv_reopen_set_read_only(s->target_bs, true, NULL);
67
}
68
69
g_free(s->backing_file_str);
70
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
71
{
72
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
73
BlockBackend *blk = s->common.blk;
74
- BlockDriverState *bs = blk_bs(blk);
75
- BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
76
+ BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
77
bool enable_cor = !bdrv_cow_child(s->base_overlay);
78
int64_t len;
79
int64_t offset = 0;
80
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
81
return 0;
82
}
83
84
- len = bdrv_getlength(bs);
85
+ len = bdrv_getlength(s->target_bs);
86
if (len < 0) {
87
return len;
88
}
89
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
90
* account.
91
*/
92
if (enable_cor) {
93
- bdrv_enable_copy_on_read(bs);
94
+ bdrv_enable_copy_on_read(s->target_bs);
95
}
96
97
for ( ; offset < len; offset += n) {
98
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
99
}
100
101
if (enable_cor) {
102
- bdrv_disable_copy_on_read(bs);
103
+ bdrv_disable_copy_on_read(s->target_bs);
104
}
105
106
/* Do not remove the backing file if an error was there but ignored. */
107
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
108
s->base_overlay = base_overlay;
109
s->above_base = above_base;
110
s->backing_file_str = g_strdup(backing_file_str);
111
+ s->target_bs = bs;
112
s->bs_read_only = bs_read_only;
113
s->chain_frozen = true;
114
115
--
116
2.29.2
117
118
diff view generated by jsdifflib
Deleted patch
1
There are a couple of environment variables that we fetch with
2
os.environ.get() without supplying a default. Clearly they are required
3
and expected to be set by the ./check script (as evidenced by
4
execute_setup_common(), which checks for test_dir and
5
qemu_default_machine to be set, and aborts if they are not).
6
1
7
Using .get() this way has the disadvantage of returning an Optional[str]
8
type, which mypy will complain about when tests just assume these values
9
to be str.
10
11
Use [] instead, which raises a KeyError for environment variables that
12
are not set. When this exception is raised, catch it and move the abort
13
code from execute_setup_common() there.
14
15
Drop the 'assert iotests.sock_dir is not None' from iotest 300, because
16
that sort of thing is precisely what this patch wants to prevent.
17
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
20
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
21
Message-Id: <20210118105720.14824-2-mreitz@redhat.com>
22
---
23
tests/qemu-iotests/300 | 1 -
24
tests/qemu-iotests/iotests.py | 26 +++++++++++++-------------
25
2 files changed, 13 insertions(+), 14 deletions(-)
26
27
diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
28
index XXXXXXX..XXXXXXX 100755
29
--- a/tests/qemu-iotests/300
30
+++ b/tests/qemu-iotests/300
31
@@ -XXX,XX +XXX,XX @@ import qemu
32
33
BlockBitmapMapping = List[Dict[str, Union[str, List[Dict[str, str]]]]]
34
35
-assert iotests.sock_dir is not None
36
mig_sock = os.path.join(iotests.sock_dir, 'mig_sock')
37
38
39
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
40
index XXXXXXX..XXXXXXX 100644
41
--- a/tests/qemu-iotests/iotests.py
42
+++ b/tests/qemu-iotests/iotests.py
43
@@ -XXX,XX +XXX,XX @@ qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
44
45
imgfmt = os.environ.get('IMGFMT', 'raw')
46
imgproto = os.environ.get('IMGPROTO', 'file')
47
-test_dir = os.environ.get('TEST_DIR')
48
-sock_dir = os.environ.get('SOCK_DIR')
49
output_dir = os.environ.get('OUTPUT_DIR', '.')
50
-cachemode = os.environ.get('CACHEMODE')
51
-aiomode = os.environ.get('AIOMODE')
52
-qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE')
53
+
54
+try:
55
+ test_dir = os.environ['TEST_DIR']
56
+ sock_dir = os.environ['SOCK_DIR']
57
+ cachemode = os.environ['CACHEMODE']
58
+ aiomode = os.environ['AIOMODE']
59
+ qemu_default_machine = os.environ['QEMU_DEFAULT_MACHINE']
60
+except KeyError:
61
+ # We are using these variables as proxies to indicate that we're
62
+ # not being run via "check". There may be other things set up by
63
+ # "check" that individual test cases rely on.
64
+ sys.stderr.write('Please run this test via the "check" script\n')
65
+ sys.exit(os.EX_USAGE)
66
67
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
68
69
@@ -XXX,XX +XXX,XX @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
70
"""
71
# Note: Python 3.6 and pylint do not like 'Collection' so use 'Sequence'.
72
73
- # We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
74
- # indicate that we're not being run via "check". There may be
75
- # other things set up by "check" that individual test cases rely
76
- # on.
77
- if test_dir is None or qemu_default_machine is None:
78
- sys.stderr.write('Please run this test via the "check" script\n')
79
- sys.exit(os.EX_USAGE)
80
-
81
debug = '-d' in sys.argv
82
if debug:
83
sys.argv.remove('-d')
84
--
85
2.29.2
86
87
diff view generated by jsdifflib
Deleted patch
1
Instead of checking iotests.py only, check all Python files in the
2
qemu-iotests/ directory. Of course, most of them do not pass, so there
3
is an extensive skip list for now. (The only files that do pass are
4
209, 254, 283, and iotests.py.)
5
1
6
(Alternatively, we could have the opposite, i.e. an explicit list of
7
files that we do want to check, but I think it is better to check files
8
by default.)
9
10
Unless started in debug mode (./check -d), the output has no information
11
on which files are tested, so we will not have a problem e.g. with
12
backports, where some files may be missing when compared to upstream.
13
14
Besides the technical rewrite, some more things are changed:
15
16
- For the pylint invocation, PYTHONPATH is adjusted. This mirrors
17
setting MYPYPATH for mypy.
18
19
- Also, MYPYPATH is now derived from PYTHONPATH, so that we include
20
paths set by the environment. Maybe at some point we want to let the
21
check script add '../../python/' to PYTHONPATH so that iotests.py does
22
not need to do that.
23
24
- Passing --notes=FIXME,XXX to pylint suppresses warnings for TODO
25
comments. TODO is fine, we do not need 297 to complain about such
26
comments.
27
28
- The "Success" line from mypy's output is suppressed, because (A) it
29
does not add useful information, and (B) it would leak information
30
about the files having been tested to the reference output, which we
31
decidedly do not want.
32
33
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
34
Signed-off-by: Max Reitz <mreitz@redhat.com>
35
Message-Id: <20210118105720.14824-3-mreitz@redhat.com>
36
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
37
---
38
tests/qemu-iotests/297 | 112 +++++++++++++++++++++++++++++--------
39
tests/qemu-iotests/297.out | 5 +-
40
2 files changed, 92 insertions(+), 25 deletions(-)
41
42
diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297
43
index XXXXXXX..XXXXXXX 100755
44
--- a/tests/qemu-iotests/297
45
+++ b/tests/qemu-iotests/297
46
@@ -XXX,XX +XXX,XX @@
47
-#!/usr/bin/env bash
48
+#!/usr/bin/env python3
49
# group: meta
50
#
51
# Copyright (C) 2020 Red Hat, Inc.
52
@@ -XXX,XX +XXX,XX @@
53
# You should have received a copy of the GNU General Public License
54
# along with this program. If not, see <http://www.gnu.org/licenses/>.
55
56
-seq=$(basename $0)
57
-echo "QA output created by $seq"
58
+import os
59
+import re
60
+import shutil
61
+import subprocess
62
+import sys
63
64
-status=1    # failure is the default!
65
+import iotests
66
67
-# get standard environment
68
-. ./common.rc
69
70
-if ! type -p "pylint-3" > /dev/null; then
71
- _notrun "pylint-3 not found"
72
-fi
73
-if ! type -p "mypy" > /dev/null; then
74
- _notrun "mypy not found"
75
-fi
76
+# TODO: Empty this list!
77
+SKIP_FILES = (
78
+ '030', '040', '041', '044', '045', '055', '056', '057', '065', '093',
79
+ '096', '118', '124', '129', '132', '136', '139', '147', '148', '149',
80
+ '151', '152', '155', '163', '165', '169', '194', '196', '199', '202',
81
+ '203', '205', '206', '207', '208', '210', '211', '212', '213', '216',
82
+ '218', '219', '222', '224', '228', '234', '235', '236', '237', '238',
83
+ '240', '242', '245', '246', '248', '255', '256', '257', '258', '260',
84
+ '262', '264', '266', '274', '277', '280', '281', '295', '296', '298',
85
+ '299', '300', '302', '303', '304', '307',
86
+ 'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py'
87
+)
88
89
-pylint-3 --score=n iotests.py
90
91
-MYPYPATH=../../python/ mypy --warn-unused-configs --disallow-subclassing-any \
92
- --disallow-any-generics --disallow-incomplete-defs \
93
- --disallow-untyped-decorators --no-implicit-optional \
94
- --warn-redundant-casts --warn-unused-ignores \
95
- --no-implicit-reexport iotests.py
96
+def is_python_file(filename):
97
+ if not os.path.isfile(filename):
98
+ return False
99
100
-# success, all done
101
-echo "*** done"
102
-rm -f $seq.full
103
-status=0
104
+ if filename.endswith('.py'):
105
+ return True
106
+
107
+ with open(filename) as f:
108
+ try:
109
+ first_line = f.readline()
110
+ return re.match('^#!.*python', first_line) is not None
111
+ except UnicodeDecodeError: # Ignore binary files
112
+ return False
113
+
114
+
115
+def run_linters():
116
+ files = [filename for filename in (set(os.listdir('.')) - set(SKIP_FILES))
117
+ if is_python_file(filename)]
118
+
119
+ iotests.logger.debug('Files to be checked:')
120
+ iotests.logger.debug(', '.join(sorted(files)))
121
+
122
+ print('=== pylint ===')
123
+ sys.stdout.flush()
124
+
125
+ # Todo notes are fine, but fixme's or xxx's should probably just be
126
+ # fixed (in tests, at least)
127
+ env = os.environ.copy()
128
+ qemu_module_path = os.path.join(os.path.dirname(__file__),
129
+ '..', '..', 'python')
130
+ try:
131
+ env['PYTHONPATH'] += os.pathsep + qemu_module_path
132
+ except KeyError:
133
+ env['PYTHONPATH'] = qemu_module_path
134
+ subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files),
135
+ env=env, check=False)
136
+
137
+ print('=== mypy ===')
138
+ sys.stdout.flush()
139
+
140
+ # We have to call mypy separately for each file. Otherwise, it
141
+ # will interpret all given files as belonging together (i.e., they
142
+ # may not both define the same classes, etc.; most notably, they
143
+ # must not both define the __main__ module).
144
+ env['MYPYPATH'] = env['PYTHONPATH']
145
+ for filename in files:
146
+ p = subprocess.run(('mypy',
147
+ '--warn-unused-configs',
148
+ '--disallow-subclassing-any',
149
+ '--disallow-any-generics',
150
+ '--disallow-incomplete-defs',
151
+ '--disallow-untyped-decorators',
152
+ '--no-implicit-optional',
153
+ '--warn-redundant-casts',
154
+ '--warn-unused-ignores',
155
+ '--no-implicit-reexport',
156
+ filename),
157
+ env=env,
158
+ check=False,
159
+ stdout=subprocess.PIPE,
160
+ stderr=subprocess.STDOUT,
161
+ universal_newlines=True)
162
+
163
+ if p.returncode != 0:
164
+ print(p.stdout)
165
+
166
+
167
+for linter in ('pylint-3', 'mypy'):
168
+ if shutil.which(linter) is None:
169
+ iotests.notrun(f'{linter} not found')
170
+
171
+iotests.script_main(run_linters)
172
diff --git a/tests/qemu-iotests/297.out b/tests/qemu-iotests/297.out
173
index XXXXXXX..XXXXXXX 100644
174
--- a/tests/qemu-iotests/297.out
175
+++ b/tests/qemu-iotests/297.out
176
@@ -XXX,XX +XXX,XX @@
177
-QA output created by 297
178
-Success: no issues found in 1 source file
179
-*** done
180
+=== pylint ===
181
+=== mypy ===
182
--
183
2.29.2
184
185
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Max Reitz <mreitz@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
5
Message-Id: <20210118105720.14824-4-mreitz@redhat.com>
6
---
7
tests/qemu-iotests/124 | 8 +-------
8
tests/qemu-iotests/iotests.py | 11 +++++++----
9
2 files changed, 8 insertions(+), 11 deletions(-)
10
1
11
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/124
14
+++ b/tests/qemu-iotests/124
15
@@ -XXX,XX +XXX,XX @@
16
17
import os
18
import iotests
19
+from iotests import try_remove
20
21
22
def io_write_patterns(img, patterns):
23
@@ -XXX,XX +XXX,XX @@ def io_write_patterns(img, patterns):
24
iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
25
26
27
-def try_remove(img):
28
- try:
29
- os.remove(img)
30
- except OSError:
31
- pass
32
-
33
-
34
def transaction_action(action, **kwargs):
35
return {
36
'type': action,
37
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
38
index XXXXXXX..XXXXXXX 100644
39
--- a/tests/qemu-iotests/iotests.py
40
+++ b/tests/qemu-iotests/iotests.py
41
@@ -XXX,XX +XXX,XX @@ class FilePath:
42
return False
43
44
45
+def try_remove(img):
46
+ try:
47
+ os.remove(img)
48
+ except OSError:
49
+ pass
50
+
51
def file_path_remover():
52
for path in reversed(file_path_remover.paths):
53
- try:
54
- os.remove(path)
55
- except OSError:
56
- pass
57
+ try_remove(path)
58
59
60
def file_path(*names, base_dir=test_dir):
61
--
62
2.29.2
63
64
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Max Reitz <mreitz@redhat.com>
2
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
3
Reviewed-by: Eric Blake <eblake@redhat.com>
4
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
5
Message-Id: <20210118105720.14824-5-mreitz@redhat.com>
6
---
7
tests/qemu-iotests/129 | 2 ++
8
1 file changed, 2 insertions(+)
9
1
10
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
11
index XXXXXXX..XXXXXXX 100755
12
--- a/tests/qemu-iotests/129
13
+++ b/tests/qemu-iotests/129
14
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
15
result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
16
**params)
17
self.vm.shutdown()
18
+ for img in (self.test_img, self.target_img, self.base_img):
19
+ iotests.try_remove(img)
20
21
def do_test_stop(self, cmd, **args):
22
"""Test 'stop' while block job is running on a throttled drive.
23
--
24
2.29.2
25
26
diff view generated by jsdifflib
Deleted patch
1
@busy is false when the job is paused, which happens all the time
2
because that is how jobs yield (e.g. for mirror at least since commit
3
565ac01f8d3).
4
1
5
Back when 129 was added (2015), perhaps there was no better way of
6
checking whether the job was still actually running. Now we have the
7
@status field (as of 58b295ba52c, i.e. 2018), which can give us exactly
8
that information.
9
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
14
Message-Id: <20210118105720.14824-6-mreitz@redhat.com>
15
---
16
tests/qemu-iotests/129 | 2 +-
17
1 file changed, 1 insertion(+), 1 deletion(-)
18
19
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
20
index XXXXXXX..XXXXXXX 100755
21
--- a/tests/qemu-iotests/129
22
+++ b/tests/qemu-iotests/129
23
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
24
result = self.vm.qmp("stop")
25
self.assert_qmp(result, 'return', {})
26
result = self.vm.qmp("query-block-jobs")
27
- self.assert_qmp(result, 'return[0]/busy', True)
28
+ self.assert_qmp(result, 'return[0]/status', 'running')
29
self.assert_qmp(result, 'return[0]/ready', False)
30
31
def test_drive_mirror(self):
32
--
33
2.29.2
34
35
diff view generated by jsdifflib
Deleted patch
1
Throttling on the BB has not affected block jobs in a while, so it is
2
possible that one of the jobs in 129 finishes before the VM is stopped.
3
We can fix that by running the job from a throttle node.
4
1
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
9
Message-Id: <20210118105720.14824-7-mreitz@redhat.com>
10
---
11
tests/qemu-iotests/129 | 37 +++++++++++++------------------------
12
1 file changed, 13 insertions(+), 24 deletions(-)
13
14
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
15
index XXXXXXX..XXXXXXX 100755
16
--- a/tests/qemu-iotests/129
17
+++ b/tests/qemu-iotests/129
18
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
19
iotests.qemu_img('create', '-f', iotests.imgfmt, self.test_img,
20
"-b", self.base_img, '-F', iotests.imgfmt)
21
iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M', self.test_img)
22
- self.vm = iotests.VM().add_drive(self.test_img)
23
+ self.vm = iotests.VM()
24
+ self.vm.add_object('throttle-group,id=tg0,x-bps-total=1024')
25
+
26
+ source_drive = 'driver=throttle,' \
27
+ 'throttle-group=tg0,' \
28
+ f'file.driver={iotests.imgfmt},' \
29
+ f'file.file.filename={self.test_img}'
30
+
31
+ self.vm.add_drive(None, source_drive)
32
self.vm.launch()
33
34
def tearDown(self):
35
- params = {"device": "drive0",
36
- "bps": 0,
37
- "bps_rd": 0,
38
- "bps_wr": 0,
39
- "iops": 0,
40
- "iops_rd": 0,
41
- "iops_wr": 0,
42
- }
43
- result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
44
- **params)
45
self.vm.shutdown()
46
for img in (self.test_img, self.target_img, self.base_img):
47
iotests.try_remove(img)
48
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
49
def do_test_stop(self, cmd, **args):
50
"""Test 'stop' while block job is running on a throttled drive.
51
The 'stop' command shouldn't drain the job"""
52
- params = {"device": "drive0",
53
- "bps": 1024,
54
- "bps_rd": 0,
55
- "bps_wr": 0,
56
- "iops": 0,
57
- "iops_rd": 0,
58
- "iops_wr": 0,
59
- }
60
- result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
61
- **params)
62
- self.assert_qmp(result, 'return', {})
63
result = self.vm.qmp(cmd, **args)
64
self.assert_qmp(result, 'return', {})
65
+
66
result = self.vm.qmp("stop")
67
self.assert_qmp(result, 'return', {})
68
result = self.vm.qmp("query-block-jobs")
69
+
70
self.assert_qmp(result, 'return[0]/status', 'running')
71
self.assert_qmp(result, 'return[0]/ready', False)
72
73
def test_drive_mirror(self):
74
self.do_test_stop("drive-mirror", device="drive0",
75
- target=self.target_img,
76
+ target=self.target_img, format=iotests.imgfmt,
77
sync="full")
78
79
def test_drive_backup(self):
80
self.do_test_stop("drive-backup", device="drive0",
81
- target=self.target_img,
82
+ target=self.target_img, format=iotests.imgfmt,
83
sync="full")
84
85
def test_block_commit(self):
86
--
87
2.29.2
88
89
diff view generated by jsdifflib
Deleted patch
1
Before this patch, test_block_commit() performs an active commit, which
2
under the hood is a mirror job. If we want to test various different
3
block jobs, we should perhaps run an actual commit job instead.
4
1
5
Doing so requires adding an overlay above the source node before the
6
commit is done (and then specifying the source node as the top node for
7
the commit job).
8
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
13
Message-Id: <20210118105720.14824-8-mreitz@redhat.com>
14
---
15
tests/qemu-iotests/129 | 27 +++++++++++++++++++++++++--
16
1 file changed, 25 insertions(+), 2 deletions(-)
17
18
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/129
21
+++ b/tests/qemu-iotests/129
22
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
23
test_img = os.path.join(iotests.test_dir, 'test.img')
24
target_img = os.path.join(iotests.test_dir, 'target.img')
25
base_img = os.path.join(iotests.test_dir, 'base.img')
26
+ overlay_img = os.path.join(iotests.test_dir, 'overlay.img')
27
28
def setUp(self):
29
iotests.qemu_img('create', '-f', iotests.imgfmt, self.base_img, "1G")
30
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
31
self.vm.add_object('throttle-group,id=tg0,x-bps-total=1024')
32
33
source_drive = 'driver=throttle,' \
34
+ 'node-name=source,' \
35
'throttle-group=tg0,' \
36
f'file.driver={iotests.imgfmt},' \
37
f'file.file.filename={self.test_img}'
38
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
39
40
def tearDown(self):
41
self.vm.shutdown()
42
- for img in (self.test_img, self.target_img, self.base_img):
43
+ for img in (self.test_img, self.target_img, self.base_img,
44
+ self.overlay_img):
45
iotests.try_remove(img)
46
47
def do_test_stop(self, cmd, **args):
48
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
49
sync="full")
50
51
def test_block_commit(self):
52
- self.do_test_stop("block-commit", device="drive0")
53
+ # Add overlay above the source node so that we actually use a
54
+ # commit job instead of a mirror job
55
+
56
+ iotests.qemu_img('create', '-f', iotests.imgfmt, self.overlay_img,
57
+ '1G')
58
+
59
+ result = self.vm.qmp('blockdev-add', **{
60
+ 'node-name': 'overlay',
61
+ 'driver': iotests.imgfmt,
62
+ 'file': {
63
+ 'driver': 'file',
64
+ 'filename': self.overlay_img
65
+ }
66
+ })
67
+ self.assert_qmp(result, 'return', {})
68
+
69
+ result = self.vm.qmp('blockdev-snapshot',
70
+ node='source', overlay='overlay')
71
+ self.assert_qmp(result, 'return', {})
72
+
73
+ self.do_test_stop('block-commit', device='drive0', top_node='source')
74
75
if __name__ == '__main__':
76
iotests.main(supported_fmts=["qcow2"],
77
--
78
2.29.2
79
80
diff view generated by jsdifflib
Deleted patch
1
Issuing 'stop' on the VM drains all nodes. If the mirror job has many
2
large requests in flight, this may lead to significant I/O that looks a
3
bit like 'stop' would make the job try to complete (which is what 129
4
should verify not to happen).
5
1
6
We can limit the I/O in flight by limiting the buffer size, so mirror
7
will make very little progress during the 'stop' drain.
8
9
(We do not need to do anything about commit, which has a buffer size of
10
512 kB by default; or backup, which goes cluster by cluster. Once we
11
have asynchronous requests for backup, that will change, but then we can
12
fine-tune the backup job to only perform a single request on a very
13
small chunk, too.)
14
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
17
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
19
Message-Id: <20210118105720.14824-9-mreitz@redhat.com>
20
---
21
tests/qemu-iotests/129 | 2 +-
22
1 file changed, 1 insertion(+), 1 deletion(-)
23
24
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
25
index XXXXXXX..XXXXXXX 100755
26
--- a/tests/qemu-iotests/129
27
+++ b/tests/qemu-iotests/129
28
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
29
def test_drive_mirror(self):
30
self.do_test_stop("drive-mirror", device="drive0",
31
target=self.target_img, format=iotests.imgfmt,
32
- sync="full")
33
+ sync="full", buf_size=65536)
34
35
def test_drive_backup(self):
36
self.do_test_stop("drive-backup", device="drive0",
37
--
38
2.29.2
39
40
diff view generated by jsdifflib
Deleted patch
1
And consequentially drop it from 297's skip list.
2
1
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
5
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Message-Id: <20210118105720.14824-10-mreitz@redhat.com>
7
---
8
tests/qemu-iotests/129 | 4 ++--
9
tests/qemu-iotests/297 | 2 +-
10
2 files changed, 3 insertions(+), 3 deletions(-)
11
12
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
13
index XXXXXXX..XXXXXXX 100755
14
--- a/tests/qemu-iotests/129
15
+++ b/tests/qemu-iotests/129
16
@@ -XXX,XX +XXX,XX @@
17
18
import os
19
import iotests
20
-import time
21
22
class TestStopWithBlockJob(iotests.QMPTestCase):
23
test_img = os.path.join(iotests.test_dir, 'test.img')
24
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
25
iotests.qemu_img('create', '-f', iotests.imgfmt, self.base_img, "1G")
26
iotests.qemu_img('create', '-f', iotests.imgfmt, self.test_img,
27
"-b", self.base_img, '-F', iotests.imgfmt)
28
- iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M', self.test_img)
29
+ iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M',
30
+ self.test_img)
31
self.vm = iotests.VM()
32
self.vm.add_object('throttle-group,id=tg0,x-bps-total=1024')
33
34
diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297
35
index XXXXXXX..XXXXXXX 100755
36
--- a/tests/qemu-iotests/297
37
+++ b/tests/qemu-iotests/297
38
@@ -XXX,XX +XXX,XX @@ import iotests
39
# TODO: Empty this list!
40
SKIP_FILES = (
41
'030', '040', '041', '044', '045', '055', '056', '057', '065', '093',
42
- '096', '118', '124', '129', '132', '136', '139', '147', '148', '149',
43
+ '096', '118', '124', '132', '136', '139', '147', '148', '149',
44
'151', '152', '155', '163', '165', '169', '194', '196', '199', '202',
45
'203', '205', '206', '207', '208', '210', '211', '212', '213', '216',
46
'218', '219', '222', '224', '228', '234', '235', '236', '237', '238',
47
--
48
2.29.2
49
50
diff view generated by jsdifflib
Deleted patch
1
And consequentially drop it from 297's skip list.
2
1
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Reviewed-by: Willian Rampazzo <willianr@redhat.com>
6
Message-Id: <20210118105720.14824-11-mreitz@redhat.com>
7
---
8
tests/qemu-iotests/297 | 2 +-
9
tests/qemu-iotests/300 | 18 +++++++++++++++---
10
2 files changed, 16 insertions(+), 4 deletions(-)
11
12
diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297
13
index XXXXXXX..XXXXXXX 100755
14
--- a/tests/qemu-iotests/297
15
+++ b/tests/qemu-iotests/297
16
@@ -XXX,XX +XXX,XX @@ SKIP_FILES = (
17
'218', '219', '222', '224', '228', '234', '235', '236', '237', '238',
18
'240', '242', '245', '246', '248', '255', '256', '257', '258', '260',
19
'262', '264', '266', '274', '277', '280', '281', '295', '296', '298',
20
- '299', '300', '302', '303', '304', '307',
21
+ '299', '302', '303', '304', '307',
22
'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py'
23
)
24
25
diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
26
index XXXXXXX..XXXXXXX 100755
27
--- a/tests/qemu-iotests/300
28
+++ b/tests/qemu-iotests/300
29
@@ -XXX,XX +XXX,XX @@ import os
30
import random
31
import re
32
from typing import Dict, List, Optional, Union
33
+
34
import iotests
35
+
36
+# Import qemu after iotests.py has amended sys.path
37
+# pylint: disable=wrong-import-order
38
import qemu
39
40
BlockBitmapMapping = List[Dict[str, Union[str, List[Dict[str, str]]]]]
41
@@ -XXX,XX +XXX,XX @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
42
If @msg is None, check that there has not been any error.
43
"""
44
self.vm_b.shutdown()
45
+
46
+ log = self.vm_b.get_log()
47
+ assert log is not None # Loaded after shutdown
48
+
49
if msg is None:
50
- self.assertNotIn('qemu-system-', self.vm_b.get_log())
51
+ self.assertNotIn('qemu-system-', log)
52
else:
53
- self.assertIn(msg, self.vm_b.get_log())
54
+ self.assertIn(msg, log)
55
56
@staticmethod
57
def mapping(node_name: str, node_alias: str,
58
@@ -XXX,XX +XXX,XX @@ class TestBlockBitmapMappingErrors(TestDirtyBitmapMigration):
59
60
# Check for the error in the source's log
61
self.vm_a.shutdown()
62
+
63
+ log = self.vm_a.get_log()
64
+ assert log is not None # Loaded after shutdown
65
+
66
self.assertIn(f"Cannot migrate bitmap '{name}' on node "
67
f"'{self.src_node_name}': Name is longer than 255 bytes",
68
- self.vm_a.get_log())
69
+ log)
70
71
# Expect abnormal shutdown of the destination VM because of
72
# the failed migration
73
--
74
2.29.2
75
76
diff view generated by jsdifflib
Deleted patch
1
Disposition (action) for any given signal is global for the process.
2
When two threads run coroutine-sigaltstack's qemu_coroutine_new()
3
concurrently, they may interfere with each other: One of them may revert
4
the SIGUSR2 handler to SIG_DFL, between the other thread (a) setting up
5
coroutine_trampoline() as the handler and (b) raising SIGUSR2. That
6
SIGUSR2 will then terminate the QEMU process abnormally.
7
1
8
We have to ensure that only one thread at a time can modify the
9
process-global SIGUSR2 handler. To do so, wrap the whole section where
10
that is done in a mutex.
11
12
Alternatively, we could for example have the SIGUSR2 handler always be
13
coroutine_trampoline(), so there would be no need to invoke sigaction()
14
in qemu_coroutine_new(). Laszlo has posted a patch to do so here:
15
16
https://lists.nongnu.org/archive/html/qemu-devel/2021-01/msg05962.html
17
18
However, given that coroutine-sigaltstack is more of a fallback
19
implementation for platforms that do not support ucontext, that change
20
may be a bit too invasive to be comfortable with it. The mutex proposed
21
here may negatively impact performance, but the change is much simpler.
22
23
Signed-off-by: Max Reitz <mreitz@redhat.com>
24
Message-Id: <20210125120305.19520-1-mreitz@redhat.com>
25
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
26
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
27
---
28
util/coroutine-sigaltstack.c | 9 +++++++++
29
1 file changed, 9 insertions(+)
30
31
diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c
32
index XXXXXXX..XXXXXXX 100644
33
--- a/util/coroutine-sigaltstack.c
34
+++ b/util/coroutine-sigaltstack.c
35
@@ -XXX,XX +XXX,XX @@ Coroutine *qemu_coroutine_new(void)
36
sigset_t sigs;
37
sigset_t osigs;
38
sigjmp_buf old_env;
39
+ static pthread_mutex_t sigusr2_mutex = PTHREAD_MUTEX_INITIALIZER;
40
41
/* The way to manipulate stack is with the sigaltstack function. We
42
* prepare a stack, with it delivering a signal to ourselves and then
43
@@ -XXX,XX +XXX,XX @@ Coroutine *qemu_coroutine_new(void)
44
sa.sa_handler = coroutine_trampoline;
45
sigfillset(&sa.sa_mask);
46
sa.sa_flags = SA_ONSTACK;
47
+
48
+ /*
49
+ * sigaction() is a process-global operation. We must not run
50
+ * this code in multiple threads at once.
51
+ */
52
+ pthread_mutex_lock(&sigusr2_mutex);
53
if (sigaction(SIGUSR2, &sa, &osa) != 0) {
54
abort();
55
}
56
@@ -XXX,XX +XXX,XX @@ Coroutine *qemu_coroutine_new(void)
57
* Restore the old SIGUSR2 signal handler and mask
58
*/
59
sigaction(SIGUSR2, &osa, NULL);
60
+ pthread_mutex_unlock(&sigusr2_mutex);
61
+
62
pthread_sigmask(SIG_SETMASK, &osigs, NULL);
63
64
/*
65
--
66
2.29.2
67
68
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Experiments show, that copy_range is not always making things faster.
4
So, to make experimentation simpler, let's add a parameter. Some more
5
perf parameters will be added soon, so here is a new struct.
6
7
For now, add new backup qmp parameter with x- prefix for the following
8
reasons:
9
10
- We are going to add more performance parameters, some will be
11
related to the whole block-copy process, some only to background
12
copying in backup (ignored for copy-before-write operations).
13
- On the other hand, we are going to use block-copy interface in other
14
block jobs, which will need performance options as well.. And it
15
should be the same structure or at least somehow related.
16
17
So, there are too much unclean things about how the interface and now
18
we need the new options mostly for testing. Let's keep them
19
experimental for a while.
20
21
In do_backup_common() new x-perf parameter handled in a way to
22
make further options addition simpler.
23
24
We add use-copy-range with default=true, and we'll change the default
25
in further patch, after moving backup to use block-copy.
26
27
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
28
Reviewed-by: Max Reitz <mreitz@redhat.com>
29
Message-Id: <20210116214705.822267-2-vsementsov@virtuozzo.com>
30
[mreitz: s/5\.2/6.0/]
31
Signed-off-by: Max Reitz <mreitz@redhat.com>
32
---
33
qapi/block-core.json | 17 ++++++++++++++++-
34
block/backup-top.h | 1 +
35
include/block/block-copy.h | 2 +-
36
include/block/block_int.h | 3 +++
37
block/backup-top.c | 4 +++-
38
block/backup.c | 6 +++++-
39
block/block-copy.c | 4 ++--
40
block/replication.c | 2 ++
41
blockdev.c | 8 ++++++++
42
9 files changed, 41 insertions(+), 6 deletions(-)
43
44
diff --git a/qapi/block-core.json b/qapi/block-core.json
45
index XXXXXXX..XXXXXXX 100644
46
--- a/qapi/block-core.json
47
+++ b/qapi/block-core.json
48
@@ -XXX,XX +XXX,XX @@
49
{ 'struct': 'BlockdevSnapshot',
50
'data': { 'node': 'str', 'overlay': 'str' } }
51
52
+##
53
+# @BackupPerf:
54
+#
55
+# Optional parameters for backup. These parameters don't affect
56
+# functionality, but may significantly affect performance.
57
+#
58
+# @use-copy-range: Use copy offloading. Default true.
59
+#
60
+# Since: 6.0
61
+##
62
+{ 'struct': 'BackupPerf',
63
+ 'data': { '*use-copy-range': 'bool' }}
64
+
65
##
66
# @BackupCommon:
67
#
68
@@ -XXX,XX +XXX,XX @@
69
# above node specified by @drive. If this option is not given,
70
# a node name is autogenerated. (Since: 4.2)
71
#
72
+# @x-perf: Performance options. (Since 6.0)
73
+#
74
# Note: @on-source-error and @on-target-error only affect background
75
# I/O. If an error occurs during a guest write request, the device's
76
# rerror/werror actions will be used.
77
@@ -XXX,XX +XXX,XX @@
78
'*on-source-error': 'BlockdevOnError',
79
'*on-target-error': 'BlockdevOnError',
80
'*auto-finalize': 'bool', '*auto-dismiss': 'bool',
81
- '*filter-node-name': 'str' } }
82
+ '*filter-node-name': 'str', '*x-perf': 'BackupPerf' } }
83
84
##
85
# @DriveBackup:
86
diff --git a/block/backup-top.h b/block/backup-top.h
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/backup-top.h
89
+++ b/block/backup-top.h
90
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
91
BlockDriverState *target,
92
const char *filter_node_name,
93
uint64_t cluster_size,
94
+ BackupPerf *perf,
95
BdrvRequestFlags write_flags,
96
BlockCopyState **bcs,
97
Error **errp);
98
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
99
index XXXXXXX..XXXXXXX 100644
100
--- a/include/block/block-copy.h
101
+++ b/include/block/block-copy.h
102
@@ -XXX,XX +XXX,XX @@ typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
103
typedef struct BlockCopyState BlockCopyState;
104
105
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
106
- int64_t cluster_size,
107
+ int64_t cluster_size, bool use_copy_range,
108
BdrvRequestFlags write_flags,
109
Error **errp);
110
111
diff --git a/include/block/block_int.h b/include/block/block_int.h
112
index XXXXXXX..XXXXXXX 100644
113
--- a/include/block/block_int.h
114
+++ b/include/block/block_int.h
115
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
116
* @sync_mode: What parts of the disk image should be copied to the destination.
117
* @sync_bitmap: The dirty bitmap if sync_mode is 'bitmap' or 'incremental'
118
* @bitmap_mode: The bitmap synchronization policy to use.
119
+ * @perf: Performance options. All actual fields assumed to be present,
120
+ * all ".has_*" fields are ignored.
121
* @on_source_error: The action to take upon error reading from the source.
122
* @on_target_error: The action to take upon error writing to the target.
123
* @creation_flags: Flags that control the behavior of the Job lifetime.
124
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
125
BitmapSyncMode bitmap_mode,
126
bool compress,
127
const char *filter_node_name,
128
+ BackupPerf *perf,
129
BlockdevOnError on_source_error,
130
BlockdevOnError on_target_error,
131
int creation_flags,
132
diff --git a/block/backup-top.c b/block/backup-top.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/block/backup-top.c
135
+++ b/block/backup-top.c
136
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
137
BlockDriverState *target,
138
const char *filter_node_name,
139
uint64_t cluster_size,
140
+ BackupPerf *perf,
141
BdrvRequestFlags write_flags,
142
BlockCopyState **bcs,
143
Error **errp)
144
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
145
146
state->cluster_size = cluster_size;
147
state->bcs = block_copy_state_new(top->backing, state->target,
148
- cluster_size, write_flags, &local_err);
149
+ cluster_size, perf->use_copy_range,
150
+ write_flags, &local_err);
151
if (local_err) {
152
error_prepend(&local_err, "Cannot create block-copy-state: ");
153
goto fail;
154
diff --git a/block/backup.c b/block/backup.c
155
index XXXXXXX..XXXXXXX 100644
156
--- a/block/backup.c
157
+++ b/block/backup.c
158
@@ -XXX,XX +XXX,XX @@ typedef struct BackupBlockJob {
159
uint64_t len;
160
uint64_t bytes_read;
161
int64_t cluster_size;
162
+ BackupPerf perf;
163
164
BlockCopyState *bcs;
165
} BackupBlockJob;
166
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
167
BitmapSyncMode bitmap_mode,
168
bool compress,
169
const char *filter_node_name,
170
+ BackupPerf *perf,
171
BlockdevOnError on_source_error,
172
BlockdevOnError on_target_error,
173
int creation_flags,
174
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
175
(compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
176
177
backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
178
- cluster_size, write_flags, &bcs, errp);
179
+ cluster_size, perf,
180
+ write_flags, &bcs, errp);
181
if (!backup_top) {
182
goto error;
183
}
184
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
185
job->bcs = bcs;
186
job->cluster_size = cluster_size;
187
job->len = len;
188
+ job->perf = *perf;
189
190
block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job);
191
block_copy_set_progress_meter(bcs, &job->common.job.progress);
192
diff --git a/block/block-copy.c b/block/block-copy.c
193
index XXXXXXX..XXXXXXX 100644
194
--- a/block/block-copy.c
195
+++ b/block/block-copy.c
196
@@ -XXX,XX +XXX,XX @@ static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target)
197
}
198
199
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
200
- int64_t cluster_size,
201
+ int64_t cluster_size, bool use_copy_range,
202
BdrvRequestFlags write_flags, Error **errp)
203
{
204
BlockCopyState *s;
205
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
206
* We enable copy-range, but keep small copy_size, until first
207
* successful copy_range (look at block_copy_do_copy).
208
*/
209
- s->use_copy_range = true;
210
+ s->use_copy_range = use_copy_range;
211
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
212
}
213
214
diff --git a/block/replication.c b/block/replication.c
215
index XXXXXXX..XXXXXXX 100644
216
--- a/block/replication.c
217
+++ b/block/replication.c
218
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
219
int64_t active_length, hidden_length, disk_length;
220
AioContext *aio_context;
221
Error *local_err = NULL;
222
+ BackupPerf perf = { .use_copy_range = true };
223
224
aio_context = bdrv_get_aio_context(bs);
225
aio_context_acquire(aio_context);
226
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
227
s->backup_job = backup_job_create(
228
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
229
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
230
+ &perf,
231
BLOCKDEV_ON_ERROR_REPORT,
232
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
233
backup_job_completed, bs, NULL, &local_err);
234
diff --git a/blockdev.c b/blockdev.c
235
index XXXXXXX..XXXXXXX 100644
236
--- a/blockdev.c
237
+++ b/blockdev.c
238
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
239
{
240
BlockJob *job = NULL;
241
BdrvDirtyBitmap *bmap = NULL;
242
+ BackupPerf perf = { .use_copy_range = true };
243
int job_flags = JOB_DEFAULT;
244
245
if (!backup->has_speed) {
246
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
247
backup->compress = false;
248
}
249
250
+ if (backup->x_perf) {
251
+ if (backup->x_perf->has_use_copy_range) {
252
+ perf.use_copy_range = backup->x_perf->use_copy_range;
253
+ }
254
+ }
255
+
256
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
257
(backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
258
/* done before desugaring 'incremental' to print the right message */
259
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
260
backup->sync, bmap, backup->bitmap_mode,
261
backup->compress,
262
backup->filter_node_name,
263
+ &perf,
264
backup->on_source_error,
265
backup->on_target_error,
266
job_flags, NULL, NULL, txn, errp);
267
--
268
2.29.2
269
270
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
Refactor common path to use BlockCopyCallState pointer as parameter, to
3
Until ESXi 6.5 VMware used the vmfsSparse format for snapshots (VMDK3 in
4
prepare it for use in asynchronous block-copy (at least, we'll need to
4
QEMU).
5
run block-copy in a coroutine, passing the whole parameters as one
5
6
pointer).
6
This format was lacking in the following:
7
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
* Grain directory (L1) and grain table (L2) entries were 32-bit,
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
allowing access to only 2TB (slightly less) of data.
10
Message-Id: <20210116214705.822267-3-vsementsov@virtuozzo.com>
10
* The grain size (default) was 512 bytes - leading to data
11
fragmentation and many grain tables.
12
* For space reclamation purposes, it was necessary to find all the
13
grains which are not pointed to by any grain table - so a reverse
14
mapping of "offset of grain in vmdk" to "grain table" must be
15
constructed - which takes large amounts of CPU/RAM.
16
17
The format specification can be found in VMware's documentation:
18
https://www.vmware.com/support/developer/vddk/vmdk_50_technote.pdf
19
20
In ESXi 6.5, to support snapshot files larger than 2TB, a new format was
21
introduced: SESparse (Space Efficient).
22
23
This format fixes the above issues:
24
25
* All entries are now 64-bit.
26
* The grain size (default) is 4KB.
27
* Grain directory and grain tables are now located at the beginning
28
of the file.
29
+ seSparse format reserves space for all grain tables.
30
+ Grain tables can be addressed using an index.
31
+ Grains are located in the end of the file and can also be
32
addressed with an index.
33
- seSparse vmdks of large disks (64TB) have huge preallocated
34
headers - mainly due to L2 tables, even for empty snapshots.
35
* The header contains a reverse mapping ("backmap") of "offset of
36
grain in vmdk" to "grain table" and a bitmap ("free bitmap") which
37
specifies for each grain - whether it is allocated or not.
38
Using these data structures we can implement space reclamation
39
efficiently.
40
* Due to the fact that the header now maintains two mappings:
41
* The regular one (grain directory & grain tables)
42
* A reverse one (backmap and free bitmap)
43
These data structures can lose consistency upon crash and result
44
in a corrupted VMDK.
45
Therefore, a journal is also added to the VMDK and is replayed
46
when the VMware reopens the file after a crash.
47
48
Since ESXi 6.7 - SESparse is the only snapshot format available.
49
50
Unfortunately, VMware does not provide documentation regarding the new
51
seSparse format.
52
53
This commit is based on black-box research of the seSparse format.
54
Various in-guest block operations and their effect on the snapshot file
55
were tested.
56
57
The only VMware provided source of information (regarding the underlying
58
implementation) was a log file on the ESXi:
59
60
/var/log/hostd.log
61
62
Whenever an seSparse snapshot is created - the log is being populated
63
with seSparse records.
64
65
Relevant log records are of the form:
66
67
[...] Const Header:
68
[...] constMagic = 0xcafebabe
69
[...] version = 2.1
70
[...] capacity = 204800
71
[...] grainSize = 8
72
[...] grainTableSize = 64
73
[...] flags = 0
74
[...] Extents:
75
[...] Header : <1 : 1>
76
[...] JournalHdr : <2 : 2>
77
[...] Journal : <2048 : 2048>
78
[...] GrainDirectory : <4096 : 2048>
79
[...] GrainTables : <6144 : 2048>
80
[...] FreeBitmap : <8192 : 2048>
81
[...] BackMap : <10240 : 2048>
82
[...] Grain : <12288 : 204800>
83
[...] Volatile Header:
84
[...] volatileMagic = 0xcafecafe
85
[...] FreeGTNumber = 0
86
[...] nextTxnSeqNumber = 0
87
[...] replayJournal = 0
88
89
The sizes that are seen in the log file are in sectors.
90
Extents are of the following format: <offset : size>
91
92
This commit is a strict implementation which enforces:
93
* magics
94
* version number 2.1
95
* grain size of 8 sectors (4KB)
96
* grain table size of 64 sectors
97
* zero flags
98
* extent locations
99
100
Additionally, this commit proivdes only a subset of the functionality
101
offered by seSparse's format:
102
* Read-only
103
* No journal replay
104
* No space reclamation
105
* No unmap support
106
107
Hence, journal header, journal, free bitmap and backmap extents are
108
unused, only the "classic" (L1 -> L2 -> data) grain access is
109
implemented.
110
111
However there are several differences in the grain access itself.
112
Grain directory (L1):
113
* Grain directory entries are indexes (not offsets) to grain
114
tables.
115
* Valid grain directory entries have their highest nibble set to
116
0x1.
117
* Since grain tables are always located in the beginning of the
118
file - the index can fit into 32 bits - so we can use its low
119
part if it's valid.
120
Grain table (L2):
121
* Grain table entries are indexes (not offsets) to grains.
122
* If the highest nibble of the entry is:
123
0x0:
124
The grain in not allocated.
125
The rest of the bytes are 0.
126
0x1:
127
The grain is unmapped - guest sees a zero grain.
128
The rest of the bits point to the previously mapped grain,
129
see 0x3 case.
130
0x2:
131
The grain is zero.
132
0x3:
133
The grain is allocated - to get the index calculate:
134
((entry & 0x0fff000000000000) >> 48) |
135
((entry & 0x0000ffffffffffff) << 12)
136
* The difference between 0x1 and 0x2 is that 0x1 is an unallocated
137
grain which results from the guest using sg_unmap to unmap the
138
grain - but the grain itself still exists in the grain extent - a
139
space reclamation procedure should delete it.
140
Unmapping a zero grain has no effect (0x2 will not change to 0x1)
141
but unmapping an unallocated grain will (0x0 to 0x1) - naturally.
142
143
In order to implement seSparse some fields had to be changed to support
144
both 32-bit and 64-bit entry sizes.
145
146
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
147
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
148
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
149
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
150
Message-id: 20190620091057.47441-4-shmuel.eiderman@oracle.com
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
151
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
152
---
13
block/block-copy.c | 51 ++++++++++++++++++++++++++++++++++------------
153
block/vmdk.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++---
14
1 file changed, 38 insertions(+), 13 deletions(-)
154
1 file changed, 342 insertions(+), 16 deletions(-)
15
155
16
diff --git a/block/block-copy.c b/block/block-copy.c
156
diff --git a/block/vmdk.c b/block/vmdk.c
17
index XXXXXXX..XXXXXXX 100644
157
index XXXXXXX..XXXXXXX 100644
18
--- a/block/block-copy.c
158
--- a/block/vmdk.c
19
+++ b/block/block-copy.c
159
+++ b/block/vmdk.c
20
@@ -XXX,XX +XXX,XX @@
160
@@ -XXX,XX +XXX,XX @@ typedef struct {
21
static coroutine_fn int block_copy_task_entry(AioTask *task);
161
uint16_t compressAlgorithm;
22
162
} QEMU_PACKED VMDK4Header;
23
typedef struct BlockCopyCallState {
163
24
+ /* IN parameters */
164
+typedef struct VMDKSESparseConstHeader {
25
+ BlockCopyState *s;
165
+ uint64_t magic;
26
+ int64_t offset;
166
+ uint64_t version;
27
+ int64_t bytes;
167
+ uint64_t capacity;
28
+
168
+ uint64_t grain_size;
29
+ /* State */
169
+ uint64_t grain_table_size;
30
bool failed;
170
+ uint64_t flags;
31
+
171
+ uint64_t reserved1;
32
+ /* OUT parameters */
172
+ uint64_t reserved2;
33
bool error_is_read;
173
+ uint64_t reserved3;
34
} BlockCopyCallState;
174
+ uint64_t reserved4;
35
175
+ uint64_t volatile_header_offset;
36
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
176
+ uint64_t volatile_header_size;
37
* Returns 1 if dirty clusters found and successfully copied, 0 if no dirty
177
+ uint64_t journal_header_offset;
38
* clusters found and -errno on failure.
178
+ uint64_t journal_header_size;
39
*/
179
+ uint64_t journal_offset;
40
-static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
180
+ uint64_t journal_size;
41
- int64_t offset, int64_t bytes,
181
+ uint64_t grain_dir_offset;
42
- bool *error_is_read)
182
+ uint64_t grain_dir_size;
43
+static int coroutine_fn
183
+ uint64_t grain_tables_offset;
44
+block_copy_dirty_clusters(BlockCopyCallState *call_state)
184
+ uint64_t grain_tables_size;
45
{
185
+ uint64_t free_bitmap_offset;
46
+ BlockCopyState *s = call_state->s;
186
+ uint64_t free_bitmap_size;
47
+ int64_t offset = call_state->offset;
187
+ uint64_t backmap_offset;
48
+ int64_t bytes = call_state->bytes;
188
+ uint64_t backmap_size;
49
+
189
+ uint64_t grains_offset;
50
int ret = 0;
190
+ uint64_t grains_size;
51
bool found_dirty = false;
191
+ uint8_t pad[304];
52
int64_t end = offset + bytes;
192
+} QEMU_PACKED VMDKSESparseConstHeader;
53
AioTaskPool *aio = NULL;
193
+
54
- BlockCopyCallState call_state = {false, false};
194
+typedef struct VMDKSESparseVolatileHeader {
55
195
+ uint64_t magic;
56
/*
196
+ uint64_t free_gt_number;
57
* block_copy() user is responsible for keeping source and target in same
197
+ uint64_t next_txn_seq_number;
58
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
198
+ uint64_t replay_journal;
59
BlockCopyTask *task;
199
+ uint8_t pad[480];
60
int64_t status_bytes;
200
+} QEMU_PACKED VMDKSESparseVolatileHeader;
61
201
+
62
- task = block_copy_task_create(s, &call_state, offset, bytes);
202
#define L2_CACHE_SIZE 16
63
+ task = block_copy_task_create(s, call_state, offset, bytes);
203
64
if (!task) {
204
typedef struct VmdkExtent {
65
/* No more dirty bits in the bitmap */
205
@@ -XXX,XX +XXX,XX @@ typedef struct VmdkExtent {
66
trace_block_copy_skip_range(s, offset, bytes);
206
bool compressed;
67
@@ -XXX,XX +XXX,XX @@ out:
207
bool has_marker;
68
208
bool has_zero_grain;
69
aio_task_pool_free(aio);
209
+ bool sesparse;
70
}
210
+ uint64_t sesparse_l2_tables_offset;
71
- if (error_is_read && ret < 0) {
211
+ uint64_t sesparse_clusters_offset;
72
- *error_is_read = call_state.error_is_read;
212
+ int32_t entry_size;
73
- }
213
int version;
74
214
int64_t sectors;
75
return ret < 0 ? ret : found_dirty;
215
int64_t end_sector;
76
}
216
int64_t flat_start_offset;
77
217
int64_t l1_table_offset;
78
/*
218
int64_t l1_backup_table_offset;
79
- * block_copy
219
- uint32_t *l1_table;
80
+ * block_copy_common
220
+ void *l1_table;
81
*
221
uint32_t *l1_backup_table;
82
* Copy requested region, accordingly to dirty bitmap.
222
unsigned int l1_size;
83
* Collaborate with parallel block_copy requests: if they succeed it will help
223
uint32_t l1_entry_sectors;
84
@@ -XXX,XX +XXX,XX @@ out:
224
85
* it means that some I/O operation failed in context of _this_ block_copy call,
225
unsigned int l2_size;
86
* not some parallel operation.
226
- uint32_t *l2_cache;
87
*/
227
+ void *l2_cache;
88
-int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
228
uint32_t l2_cache_offsets[L2_CACHE_SIZE];
89
- bool *error_is_read)
229
uint32_t l2_cache_counts[L2_CACHE_SIZE];
90
+static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
230
91
{
231
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
92
int ret;
232
* minimal L2 table size: 512 entries
93
233
* 8 TB is still more than the maximal value supported for
94
do {
234
* VMDK3 & VMDK4 which is 2TB.
95
- ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read);
235
+ * 64TB - for "ESXi seSparse Extent"
96
+ ret = block_copy_dirty_clusters(call_state);
236
+ * minimal cluster size: 512B (default is 4KB)
97
237
+ * L2 table size: 4096 entries (const).
98
if (ret == 0) {
238
+ * 64TB is more than the maximal value supported for
99
- ret = block_copy_wait_one(s, offset, bytes);
239
+ * seSparse VMDKs (which is slightly less than 64TB)
100
+ ret = block_copy_wait_one(call_state->s, call_state->offset,
240
*/
101
+ call_state->bytes);
241
error_setg(errp, "L1 size too big");
102
}
242
return -EFBIG;
103
243
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
104
/*
244
extent->l2_size = l2_size;
105
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
245
extent->cluster_sectors = flat ? sectors : cluster_sectors;
246
extent->next_cluster_sector = ROUND_UP(nb_sectors, cluster_sectors);
247
+ extent->entry_size = sizeof(uint32_t);
248
249
if (s->num_extents > 1) {
250
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
251
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
252
int i;
253
254
/* read the L1 table */
255
- l1_size = extent->l1_size * sizeof(uint32_t);
256
+ l1_size = extent->l1_size * extent->entry_size;
257
extent->l1_table = g_try_malloc(l1_size);
258
if (l1_size && extent->l1_table == NULL) {
259
return -ENOMEM;
260
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
261
goto fail_l1;
262
}
263
for (i = 0; i < extent->l1_size; i++) {
264
- le32_to_cpus(&extent->l1_table[i]);
265
+ if (extent->entry_size == sizeof(uint64_t)) {
266
+ le64_to_cpus((uint64_t *)extent->l1_table + i);
267
+ } else {
268
+ assert(extent->entry_size == sizeof(uint32_t));
269
+ le32_to_cpus((uint32_t *)extent->l1_table + i);
270
+ }
271
}
272
273
if (extent->l1_backup_table_offset) {
274
+ assert(!extent->sesparse);
275
extent->l1_backup_table = g_try_malloc(l1_size);
276
if (l1_size && extent->l1_backup_table == NULL) {
277
ret = -ENOMEM;
278
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
279
}
280
281
extent->l2_cache =
282
- g_new(uint32_t, extent->l2_size * L2_CACHE_SIZE);
283
+ g_malloc(extent->entry_size * extent->l2_size * L2_CACHE_SIZE);
284
return 0;
285
fail_l1b:
286
g_free(extent->l1_backup_table);
287
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
106
return ret;
288
return ret;
107
}
289
}
108
290
109
+int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
291
+#define SESPARSE_CONST_HEADER_MAGIC UINT64_C(0x00000000cafebabe)
110
+ bool *error_is_read)
292
+#define SESPARSE_VOLATILE_HEADER_MAGIC UINT64_C(0x00000000cafecafe)
293
+
294
+/* Strict checks - format not officially documented */
295
+static int check_se_sparse_const_header(VMDKSESparseConstHeader *header,
296
+ Error **errp)
111
+{
297
+{
112
+ BlockCopyCallState call_state = {
298
+ header->magic = le64_to_cpu(header->magic);
113
+ .s = s,
299
+ header->version = le64_to_cpu(header->version);
114
+ .offset = start,
300
+ header->grain_size = le64_to_cpu(header->grain_size);
115
+ .bytes = bytes,
301
+ header->grain_table_size = le64_to_cpu(header->grain_table_size);
116
+ };
302
+ header->flags = le64_to_cpu(header->flags);
117
+
303
+ header->reserved1 = le64_to_cpu(header->reserved1);
118
+ int ret = block_copy_common(&call_state);
304
+ header->reserved2 = le64_to_cpu(header->reserved2);
119
+
305
+ header->reserved3 = le64_to_cpu(header->reserved3);
120
+ if (error_is_read && ret < 0) {
306
+ header->reserved4 = le64_to_cpu(header->reserved4);
121
+ *error_is_read = call_state.error_is_read;
307
+
308
+ header->volatile_header_offset =
309
+ le64_to_cpu(header->volatile_header_offset);
310
+ header->volatile_header_size = le64_to_cpu(header->volatile_header_size);
311
+
312
+ header->journal_header_offset = le64_to_cpu(header->journal_header_offset);
313
+ header->journal_header_size = le64_to_cpu(header->journal_header_size);
314
+
315
+ header->journal_offset = le64_to_cpu(header->journal_offset);
316
+ header->journal_size = le64_to_cpu(header->journal_size);
317
+
318
+ header->grain_dir_offset = le64_to_cpu(header->grain_dir_offset);
319
+ header->grain_dir_size = le64_to_cpu(header->grain_dir_size);
320
+
321
+ header->grain_tables_offset = le64_to_cpu(header->grain_tables_offset);
322
+ header->grain_tables_size = le64_to_cpu(header->grain_tables_size);
323
+
324
+ header->free_bitmap_offset = le64_to_cpu(header->free_bitmap_offset);
325
+ header->free_bitmap_size = le64_to_cpu(header->free_bitmap_size);
326
+
327
+ header->backmap_offset = le64_to_cpu(header->backmap_offset);
328
+ header->backmap_size = le64_to_cpu(header->backmap_size);
329
+
330
+ header->grains_offset = le64_to_cpu(header->grains_offset);
331
+ header->grains_size = le64_to_cpu(header->grains_size);
332
+
333
+ if (header->magic != SESPARSE_CONST_HEADER_MAGIC) {
334
+ error_setg(errp, "Bad const header magic: 0x%016" PRIx64,
335
+ header->magic);
336
+ return -EINVAL;
337
+ }
338
+
339
+ if (header->version != 0x0000000200000001) {
340
+ error_setg(errp, "Unsupported version: 0x%016" PRIx64,
341
+ header->version);
342
+ return -ENOTSUP;
343
+ }
344
+
345
+ if (header->grain_size != 8) {
346
+ error_setg(errp, "Unsupported grain size: %" PRIu64,
347
+ header->grain_size);
348
+ return -ENOTSUP;
349
+ }
350
+
351
+ if (header->grain_table_size != 64) {
352
+ error_setg(errp, "Unsupported grain table size: %" PRIu64,
353
+ header->grain_table_size);
354
+ return -ENOTSUP;
355
+ }
356
+
357
+ if (header->flags != 0) {
358
+ error_setg(errp, "Unsupported flags: 0x%016" PRIx64,
359
+ header->flags);
360
+ return -ENOTSUP;
361
+ }
362
+
363
+ if (header->reserved1 != 0 || header->reserved2 != 0 ||
364
+ header->reserved3 != 0 || header->reserved4 != 0) {
365
+ error_setg(errp, "Unsupported reserved bits:"
366
+ " 0x%016" PRIx64 " 0x%016" PRIx64
367
+ " 0x%016" PRIx64 " 0x%016" PRIx64,
368
+ header->reserved1, header->reserved2,
369
+ header->reserved3, header->reserved4);
370
+ return -ENOTSUP;
371
+ }
372
+
373
+ /* check that padding is 0 */
374
+ if (!buffer_is_zero(header->pad, sizeof(header->pad))) {
375
+ error_setg(errp, "Unsupported non-zero const header padding");
376
+ return -ENOTSUP;
377
+ }
378
+
379
+ return 0;
380
+}
381
+
382
+static int check_se_sparse_volatile_header(VMDKSESparseVolatileHeader *header,
383
+ Error **errp)
384
+{
385
+ header->magic = le64_to_cpu(header->magic);
386
+ header->free_gt_number = le64_to_cpu(header->free_gt_number);
387
+ header->next_txn_seq_number = le64_to_cpu(header->next_txn_seq_number);
388
+ header->replay_journal = le64_to_cpu(header->replay_journal);
389
+
390
+ if (header->magic != SESPARSE_VOLATILE_HEADER_MAGIC) {
391
+ error_setg(errp, "Bad volatile header magic: 0x%016" PRIx64,
392
+ header->magic);
393
+ return -EINVAL;
394
+ }
395
+
396
+ if (header->replay_journal) {
397
+ error_setg(errp, "Image is dirty, Replaying journal not supported");
398
+ return -ENOTSUP;
399
+ }
400
+
401
+ /* check that padding is 0 */
402
+ if (!buffer_is_zero(header->pad, sizeof(header->pad))) {
403
+ error_setg(errp, "Unsupported non-zero volatile header padding");
404
+ return -ENOTSUP;
405
+ }
406
+
407
+ return 0;
408
+}
409
+
410
+static int vmdk_open_se_sparse(BlockDriverState *bs,
411
+ BdrvChild *file,
412
+ int flags, Error **errp)
413
+{
414
+ int ret;
415
+ VMDKSESparseConstHeader const_header;
416
+ VMDKSESparseVolatileHeader volatile_header;
417
+ VmdkExtent *extent;
418
+
419
+ ret = bdrv_apply_auto_read_only(bs,
420
+ "No write support for seSparse images available", errp);
421
+ if (ret < 0) {
422
+ return ret;
423
+ }
424
+
425
+ assert(sizeof(const_header) == SECTOR_SIZE);
426
+
427
+ ret = bdrv_pread(file, 0, &const_header, sizeof(const_header));
428
+ if (ret < 0) {
429
+ bdrv_refresh_filename(file->bs);
430
+ error_setg_errno(errp, -ret,
431
+ "Could not read const header from file '%s'",
432
+ file->bs->filename);
433
+ return ret;
434
+ }
435
+
436
+ /* check const header */
437
+ ret = check_se_sparse_const_header(&const_header, errp);
438
+ if (ret < 0) {
439
+ return ret;
440
+ }
441
+
442
+ assert(sizeof(volatile_header) == SECTOR_SIZE);
443
+
444
+ ret = bdrv_pread(file,
445
+ const_header.volatile_header_offset * SECTOR_SIZE,
446
+ &volatile_header, sizeof(volatile_header));
447
+ if (ret < 0) {
448
+ bdrv_refresh_filename(file->bs);
449
+ error_setg_errno(errp, -ret,
450
+ "Could not read volatile header from file '%s'",
451
+ file->bs->filename);
452
+ return ret;
453
+ }
454
+
455
+ /* check volatile header */
456
+ ret = check_se_sparse_volatile_header(&volatile_header, errp);
457
+ if (ret < 0) {
458
+ return ret;
459
+ }
460
+
461
+ ret = vmdk_add_extent(bs, file, false,
462
+ const_header.capacity,
463
+ const_header.grain_dir_offset * SECTOR_SIZE,
464
+ 0,
465
+ const_header.grain_dir_size *
466
+ SECTOR_SIZE / sizeof(uint64_t),
467
+ const_header.grain_table_size *
468
+ SECTOR_SIZE / sizeof(uint64_t),
469
+ const_header.grain_size,
470
+ &extent,
471
+ errp);
472
+ if (ret < 0) {
473
+ return ret;
474
+ }
475
+
476
+ extent->sesparse = true;
477
+ extent->sesparse_l2_tables_offset = const_header.grain_tables_offset;
478
+ extent->sesparse_clusters_offset = const_header.grains_offset;
479
+ extent->entry_size = sizeof(uint64_t);
480
+
481
+ ret = vmdk_init_tables(bs, extent, errp);
482
+ if (ret) {
483
+ /* free extent allocated by vmdk_add_extent */
484
+ vmdk_free_last_extent(bs);
122
+ }
485
+ }
123
+
486
+
124
+ return ret;
487
+ return ret;
125
+}
488
+}
126
+
489
+
127
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
490
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
491
QDict *options, Error **errp);
492
493
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
494
* RW [size in sectors] SPARSE "file-name.vmdk"
495
* RW [size in sectors] VMFS "file-name.vmdk"
496
* RW [size in sectors] VMFSSPARSE "file-name.vmdk"
497
+ * RW [size in sectors] SESPARSE "file-name.vmdk"
498
*/
499
flat_offset = -1;
500
matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
501
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
502
503
if (sectors <= 0 ||
504
(strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
505
- strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) ||
506
+ strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE") &&
507
+ strcmp(type, "SESPARSE")) ||
508
(strcmp(access, "RW"))) {
509
continue;
510
}
511
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
512
return ret;
513
}
514
extent = &s->extents[s->num_extents - 1];
515
+ } else if (!strcmp(type, "SESPARSE")) {
516
+ ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
517
+ if (ret) {
518
+ bdrv_unref_child(bs, extent_file);
519
+ return ret;
520
+ }
521
+ extent = &s->extents[s->num_extents - 1];
522
} else {
523
error_setg(errp, "Unsupported extent type '%s'", type);
524
bdrv_unref_child(bs, extent_file);
525
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
526
if (strcmp(ct, "monolithicFlat") &&
527
strcmp(ct, "vmfs") &&
528
strcmp(ct, "vmfsSparse") &&
529
+ strcmp(ct, "seSparse") &&
530
strcmp(ct, "twoGbMaxExtentSparse") &&
531
strcmp(ct, "twoGbMaxExtentFlat")) {
532
error_setg(errp, "Unsupported image type '%s'", ct);
533
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
128
{
534
{
129
return s->copy_bitmap;
535
unsigned int l1_index, l2_offset, l2_index;
536
int min_index, i, j;
537
- uint32_t min_count, *l2_table;
538
+ uint32_t min_count;
539
+ void *l2_table;
540
bool zeroed = false;
541
int64_t ret;
542
int64_t cluster_sector;
543
+ unsigned int l2_size_bytes = extent->l2_size * extent->entry_size;
544
545
if (m_data) {
546
m_data->valid = 0;
547
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
548
if (l1_index >= extent->l1_size) {
549
return VMDK_ERROR;
550
}
551
- l2_offset = extent->l1_table[l1_index];
552
+ if (extent->sesparse) {
553
+ uint64_t l2_offset_u64;
554
+
555
+ assert(extent->entry_size == sizeof(uint64_t));
556
+
557
+ l2_offset_u64 = ((uint64_t *)extent->l1_table)[l1_index];
558
+ if (l2_offset_u64 == 0) {
559
+ l2_offset = 0;
560
+ } else if ((l2_offset_u64 & 0xffffffff00000000) != 0x1000000000000000) {
561
+ /*
562
+ * Top most nibble is 0x1 if grain table is allocated.
563
+ * strict check - top most 4 bytes must be 0x10000000 since max
564
+ * supported size is 64TB for disk - so no more than 64TB / 16MB
565
+ * grain directories which is smaller than uint32,
566
+ * where 16MB is the only supported default grain table coverage.
567
+ */
568
+ return VMDK_ERROR;
569
+ } else {
570
+ l2_offset_u64 = l2_offset_u64 & 0x00000000ffffffff;
571
+ l2_offset_u64 = extent->sesparse_l2_tables_offset +
572
+ l2_offset_u64 * l2_size_bytes / SECTOR_SIZE;
573
+ if (l2_offset_u64 > 0x00000000ffffffff) {
574
+ return VMDK_ERROR;
575
+ }
576
+ l2_offset = (unsigned int)(l2_offset_u64);
577
+ }
578
+ } else {
579
+ assert(extent->entry_size == sizeof(uint32_t));
580
+ l2_offset = ((uint32_t *)extent->l1_table)[l1_index];
581
+ }
582
if (!l2_offset) {
583
return VMDK_UNALLOC;
584
}
585
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
586
extent->l2_cache_counts[j] >>= 1;
587
}
588
}
589
- l2_table = extent->l2_cache + (i * extent->l2_size);
590
+ l2_table = (char *)extent->l2_cache + (i * l2_size_bytes);
591
goto found;
592
}
593
}
594
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
595
min_index = i;
596
}
597
}
598
- l2_table = extent->l2_cache + (min_index * extent->l2_size);
599
+ l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes);
600
BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD);
601
if (bdrv_pread(extent->file,
602
(int64_t)l2_offset * 512,
603
l2_table,
604
- extent->l2_size * sizeof(uint32_t)
605
- ) != extent->l2_size * sizeof(uint32_t)) {
606
+ l2_size_bytes
607
+ ) != l2_size_bytes) {
608
return VMDK_ERROR;
609
}
610
611
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
612
extent->l2_cache_counts[min_index] = 1;
613
found:
614
l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
615
- cluster_sector = le32_to_cpu(l2_table[l2_index]);
616
617
- if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
618
- zeroed = true;
619
+ if (extent->sesparse) {
620
+ cluster_sector = le64_to_cpu(((uint64_t *)l2_table)[l2_index]);
621
+ switch (cluster_sector & 0xf000000000000000) {
622
+ case 0x0000000000000000:
623
+ /* unallocated grain */
624
+ if (cluster_sector != 0) {
625
+ return VMDK_ERROR;
626
+ }
627
+ break;
628
+ case 0x1000000000000000:
629
+ /* scsi-unmapped grain - fallthrough */
630
+ case 0x2000000000000000:
631
+ /* zero grain */
632
+ zeroed = true;
633
+ break;
634
+ case 0x3000000000000000:
635
+ /* allocated grain */
636
+ cluster_sector = (((cluster_sector & 0x0fff000000000000) >> 48) |
637
+ ((cluster_sector & 0x0000ffffffffffff) << 12));
638
+ cluster_sector = extent->sesparse_clusters_offset +
639
+ cluster_sector * extent->cluster_sectors;
640
+ break;
641
+ default:
642
+ return VMDK_ERROR;
643
+ }
644
+ } else {
645
+ cluster_sector = le32_to_cpu(((uint32_t *)l2_table)[l2_index]);
646
+
647
+ if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
648
+ zeroed = true;
649
+ }
650
}
651
652
if (!cluster_sector || zeroed) {
653
if (!allocate) {
654
return zeroed ? VMDK_ZEROED : VMDK_UNALLOC;
655
}
656
+ assert(!extent->sesparse);
657
658
if (extent->next_cluster_sector >= VMDK_EXTENT_MAX_SECTORS) {
659
return VMDK_ERROR;
660
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
661
m_data->l1_index = l1_index;
662
m_data->l2_index = l2_index;
663
m_data->l2_offset = l2_offset;
664
- m_data->l2_cache_entry = &l2_table[l2_index];
665
+ m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index;
666
}
667
}
668
*cluster_offset = cluster_sector << BDRV_SECTOR_BITS;
669
@@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset,
670
if (!extent) {
671
return -EIO;
672
}
673
+ if (extent->sesparse) {
674
+ return -ENOTSUP;
675
+ }
676
offset_in_cluster = vmdk_find_offset_in_cluster(extent, offset);
677
n_bytes = MIN(bytes, extent->cluster_sectors * BDRV_SECTOR_SIZE
678
- offset_in_cluster);
130
--
679
--
131
2.29.2
680
2.21.0
132
681
133
682
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
We'll need async block-copy invocation to use in backup directly.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210116214705.822267-4-vsementsov@virtuozzo.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
include/block/block-copy.h | 29 ++++++++++++++
11
block/block-copy.c | 81 ++++++++++++++++++++++++++++++++++++--
12
2 files changed, 106 insertions(+), 4 deletions(-)
13
14
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/block-copy.h
17
+++ b/include/block/block-copy.h
18
@@ -XXX,XX +XXX,XX @@
19
#include "qemu/co-shared-resource.h"
20
21
typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
22
+typedef void (*BlockCopyAsyncCallbackFunc)(void *opaque);
23
typedef struct BlockCopyState BlockCopyState;
24
+typedef struct BlockCopyCallState BlockCopyCallState;
25
26
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
27
int64_t cluster_size, bool use_copy_range,
28
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
29
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
30
bool *error_is_read);
31
32
+/*
33
+ * Run block-copy in a coroutine, create corresponding BlockCopyCallState
34
+ * object and return pointer to it. Never returns NULL.
35
+ *
36
+ * Caller is responsible to call block_copy_call_free() to free
37
+ * BlockCopyCallState object.
38
+ */
39
+BlockCopyCallState *block_copy_async(BlockCopyState *s,
40
+ int64_t offset, int64_t bytes,
41
+ BlockCopyAsyncCallbackFunc cb,
42
+ void *cb_opaque);
43
+
44
+/*
45
+ * Free finished BlockCopyCallState. Trying to free running
46
+ * block-copy will crash.
47
+ */
48
+void block_copy_call_free(BlockCopyCallState *call_state);
49
+
50
+/*
51
+ * Note, that block-copy call is marked finished prior to calling
52
+ * the callback.
53
+ */
54
+bool block_copy_call_finished(BlockCopyCallState *call_state);
55
+bool block_copy_call_succeeded(BlockCopyCallState *call_state);
56
+bool block_copy_call_failed(BlockCopyCallState *call_state);
57
+int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read);
58
+
59
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s);
60
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip);
61
62
diff --git a/block/block-copy.c b/block/block-copy.c
63
index XXXXXXX..XXXXXXX 100644
64
--- a/block/block-copy.c
65
+++ b/block/block-copy.c
66
@@ -XXX,XX +XXX,XX @@
67
static coroutine_fn int block_copy_task_entry(AioTask *task);
68
69
typedef struct BlockCopyCallState {
70
- /* IN parameters */
71
+ /* IN parameters. Initialized in block_copy_async() and never changed. */
72
BlockCopyState *s;
73
int64_t offset;
74
int64_t bytes;
75
+ BlockCopyAsyncCallbackFunc cb;
76
+ void *cb_opaque;
77
+
78
+ /* Coroutine where async block-copy is running */
79
+ Coroutine *co;
80
81
/* State */
82
- bool failed;
83
+ int ret;
84
+ bool finished;
85
86
/* OUT parameters */
87
bool error_is_read;
88
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
89
90
ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
91
&error_is_read);
92
- if (ret < 0 && !t->call_state->failed) {
93
- t->call_state->failed = true;
94
+ if (ret < 0 && !t->call_state->ret) {
95
+ t->call_state->ret = ret;
96
t->call_state->error_is_read = error_is_read;
97
} else {
98
progress_work_done(t->s->progress, t->bytes);
99
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
100
*/
101
} while (ret > 0);
102
103
+ call_state->finished = true;
104
+
105
+ if (call_state->cb) {
106
+ call_state->cb(call_state->cb_opaque);
107
+ }
108
+
109
return ret;
110
}
111
112
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
113
return ret;
114
}
115
116
+static void coroutine_fn block_copy_async_co_entry(void *opaque)
117
+{
118
+ block_copy_common(opaque);
119
+}
120
+
121
+BlockCopyCallState *block_copy_async(BlockCopyState *s,
122
+ int64_t offset, int64_t bytes,
123
+ BlockCopyAsyncCallbackFunc cb,
124
+ void *cb_opaque)
125
+{
126
+ BlockCopyCallState *call_state = g_new(BlockCopyCallState, 1);
127
+
128
+ *call_state = (BlockCopyCallState) {
129
+ .s = s,
130
+ .offset = offset,
131
+ .bytes = bytes,
132
+ .cb = cb,
133
+ .cb_opaque = cb_opaque,
134
+
135
+ .co = qemu_coroutine_create(block_copy_async_co_entry, call_state),
136
+ };
137
+
138
+ qemu_coroutine_enter(call_state->co);
139
+
140
+ return call_state;
141
+}
142
+
143
+void block_copy_call_free(BlockCopyCallState *call_state)
144
+{
145
+ if (!call_state) {
146
+ return;
147
+ }
148
+
149
+ assert(call_state->finished);
150
+ g_free(call_state);
151
+}
152
+
153
+bool block_copy_call_finished(BlockCopyCallState *call_state)
154
+{
155
+ return call_state->finished;
156
+}
157
+
158
+bool block_copy_call_succeeded(BlockCopyCallState *call_state)
159
+{
160
+ return call_state->finished && call_state->ret == 0;
161
+}
162
+
163
+bool block_copy_call_failed(BlockCopyCallState *call_state)
164
+{
165
+ return call_state->finished && call_state->ret < 0;
166
+}
167
+
168
+int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read)
169
+{
170
+ assert(call_state->finished);
171
+ if (error_is_read) {
172
+ *error_is_read = call_state->error_is_read;
173
+ }
174
+ return call_state->ret;
175
+}
176
+
177
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
178
{
179
return s->copy_bitmap;
180
--
181
2.29.2
182
183
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
They will be used for backup.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210116214705.822267-5-vsementsov@virtuozzo.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
include/block/block-copy.h | 6 ++++++
11
block/block-copy.c | 11 +++++++++--
12
2 files changed, 15 insertions(+), 2 deletions(-)
13
14
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/block-copy.h
17
+++ b/include/block/block-copy.h
18
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
19
*
20
* Caller is responsible to call block_copy_call_free() to free
21
* BlockCopyCallState object.
22
+ *
23
+ * @max_workers means maximum of parallel coroutines to execute sub-requests,
24
+ * must be > 0.
25
+ *
26
+ * @max_chunk means maximum length for one IO operation. Zero means unlimited.
27
*/
28
BlockCopyCallState *block_copy_async(BlockCopyState *s,
29
int64_t offset, int64_t bytes,
30
+ int max_workers, int64_t max_chunk,
31
BlockCopyAsyncCallbackFunc cb,
32
void *cb_opaque);
33
34
diff --git a/block/block-copy.c b/block/block-copy.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/block/block-copy.c
37
+++ b/block/block-copy.c
38
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
39
BlockCopyState *s;
40
int64_t offset;
41
int64_t bytes;
42
+ int max_workers;
43
+ int64_t max_chunk;
44
BlockCopyAsyncCallbackFunc cb;
45
void *cb_opaque;
46
47
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
48
int64_t offset, int64_t bytes)
49
{
50
BlockCopyTask *task;
51
+ int64_t max_chunk = MIN_NON_ZERO(s->copy_size, call_state->max_chunk);
52
53
if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap,
54
offset, offset + bytes,
55
- s->copy_size, &offset, &bytes))
56
+ max_chunk, &offset, &bytes))
57
{
58
return NULL;
59
}
60
@@ -XXX,XX +XXX,XX @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
61
bytes = end - offset;
62
63
if (!aio && bytes) {
64
- aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS);
65
+ aio = aio_task_pool_new(call_state->max_workers);
66
}
67
68
ret = block_copy_task_run(aio, task);
69
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
70
.s = s,
71
.offset = start,
72
.bytes = bytes,
73
+ .max_workers = BLOCK_COPY_MAX_WORKERS,
74
};
75
76
int ret = block_copy_common(&call_state);
77
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn block_copy_async_co_entry(void *opaque)
78
79
BlockCopyCallState *block_copy_async(BlockCopyState *s,
80
int64_t offset, int64_t bytes,
81
+ int max_workers, int64_t max_chunk,
82
BlockCopyAsyncCallbackFunc cb,
83
void *cb_opaque)
84
{
85
@@ -XXX,XX +XXX,XX @@ BlockCopyCallState *block_copy_async(BlockCopyState *s,
86
.s = s,
87
.offset = offset,
88
.bytes = bytes,
89
+ .max_workers = max_workers,
90
+ .max_chunk = max_chunk,
91
.cb = cb,
92
.cb_opaque = cb_opaque,
93
94
--
95
2.29.2
96
97
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
It simplifies debugging.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210116214705.822267-6-vsementsov@virtuozzo.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
block/block-copy.c | 11 ++++++++++-
11
1 file changed, 10 insertions(+), 1 deletion(-)
12
13
diff --git a/block/block-copy.c b/block/block-copy.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/block-copy.c
16
+++ b/block/block-copy.c
17
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
18
/* Coroutine where async block-copy is running */
19
Coroutine *co;
20
21
+ /* To reference all call states from BlockCopyState */
22
+ QLIST_ENTRY(BlockCopyCallState) list;
23
+
24
/* State */
25
int ret;
26
bool finished;
27
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
28
bool use_copy_range;
29
int64_t copy_size;
30
uint64_t len;
31
- QLIST_HEAD(, BlockCopyTask) tasks;
32
+ QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */
33
+ QLIST_HEAD(, BlockCopyCallState) calls;
34
35
BdrvRequestFlags write_flags;
36
37
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
38
}
39
40
QLIST_INIT(&s->tasks);
41
+ QLIST_INIT(&s->calls);
42
43
return s;
44
}
45
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
46
{
47
int ret;
48
49
+ QLIST_INSERT_HEAD(&call_state->s->calls, call_state, list);
50
+
51
do {
52
ret = block_copy_dirty_clusters(call_state);
53
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
55
call_state->cb(call_state->cb_opaque);
56
}
57
58
+ QLIST_REMOVE(call_state, list);
59
+
60
return ret;
61
}
62
63
--
64
2.29.2
65
66
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
We are going to directly use one async block-copy operation for backup
4
job, so we need rate limiter.
5
6
We want to maintain current backup behavior: only background copying is
7
limited and copy-before-write operations only participate in limit
8
calculation. Therefore we need one rate limiter for block-copy state
9
and boolean flag for block-copy call state for actual limitation.
10
11
Note, that we can't just calculate each chunk in limiter after
12
successful copying: it will not save us from starting a lot of async
13
sub-requests which will exceed limit too much. Instead let's use the
14
following scheme on sub-request creation:
15
1. If at the moment limit is not exceeded, create the request and
16
account it immediately.
17
2. If at the moment limit is already exceeded, drop create sub-request
18
and handle limit instead (by sleep).
19
With this approach we'll never exceed the limit more than by one
20
sub-request (which pretty much matches current backup behavior).
21
22
Note also, that if there is in-flight block-copy async call,
23
block_copy_kick() should be used after set-speed to apply new setup
24
faster. For that block_copy_kick() published in this patch.
25
26
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
27
Reviewed-by: Max Reitz <mreitz@redhat.com>
28
Message-Id: <20210116214705.822267-7-vsementsov@virtuozzo.com>
29
Signed-off-by: Max Reitz <mreitz@redhat.com>
30
---
31
include/block/block-copy.h | 5 ++++-
32
block/backup-top.c | 2 +-
33
block/backup.c | 2 +-
34
block/block-copy.c | 46 +++++++++++++++++++++++++++++++++++++-
35
4 files changed, 51 insertions(+), 4 deletions(-)
36
37
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
38
index XXXXXXX..XXXXXXX 100644
39
--- a/include/block/block-copy.h
40
+++ b/include/block/block-copy.h
41
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
42
int64_t offset, int64_t *count);
43
44
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
45
- bool *error_is_read);
46
+ bool ignore_ratelimit, bool *error_is_read);
47
48
/*
49
* Run block-copy in a coroutine, create corresponding BlockCopyCallState
50
@@ -XXX,XX +XXX,XX @@ bool block_copy_call_succeeded(BlockCopyCallState *call_state);
51
bool block_copy_call_failed(BlockCopyCallState *call_state);
52
int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read);
53
54
+void block_copy_set_speed(BlockCopyState *s, uint64_t speed);
55
+void block_copy_kick(BlockCopyCallState *call_state);
56
+
57
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s);
58
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip);
59
60
diff --git a/block/backup-top.c b/block/backup-top.c
61
index XXXXXXX..XXXXXXX 100644
62
--- a/block/backup-top.c
63
+++ b/block/backup-top.c
64
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
65
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
66
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
67
68
- return block_copy(s->bcs, off, end - off, NULL);
69
+ return block_copy(s->bcs, off, end - off, true, NULL);
70
}
71
72
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
73
diff --git a/block/backup.c b/block/backup.c
74
index XXXXXXX..XXXXXXX 100644
75
--- a/block/backup.c
76
+++ b/block/backup.c
77
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
78
79
trace_backup_do_cow_enter(job, start, offset, bytes);
80
81
- ret = block_copy(job->bcs, start, end - start, error_is_read);
82
+ ret = block_copy(job->bcs, start, end - start, true, error_is_read);
83
84
trace_backup_do_cow_return(job, offset, bytes, ret);
85
86
diff --git a/block/block-copy.c b/block/block-copy.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/block-copy.c
89
+++ b/block/block-copy.c
90
@@ -XXX,XX +XXX,XX @@
91
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
92
#define BLOCK_COPY_MAX_MEM (128 * MiB)
93
#define BLOCK_COPY_MAX_WORKERS 64
94
+#define BLOCK_COPY_SLICE_TIME 100000000ULL /* ns */
95
96
static coroutine_fn int block_copy_task_entry(AioTask *task);
97
98
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
99
int64_t bytes;
100
int max_workers;
101
int64_t max_chunk;
102
+ bool ignore_ratelimit;
103
BlockCopyAsyncCallbackFunc cb;
104
void *cb_opaque;
105
106
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
107
/* State */
108
int ret;
109
bool finished;
110
+ QemuCoSleepState *sleep_state;
111
112
/* OUT parameters */
113
bool error_is_read;
114
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
115
void *progress_opaque;
116
117
SharedResource *mem;
118
+
119
+ uint64_t speed;
120
+ RateLimit rate_limit;
121
} BlockCopyState;
122
123
static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
124
@@ -XXX,XX +XXX,XX @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
125
}
126
task->zeroes = ret & BDRV_BLOCK_ZERO;
127
128
+ if (s->speed) {
129
+ if (!call_state->ignore_ratelimit) {
130
+ uint64_t ns = ratelimit_calculate_delay(&s->rate_limit, 0);
131
+ if (ns > 0) {
132
+ block_copy_task_end(task, -EAGAIN);
133
+ g_free(task);
134
+ qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, ns,
135
+ &call_state->sleep_state);
136
+ continue;
137
+ }
138
+ }
139
+
140
+ ratelimit_calculate_delay(&s->rate_limit, task->bytes);
141
+ }
142
+
143
trace_block_copy_process(s, task->offset);
144
145
co_get_from_shres(s->mem, task->bytes);
146
@@ -XXX,XX +XXX,XX @@ out:
147
return ret < 0 ? ret : found_dirty;
148
}
149
150
+void block_copy_kick(BlockCopyCallState *call_state)
151
+{
152
+ if (call_state->sleep_state) {
153
+ qemu_co_sleep_wake(call_state->sleep_state);
154
+ }
155
+}
156
+
157
/*
158
* block_copy_common
159
*
160
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
161
}
162
163
int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
164
- bool *error_is_read)
165
+ bool ignore_ratelimit, bool *error_is_read)
166
{
167
BlockCopyCallState call_state = {
168
.s = s,
169
.offset = start,
170
.bytes = bytes,
171
+ .ignore_ratelimit = ignore_ratelimit,
172
.max_workers = BLOCK_COPY_MAX_WORKERS,
173
};
174
175
@@ -XXX,XX +XXX,XX @@ void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
176
{
177
s->skip_unallocated = skip;
178
}
179
+
180
+void block_copy_set_speed(BlockCopyState *s, uint64_t speed)
181
+{
182
+ s->speed = speed;
183
+ if (speed > 0) {
184
+ ratelimit_set_speed(&s->rate_limit, speed, BLOCK_COPY_SLICE_TIME);
185
+ }
186
+
187
+ /*
188
+ * Note: it's good to kick all call states from here, but it should be done
189
+ * only from a coroutine, to not crash if s->calls list changed while
190
+ * entering one call. So for now, the only user of this function kicks its
191
+ * only one call_state by hand.
192
+ */
193
+}
194
--
195
2.29.2
196
197
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Add function to cancel running async block-copy call. It will be used
4
in backup.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20210116214705.822267-8-vsementsov@virtuozzo.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
include/block/block-copy.h | 13 +++++++++++++
12
block/block-copy.c | 24 +++++++++++++++++++-----
13
2 files changed, 32 insertions(+), 5 deletions(-)
14
15
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/block-copy.h
18
+++ b/include/block/block-copy.h
19
@@ -XXX,XX +XXX,XX @@ void block_copy_call_free(BlockCopyCallState *call_state);
20
bool block_copy_call_finished(BlockCopyCallState *call_state);
21
bool block_copy_call_succeeded(BlockCopyCallState *call_state);
22
bool block_copy_call_failed(BlockCopyCallState *call_state);
23
+bool block_copy_call_cancelled(BlockCopyCallState *call_state);
24
int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read);
25
26
void block_copy_set_speed(BlockCopyState *s, uint64_t speed);
27
void block_copy_kick(BlockCopyCallState *call_state);
28
29
+/*
30
+ * Cancel running block-copy call.
31
+ *
32
+ * Cancel leaves block-copy state valid: dirty bits are correct and you may use
33
+ * cancel + <run block_copy with same parameters> to emulate pause/resume.
34
+ *
35
+ * Note also, that the cancel is async: it only marks block-copy call to be
36
+ * cancelled. So, the call may be cancelled (block_copy_call_cancelled() reports
37
+ * true) but not yet finished (block_copy_call_finished() reports false).
38
+ */
39
+void block_copy_call_cancel(BlockCopyCallState *call_state);
40
+
41
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s);
42
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip);
43
44
diff --git a/block/block-copy.c b/block/block-copy.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block/block-copy.c
47
+++ b/block/block-copy.c
48
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
49
int ret;
50
bool finished;
51
QemuCoSleepState *sleep_state;
52
+ bool cancelled;
53
54
/* OUT parameters */
55
bool error_is_read;
56
@@ -XXX,XX +XXX,XX @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
57
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
58
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
59
60
- while (bytes && aio_task_pool_status(aio) == 0) {
61
+ while (bytes && aio_task_pool_status(aio) == 0 && !call_state->cancelled) {
62
BlockCopyTask *task;
63
int64_t status_bytes;
64
65
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
66
do {
67
ret = block_copy_dirty_clusters(call_state);
68
69
- if (ret == 0) {
70
+ if (ret == 0 && !call_state->cancelled) {
71
ret = block_copy_wait_one(call_state->s, call_state->offset,
72
call_state->bytes);
73
}
74
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
75
* 2. We have waited for some intersecting block-copy request
76
* It may have failed and produced new dirty bits.
77
*/
78
- } while (ret > 0);
79
+ } while (ret > 0 && !call_state->cancelled);
80
81
call_state->finished = true;
82
83
@@ -XXX,XX +XXX,XX @@ bool block_copy_call_finished(BlockCopyCallState *call_state)
84
85
bool block_copy_call_succeeded(BlockCopyCallState *call_state)
86
{
87
- return call_state->finished && call_state->ret == 0;
88
+ return call_state->finished && !call_state->cancelled &&
89
+ call_state->ret == 0;
90
}
91
92
bool block_copy_call_failed(BlockCopyCallState *call_state)
93
{
94
- return call_state->finished && call_state->ret < 0;
95
+ return call_state->finished && !call_state->cancelled &&
96
+ call_state->ret < 0;
97
+}
98
+
99
+bool block_copy_call_cancelled(BlockCopyCallState *call_state)
100
+{
101
+ return call_state->cancelled;
102
}
103
104
int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read)
105
@@ -XXX,XX +XXX,XX @@ int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read)
106
return call_state->ret;
107
}
108
109
+void block_copy_call_cancel(BlockCopyCallState *call_state)
110
+{
111
+ call_state->cancelled = true;
112
+ block_copy_kick(call_state);
113
+}
114
+
115
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
116
{
117
return s->copy_bitmap;
118
--
119
2.29.2
120
121
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
We are going to use async block-copy call in backup, so we'll need to
4
passthrough setting backup speed to block-copy call.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20210116214705.822267-9-vsementsov@virtuozzo.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
include/block/blockjob_int.h | 2 ++
12
blockjob.c | 6 ++++++
13
2 files changed, 8 insertions(+)
14
15
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/blockjob_int.h
18
+++ b/include/block/blockjob_int.h
19
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
20
* besides job->blk to the new AioContext.
21
*/
22
void (*attached_aio_context)(BlockJob *job, AioContext *new_context);
23
+
24
+ void (*set_speed)(BlockJob *job, int64_t speed);
25
};
26
27
/**
28
diff --git a/blockjob.c b/blockjob.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/blockjob.c
31
+++ b/blockjob.c
32
@@ -XXX,XX +XXX,XX @@ static bool job_timer_pending(Job *job)
33
34
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
35
{
36
+ const BlockJobDriver *drv = block_job_driver(job);
37
int64_t old_speed = job->speed;
38
39
if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
40
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
41
ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME);
42
43
job->speed = speed;
44
+
45
+ if (drv->set_speed) {
46
+ drv->set_speed(job, speed);
47
+ }
48
+
49
if (speed && speed <= old_speed) {
50
return;
51
}
52
--
53
2.29.2
54
55
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
If main job coroutine called job_yield (while some background process
4
is in progress), we should give it a chance to call job_pause_point().
5
It will be used in backup, when moved on async block-copy.
6
7
Note, that job_user_pause is not enough: we want to handle
8
child_job_drained_begin() as well, which call job_pause().
9
10
Still, if job is already in job_do_yield() in job_pause_point() we
11
should not enter it.
12
13
iotest 109 output is modified: on stop we do bdrv_drain_all() which now
14
triggers job pause immediately (and pause after ready is standby).
15
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Message-Id: <20210116214705.822267-10-vsementsov@virtuozzo.com>
18
Reviewed-by: Max Reitz <mreitz@redhat.com>
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
---
21
job.c | 3 +++
22
tests/qemu-iotests/109.out | 24 ++++++++++++++++++++++++
23
2 files changed, 27 insertions(+)
24
25
diff --git a/job.c b/job.c
26
index XXXXXXX..XXXXXXX 100644
27
--- a/job.c
28
+++ b/job.c
29
@@ -XXX,XX +XXX,XX @@ static bool job_timer_not_pending(Job *job)
30
void job_pause(Job *job)
31
{
32
job->pause_count++;
33
+ if (!job->paused) {
34
+ job_enter(job);
35
+ }
36
}
37
38
void job_resume(Job *job)
39
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
40
index XXXXXXX..XXXXXXX 100644
41
--- a/tests/qemu-iotests/109.out
42
+++ b/tests/qemu-iotests/109.out
43
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
44
{"execute":"quit"}
45
{"return": {}}
46
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
47
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
48
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
49
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
50
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
51
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
52
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
53
{"execute":"quit"}
54
{"return": {}}
55
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
56
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
57
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
58
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
59
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
60
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
61
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
62
{"execute":"quit"}
63
{"return": {}}
64
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
65
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
66
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
67
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
68
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
69
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
70
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
71
{"execute":"quit"}
72
{"return": {}}
73
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
74
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
75
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
76
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
77
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
78
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
79
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
80
{"execute":"quit"}
81
{"return": {}}
82
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
83
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
84
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
85
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
86
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
87
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
88
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
89
{"execute":"quit"}
90
{"return": {}}
91
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
92
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
93
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
94
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
95
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
96
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
97
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
98
{"execute":"quit"}
99
{"return": {}}
100
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
101
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
102
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
103
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
104
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
105
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
106
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
107
{"execute":"quit"}
108
{"return": {}}
109
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
110
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
111
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
112
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
113
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
114
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
115
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
116
{"execute":"quit"}
117
{"return": {}}
118
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
119
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
120
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
121
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
122
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
123
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
124
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
125
{"execute":"quit"}
126
{"return": {}}
127
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
128
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
129
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
130
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
131
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
132
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
133
@@ -XXX,XX +XXX,XX @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
134
{"execute":"quit"}
135
{"return": {}}
136
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
137
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
138
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
139
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
140
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
141
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
142
@@ -XXX,XX +XXX,XX @@ Images are identical.
143
{"execute":"quit"}
144
{"return": {}}
145
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
146
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
147
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
148
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
149
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
150
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
151
--
152
2.29.2
153
154
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Add new parameters to configure future backup features. The patch
4
doesn't introduce aio backup requests (so we actually have only one
5
worker) neither requests larger than one cluster. Still, formally we
6
satisfy these maximums anyway, so add the parameters now, to facilitate
7
further patch which will really change backup job behavior.
8
9
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Message-Id: <20210116214705.822267-11-vsementsov@virtuozzo.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
14
qapi/block-core.json | 13 ++++++++++++-
15
block/backup.c | 28 +++++++++++++++++++++++-----
16
block/replication.c | 2 +-
17
blockdev.c | 8 +++++++-
18
4 files changed, 43 insertions(+), 8 deletions(-)
19
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
22
--- a/qapi/block-core.json
23
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
25
#
26
# @use-copy-range: Use copy offloading. Default true.
27
#
28
+# @max-workers: Maximum number of parallel requests for the sustained background
29
+# copying process. Doesn't influence copy-before-write operations.
30
+# Default 64.
31
+#
32
+# @max-chunk: Maximum request length for the sustained background copying
33
+# process. Doesn't influence copy-before-write operations.
34
+# 0 means unlimited. If max-chunk is non-zero then it should not be
35
+# less than job cluster size which is calculated as maximum of
36
+# target image cluster size and 64k. Default 0.
37
+#
38
# Since: 6.0
39
##
40
{ 'struct': 'BackupPerf',
41
- 'data': { '*use-copy-range': 'bool' }}
42
+ 'data': { '*use-copy-range': 'bool',
43
+ '*max-workers': 'int', '*max-chunk': 'int64' } }
44
45
##
46
# @BackupCommon:
47
diff --git a/block/backup.c b/block/backup.c
48
index XXXXXXX..XXXXXXX 100644
49
--- a/block/backup.c
50
+++ b/block/backup.c
51
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
52
return NULL;
53
}
54
55
+ cluster_size = backup_calculate_cluster_size(target, errp);
56
+ if (cluster_size < 0) {
57
+ goto error;
58
+ }
59
+
60
+ if (perf->max_workers < 1) {
61
+ error_setg(errp, "max-workers must be greater than zero");
62
+ return NULL;
63
+ }
64
+
65
+ if (perf->max_chunk < 0) {
66
+ error_setg(errp, "max-chunk must be zero (which means no limit) or "
67
+ "positive");
68
+ return NULL;
69
+ }
70
+
71
+ if (perf->max_chunk && perf->max_chunk < cluster_size) {
72
+ error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
73
+ "cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
74
+ return NULL;
75
+ }
76
+
77
+
78
if (sync_bitmap) {
79
/* If we need to write to this bitmap, check that we can: */
80
if (bitmap_mode != BITMAP_SYNC_MODE_NEVER &&
81
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
82
goto error;
83
}
84
85
- cluster_size = backup_calculate_cluster_size(target, errp);
86
- if (cluster_size < 0) {
87
- goto error;
88
- }
89
-
90
/*
91
* If source is in backing chain of target assume that target is going to be
92
* used for "image fleecing", i.e. it should represent a kind of snapshot of
93
diff --git a/block/replication.c b/block/replication.c
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/replication.c
96
+++ b/block/replication.c
97
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
98
int64_t active_length, hidden_length, disk_length;
99
AioContext *aio_context;
100
Error *local_err = NULL;
101
- BackupPerf perf = { .use_copy_range = true };
102
+ BackupPerf perf = { .use_copy_range = true, .max_workers = 1 };
103
104
aio_context = bdrv_get_aio_context(bs);
105
aio_context_acquire(aio_context);
106
diff --git a/blockdev.c b/blockdev.c
107
index XXXXXXX..XXXXXXX 100644
108
--- a/blockdev.c
109
+++ b/blockdev.c
110
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
111
{
112
BlockJob *job = NULL;
113
BdrvDirtyBitmap *bmap = NULL;
114
- BackupPerf perf = { .use_copy_range = true };
115
+ BackupPerf perf = { .use_copy_range = true, .max_workers = 64 };
116
int job_flags = JOB_DEFAULT;
117
118
if (!backup->has_speed) {
119
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
120
if (backup->x_perf->has_use_copy_range) {
121
perf.use_copy_range = backup->x_perf->use_copy_range;
122
}
123
+ if (backup->x_perf->has_max_workers) {
124
+ perf.max_workers = backup->x_perf->max_workers;
125
+ }
126
+ if (backup->x_perf->has_max_chunk) {
127
+ perf.max_chunk = backup->x_perf->max_chunk;
128
+ }
129
}
130
131
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
132
--
133
2.29.2
134
135
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
After introducing parallel async copy requests instead of plain
4
cluster-by-cluster copying loop, we'll have to wait for paused status,
5
as we need to wait for several parallel request. So, let's gently wait
6
instead of just asserting that job already paused.
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Message-Id: <20210116214705.822267-12-vsementsov@virtuozzo.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
tests/qemu-iotests/056 | 9 +++++++--
14
1 file changed, 7 insertions(+), 2 deletions(-)
15
16
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
17
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/qemu-iotests/056
19
+++ b/tests/qemu-iotests/056
20
@@ -XXX,XX +XXX,XX @@ class BackupTest(iotests.QMPTestCase):
21
event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
22
match={'data': {'device': 'drive0'}})
23
self.assertNotEqual(event, None)
24
- # OK, job should be wedged
25
- res = self.vm.qmp('query-block-jobs')
26
+ # OK, job should pause, but it can't do it immediately, as it can't
27
+ # cancel other parallel requests (which didn't fail)
28
+ with iotests.Timeout(60, "Timeout waiting for backup actually paused"):
29
+ while True:
30
+ res = self.vm.qmp('query-block-jobs')
31
+ if res['return'][0]['status'] == 'paused':
32
+ break
33
self.assert_qmp(res, 'return[0]/status', 'paused')
34
res = self.vm.qmp('block-job-dismiss', id='drive0')
35
self.assert_qmp(res, 'error/desc',
36
--
37
2.29.2
38
39
diff view generated by jsdifflib
Deleted patch
1
Right now, this does not change anything, because backup ignores
2
max-chunk and max-workers. However, as soon as backup is switched over
3
to block-copy for the background copying process, we will need it to
4
keep 129 passing.
5
1
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210120102043.28346-1-mreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
---
10
tests/qemu-iotests/129 | 7 ++++++-
11
1 file changed, 6 insertions(+), 1 deletion(-)
12
13
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
14
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/129
16
+++ b/tests/qemu-iotests/129
17
@@ -XXX,XX +XXX,XX @@ class TestStopWithBlockJob(iotests.QMPTestCase):
18
sync="full", buf_size=65536)
19
20
def test_drive_backup(self):
21
+ # Limit max-chunk and max-workers so that block-copy will not
22
+ # launch so many workers working on so much data each that
23
+ # stop's bdrv_drain_all() would finish the job
24
self.do_test_stop("drive-backup", device="drive0",
25
target=self.target_img, format=iotests.imgfmt,
26
- sync="full")
27
+ sync="full",
28
+ x_perf={ 'max-chunk': 65536,
29
+ 'max-workers': 8 })
30
31
def test_block_commit(self):
32
# Add overlay above the source node so that we actually use a
33
--
34
2.29.2
35
36
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
The further change of moving backup to be a one block-copy call will
4
make copying chunk-size and cluster-size two separate things. So, even
5
with 64k cluster sized qcow2 image, default chunk would be 1M.
6
185 test however assumes, that with speed limited to 64K, one iteration
7
would result in offset=64K. It will change, as first iteration would
8
result in offset=1M independently of speed.
9
10
So, let's explicitly specify, what test wants: set max-chunk to 64K, so
11
that one iteration is 64K. Note, that we don't need to limit
12
max-workers, as block-copy rate limiter will handle the situation and
13
wouldn't start new workers when speed limit is obviously reached.
14
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
16
Reviewed-by: Max Reitz <mreitz@redhat.com>
17
Message-Id: <20210116214705.822267-13-vsementsov@virtuozzo.com>
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
---
20
tests/qemu-iotests/185 | 3 ++-
21
tests/qemu-iotests/185.out | 3 ++-
22
2 files changed, 4 insertions(+), 2 deletions(-)
23
24
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
25
index XXXXXXX..XXXXXXX 100755
26
--- a/tests/qemu-iotests/185
27
+++ b/tests/qemu-iotests/185
28
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
29
'target': '$TEST_IMG.copy',
30
'format': '$IMGFMT',
31
'sync': 'full',
32
- 'speed': 65536 } }" \
33
+ 'speed': 65536,
34
+ 'x-perf': {'max-chunk': 65536} } }" \
35
"return"
36
37
# If we don't sleep here 'quit' command races with disk I/O
38
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
39
index XXXXXXX..XXXXXXX 100644
40
--- a/tests/qemu-iotests/185.out
41
+++ b/tests/qemu-iotests/185.out
42
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
43
'target': 'TEST_DIR/t.IMGFMT.copy',
44
'format': 'IMGFMT',
45
'sync': 'full',
46
- 'speed': 65536 } }
47
+ 'speed': 65536,
48
+ 'x-perf': { 'max-chunk': 65536 } } }
49
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
50
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
51
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
52
--
53
2.29.2
54
55
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
The further change of moving backup to be a one block-copy call will
4
make copying chunk-size and cluster-size two separate things. So, even
5
with 64k cluster sized qcow2 image, default chunk would be 1M.
6
Test 219 depends on specified chunk-size. Update it for explicit
7
chunk-size for backup as for mirror.
8
9
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Message-Id: <20210116214705.822267-14-vsementsov@virtuozzo.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
14
tests/qemu-iotests/219 | 13 +++++++------
15
1 file changed, 7 insertions(+), 6 deletions(-)
16
17
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/219
20
+++ b/tests/qemu-iotests/219
21
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('disk.img') as disk_path, \
22
# but related to this also automatic state transitions like job
23
# completion), but still get pause points often enough to avoid making this
24
# test very slow, it's important to have the right ratio between speed and
25
- # buf_size.
26
+ # copy-chunk-size.
27
#
28
- # For backup, buf_size is hard-coded to the source image cluster size (64k),
29
- # so we'll pick the same for mirror. The slice time, i.e. the granularity
30
- # of the rate limiting is 100ms. With a speed of 256k per second, we can
31
- # get four pause points per second. This gives us 250ms per iteration,
32
- # which should be enough to stay deterministic.
33
+ # Chose 64k copy-chunk-size both for mirror (by buf_size) and backup (by
34
+ # x-max-chunk). The slice time, i.e. the granularity of the rate limiting
35
+ # is 100ms. With a speed of 256k per second, we can get four pause points
36
+ # per second. This gives us 250ms per iteration, which should be enough to
37
+ # stay deterministic.
38
39
test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
40
'device': 'drive0-node',
41
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('disk.img') as disk_path, \
42
'target': copy_path,
43
'sync': 'full',
44
'speed': 262144,
45
+ 'x-perf': {'max-chunk': 65536},
46
'auto-finalize': auto_finalize,
47
'auto-dismiss': auto_dismiss,
48
})
49
--
50
2.29.2
51
52
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Iotest 257 dumps a lot of in-progress information of backup job, such
4
as offset and bitmap dirtiness. Further commit will move backup to be
5
one block-copy call, which will introduce async parallel requests
6
instead of plain cluster-by-cluster copying. To keep things
7
deterministic, allow only one worker (only one copy request at a time)
8
for this test.
9
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Message-Id: <20210116214705.822267-15-vsementsov@virtuozzo.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
tests/qemu-iotests/257 | 1 +
16
tests/qemu-iotests/257.out | 306 ++++++++++++++++++-------------------
17
2 files changed, 154 insertions(+), 153 deletions(-)
18
19
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
20
index XXXXXXX..XXXXXXX 100755
21
--- a/tests/qemu-iotests/257
22
+++ b/tests/qemu-iotests/257
23
@@ -XXX,XX +XXX,XX @@ def blockdev_backup(vm, device, target, sync, **kwargs):
24
target=target,
25
sync=sync,
26
filter_node_name='backup-top',
27
+ x_perf={'max-workers': 1},
28
**kwargs)
29
return result
30
31
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
32
index XXXXXXX..XXXXXXX 100644
33
--- a/tests/qemu-iotests/257.out
34
+++ b/tests/qemu-iotests/257.out
35
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
36
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
37
{"return": {}}
38
{}
39
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
40
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
41
{"return": {}}
42
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
43
44
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
45
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
46
{"return": {}}
47
{}
48
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
49
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
50
{"return": {}}
51
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
52
53
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
54
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
55
{"return": {}}
56
{}
57
-{"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"}}
58
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
59
{"return": {}}
60
61
--- Write #2 ---
62
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
63
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
64
{"return": {}}
65
{}
66
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
67
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
68
{"return": {}}
69
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
70
71
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
72
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
73
{"return": {}}
74
{}
75
-{"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"}}
76
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
77
{"return": {}}
78
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
79
{"return": {}}
80
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
81
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
82
{"return": {}}
83
{}
84
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
85
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
86
{"return": {}}
87
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
88
89
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
90
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
91
{"return": {}}
92
{}
93
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
94
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
95
{"return": {}}
96
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
97
98
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
99
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
100
{"return": {}}
101
{}
102
-{"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"}}
103
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
104
{"return": {}}
105
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
106
{"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"}}
107
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
108
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
109
{"return": {}}
110
{}
111
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
112
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
113
{"return": {}}
114
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
115
116
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
117
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
118
{"return": {}}
119
{}
120
-{"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"}}
121
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
122
{"return": {}}
123
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
124
{"return": {}}
125
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
126
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
127
{"return": {}}
128
{}
129
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
130
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
131
{"return": {}}
132
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
133
134
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
135
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
136
{"return": {}}
137
{}
138
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
139
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
140
{"return": {}}
141
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
142
143
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
144
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
145
{"return": {}}
146
{}
147
-{"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"}}
148
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
149
{"return": {}}
150
151
--- Write #2 ---
152
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
153
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
154
{"return": {}}
155
{}
156
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
157
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
158
{"return": {}}
159
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
160
161
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
162
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
163
{"return": {}}
164
{}
165
-{"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"}}
166
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
167
{"return": {}}
168
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
169
{"return": {}}
170
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
171
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
172
{"return": {}}
173
{}
174
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
175
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
176
{"return": {}}
177
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
178
179
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
180
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
181
{"return": {}}
182
{}
183
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
184
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
185
{"return": {}}
186
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
187
188
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
189
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
190
{"return": {}}
191
{}
192
-{"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"}}
193
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
194
{"return": {}}
195
196
--- Write #2 ---
197
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
198
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
199
{"return": {}}
200
{}
201
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
202
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
203
{"return": {}}
204
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
205
206
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
207
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
208
{"return": {}}
209
{}
210
-{"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"}}
211
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
212
{"return": {}}
213
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
214
{"return": {}}
215
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
216
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
217
{"return": {}}
218
{}
219
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
220
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
221
{"return": {}}
222
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
223
224
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
225
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
226
{"return": {}}
227
{}
228
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
229
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
230
{"return": {}}
231
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
232
233
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
234
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
235
{"return": {}}
236
{}
237
-{"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"}}
238
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
239
{"return": {}}
240
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
241
{"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"}}
242
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
243
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
244
{"return": {}}
245
{}
246
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
247
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
248
{"return": {}}
249
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
250
251
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
252
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
253
{"return": {}}
254
{}
255
-{"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"}}
256
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
257
{"return": {}}
258
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
259
{"return": {}}
260
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
261
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
262
{"return": {}}
263
{}
264
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
265
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
266
{"return": {}}
267
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
268
269
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
270
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
271
{"return": {}}
272
{}
273
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
274
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
275
{"return": {}}
276
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
277
278
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
279
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
280
{"return": {}}
281
{}
282
-{"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"}}
283
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
284
{"return": {}}
285
286
--- Write #2 ---
287
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
288
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
289
{"return": {}}
290
{}
291
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
292
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
293
{"return": {}}
294
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
295
296
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
297
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
298
{"return": {}}
299
{}
300
-{"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"}}
301
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
302
{"return": {}}
303
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
304
{"return": {}}
305
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
306
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
307
{"return": {}}
308
{}
309
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
310
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
311
{"return": {}}
312
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
313
314
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
315
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
316
{"return": {}}
317
{}
318
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
319
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
320
{"return": {}}
321
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
322
323
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
324
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
325
{"return": {}}
326
{}
327
-{"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"}}
328
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
329
{"return": {}}
330
331
--- Write #2 ---
332
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
333
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
334
{"return": {}}
335
{}
336
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
337
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
338
{"return": {}}
339
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
340
341
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
342
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
343
{"return": {}}
344
{}
345
-{"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"}}
346
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
347
{"return": {}}
348
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
349
{"return": {}}
350
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
351
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
352
{"return": {}}
353
{}
354
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
355
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
356
{"return": {}}
357
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
358
359
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
360
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
361
{"return": {}}
362
{}
363
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
364
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
365
{"return": {}}
366
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
367
368
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
369
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
370
{"return": {}}
371
{}
372
-{"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"}}
373
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
374
{"return": {}}
375
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
376
{"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"}}
377
@@ -XXX,XX +XXX,XX @@ expecting 13 dirty sectors; have 13. OK!
378
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
379
{"return": {}}
380
{}
381
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
382
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
383
{"return": {}}
384
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
385
386
@@ -XXX,XX +XXX,XX @@ expecting 13 dirty sectors; have 13. OK!
387
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
388
{"return": {}}
389
{}
390
-{"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"}}
391
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
392
{"return": {}}
393
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
394
{"return": {}}
395
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
396
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
397
{"return": {}}
398
{}
399
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
400
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
401
{"return": {}}
402
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
403
404
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
405
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
406
{"return": {}}
407
{}
408
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
409
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
410
{"return": {}}
411
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
412
413
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
414
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
415
{"return": {}}
416
{}
417
-{"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"}}
418
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
419
{"return": {}}
420
421
--- Write #2 ---
422
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
423
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
424
{"return": {}}
425
{}
426
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
427
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
428
{"return": {}}
429
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
430
431
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
432
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
433
{"return": {}}
434
{}
435
-{"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"}}
436
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
437
{"return": {}}
438
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
439
{"return": {}}
440
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
441
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
442
{"return": {}}
443
{}
444
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
445
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
446
{"return": {}}
447
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
448
449
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
450
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
451
{"return": {}}
452
{}
453
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
454
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
455
{"return": {}}
456
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
457
458
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
459
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
460
{"return": {}}
461
{}
462
-{"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"}}
463
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
464
{"return": {}}
465
466
--- Write #2 ---
467
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
468
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
469
{"return": {}}
470
{}
471
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
472
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
473
{"return": {}}
474
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
475
476
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
477
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
478
{"return": {}}
479
{}
480
-{"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"}}
481
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
482
{"return": {}}
483
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
484
{"return": {}}
485
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
486
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
487
{"return": {}}
488
{}
489
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
490
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
491
{"return": {}}
492
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
493
494
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
495
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
496
{"return": {}}
497
{}
498
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
499
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
500
{"return": {}}
501
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
502
503
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
504
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
505
{"return": {}}
506
{}
507
-{"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"}}
508
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
509
{"return": {}}
510
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
511
{"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"}}
512
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
513
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
514
{"return": {}}
515
{}
516
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
517
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
518
{"return": {}}
519
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
520
521
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
522
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
523
{"return": {}}
524
{}
525
-{"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"}}
526
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
527
{"return": {}}
528
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
529
{"return": {}}
530
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
531
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
532
{"return": {}}
533
{}
534
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
535
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
536
{"return": {}}
537
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
538
539
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
540
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
541
{"return": {}}
542
{}
543
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
544
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
545
{"return": {}}
546
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
547
548
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
549
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
550
{"return": {}}
551
{}
552
-{"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"}}
553
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
554
{"return": {}}
555
556
--- Write #2 ---
557
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
558
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
559
{"return": {}}
560
{}
561
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
562
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
563
{"return": {}}
564
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
565
566
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
567
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
568
{"return": {}}
569
{}
570
-{"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"}}
571
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
572
{"return": {}}
573
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
574
{"return": {}}
575
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
576
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
577
{"return": {}}
578
{}
579
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
580
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
581
{"return": {}}
582
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
583
584
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
585
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
586
{"return": {}}
587
{}
588
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
589
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
590
{"return": {}}
591
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
592
593
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
594
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
595
{"return": {}}
596
{}
597
-{"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"}}
598
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
599
{"return": {}}
600
601
--- Write #2 ---
602
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
603
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
604
{"return": {}}
605
{}
606
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
607
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
608
{"return": {}}
609
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
610
611
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
612
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
613
{"return": {}}
614
{}
615
-{"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"}}
616
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
617
{"return": {}}
618
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
619
{"return": {}}
620
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
621
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
622
{"return": {}}
623
{}
624
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
625
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
626
{"return": {}}
627
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
628
629
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
630
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
631
{"return": {}}
632
{}
633
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
634
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
635
{"return": {}}
636
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
637
638
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
639
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
640
{"return": {}}
641
{}
642
-{"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"}}
643
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
644
{"return": {}}
645
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
646
{"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"}}
647
@@ -XXX,XX +XXX,XX @@ expecting 1014 dirty sectors; have 1014. OK!
648
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
649
{"return": {}}
650
{}
651
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
652
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
653
{"return": {}}
654
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
655
656
@@ -XXX,XX +XXX,XX @@ expecting 1014 dirty sectors; have 1014. OK!
657
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
658
{"return": {}}
659
{}
660
-{"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"}}
661
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
662
{"return": {}}
663
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
664
{"return": {}}
665
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
666
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
667
{"return": {}}
668
{}
669
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
670
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
671
{"return": {}}
672
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
673
674
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
675
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
676
{"return": {}}
677
{}
678
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
679
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
680
{"return": {}}
681
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
682
683
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
684
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
685
{"return": {}}
686
{}
687
-{"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"}}
688
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
689
{"return": {}}
690
691
--- Write #2 ---
692
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
693
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
694
{"return": {}}
695
{}
696
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
697
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
698
{"return": {}}
699
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
700
701
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
702
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
703
{"return": {}}
704
{}
705
-{"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"}}
706
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
707
{"return": {}}
708
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
709
{"return": {}}
710
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
711
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
712
{"return": {}}
713
{}
714
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
715
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
716
{"return": {}}
717
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
718
719
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
720
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
721
{"return": {}}
722
{}
723
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
724
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
725
{"return": {}}
726
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
727
728
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
729
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
730
{"return": {}}
731
{}
732
-{"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"}}
733
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
734
{"return": {}}
735
736
--- Write #2 ---
737
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
738
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
739
{"return": {}}
740
{}
741
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
742
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
743
{"return": {}}
744
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
745
746
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
747
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
748
{"return": {}}
749
{}
750
-{"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"}}
751
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
752
{"return": {}}
753
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
754
{"return": {}}
755
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
756
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
757
{"return": {}}
758
{}
759
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
760
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
761
{"return": {}}
762
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
763
764
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
765
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
766
{"return": {}}
767
{}
768
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
769
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
770
{"return": {}}
771
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
772
773
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
774
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
775
{"return": {}}
776
{}
777
-{"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"}}
778
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
779
{"return": {}}
780
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
781
{"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"}}
782
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
783
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
784
{"return": {}}
785
{}
786
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
787
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
788
{"return": {}}
789
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
790
791
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
792
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
793
{"return": {}}
794
{}
795
-{"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"}}
796
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
797
{"return": {}}
798
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
799
{"return": {}}
800
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
801
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
802
{"return": {}}
803
{}
804
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
805
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
806
{"return": {}}
807
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
808
809
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
810
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
811
{"return": {}}
812
{}
813
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
814
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
815
{"return": {}}
816
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
817
818
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
819
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
820
{"return": {}}
821
{}
822
-{"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"}}
823
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
824
{"return": {}}
825
826
--- Write #2 ---
827
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
828
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
829
{"return": {}}
830
{}
831
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
832
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
833
{"return": {}}
834
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
835
836
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
837
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
838
{"return": {}}
839
{}
840
-{"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"}}
841
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
842
{"return": {}}
843
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
844
{"return": {}}
845
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
846
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
847
{"return": {}}
848
{}
849
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
850
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
851
{"return": {}}
852
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
853
854
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
855
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
856
{"return": {}}
857
{}
858
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
859
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
860
{"return": {}}
861
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
862
863
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
864
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
865
{"return": {}}
866
{}
867
-{"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"}}
868
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
869
{"return": {}}
870
871
--- Write #2 ---
872
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
873
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
874
{"return": {}}
875
{}
876
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
877
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
878
{"return": {}}
879
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
880
881
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
882
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
883
{"return": {}}
884
{}
885
-{"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"}}
886
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
887
{"return": {}}
888
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
889
{"return": {}}
890
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
891
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
892
{"return": {}}
893
{}
894
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
895
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
896
{"return": {}}
897
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
898
899
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
900
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
901
{"return": {}}
902
{}
903
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
904
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
905
{"return": {}}
906
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
907
908
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
909
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
910
{"return": {}}
911
{}
912
-{"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"}}
913
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
914
{"return": {}}
915
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
916
{"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"}}
917
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
918
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
919
{"return": {}}
920
{}
921
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
922
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
923
{"return": {}}
924
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
925
926
@@ -XXX,XX +XXX,XX @@ expecting 14 dirty sectors; have 14. OK!
927
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
928
{"return": {}}
929
{}
930
-{"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"}}
931
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
932
{"return": {}}
933
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
934
{"return": {}}
935
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
936
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
937
{"return": {}}
938
{}
939
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
940
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0", "x-perf": {"max-workers": 1}}}
941
{"return": {}}
942
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
943
944
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
945
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
946
{"return": {}}
947
{}
948
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
949
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1", "x-perf": {"max-workers": 1}}}
950
{"return": {}}
951
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
952
953
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
954
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
955
{"return": {}}
956
{}
957
-{"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"}}
958
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1", "x-perf": {"max-workers": 1}}}
959
{"return": {}}
960
961
--- Write #2 ---
962
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
963
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
964
{"return": {}}
965
{}
966
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
967
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2", "x-perf": {"max-workers": 1}}}
968
{"return": {}}
969
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
970
971
@@ -XXX,XX +XXX,XX @@ expecting 12 dirty sectors; have 12. OK!
972
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
973
{"return": {}}
974
{}
975
-{"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"}}
976
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2", "x-perf": {"max-workers": 1}}}
977
{"return": {}}
978
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
979
{"return": {}}
980
@@ -XXX,XX +XXX,XX @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK!
981
982
-- Sync mode incremental tests --
983
984
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
985
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
986
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
987
988
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
989
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
990
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
991
992
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
993
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
994
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
995
996
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
997
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
998
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
999
1000
-{"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"}}
1001
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1002
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1003
1004
-{"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"}}
1005
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1006
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
1007
1008
-{"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"}}
1009
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1010
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
1011
1012
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
1013
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1014
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1015
1016
-{"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"}}
1017
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1018
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
1019
1020
-{"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"}}
1021
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1022
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
1023
1024
-- Sync mode bitmap tests --
1025
1026
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
1027
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1028
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
1029
1030
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
1031
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1032
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
1033
1034
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
1035
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1036
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
1037
1038
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
1039
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1040
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
1041
1042
-{"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"}}
1043
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1044
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1045
1046
-{"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"}}
1047
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1048
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1049
1050
-{"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"}}
1051
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1052
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1053
1054
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
1055
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1056
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1057
1058
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
1059
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1060
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
1061
1062
-- Sync mode full tests --
1063
1064
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
1065
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1066
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1067
1068
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
1069
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1070
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1071
1072
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
1073
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1074
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1075
1076
-{"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"}}
1077
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1078
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1079
1080
-{"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"}}
1081
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1082
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1083
1084
-{"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"}}
1085
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1086
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1087
1088
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
1089
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1090
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1091
1092
-{"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"}}
1093
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1094
{"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'full'"}}
1095
1096
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
1097
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1098
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
1099
1100
-- Sync mode top tests --
1101
1102
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
1103
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1104
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1105
1106
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
1107
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1108
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1109
1110
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
1111
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1112
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1113
1114
-{"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"}}
1115
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1116
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1117
1118
-{"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"}}
1119
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1120
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1121
1122
-{"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"}}
1123
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1124
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1125
1126
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
1127
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1128
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1129
1130
-{"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"}}
1131
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1132
{"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'top'"}}
1133
1134
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
1135
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1136
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
1137
1138
-- Sync mode none tests --
1139
1140
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
1141
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1142
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1143
1144
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
1145
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1146
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1147
1148
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
1149
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1150
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
1151
1152
-{"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"}}
1153
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1154
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1155
1156
-{"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"}}
1157
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1158
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1159
1160
-{"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"}}
1161
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1162
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1163
1164
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
1165
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1166
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
1167
1168
-{"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"}}
1169
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1170
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
1171
1172
-{"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"}}
1173
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1174
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
1175
1176
-{"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"}}
1177
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1178
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
1179
1180
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
1181
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target", "x-perf": {"max-workers": 1}}}
1182
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
1183
1184
--
1185
2.29.2
1186
1187
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
We are going to stop use of this callback in the following commit.
4
Still the callback handling code will be dropped in a separate commit.
5
So, for now let's make it optional.
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20210116214705.822267-16-vsementsov@virtuozzo.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
block/block-copy.c | 4 +++-
13
1 file changed, 3 insertions(+), 1 deletion(-)
14
15
diff --git a/block/block-copy.c b/block/block-copy.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/block-copy.c
18
+++ b/block/block-copy.c
19
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
20
t->call_state->error_is_read = error_is_read;
21
} else {
22
progress_work_done(t->s->progress, t->bytes);
23
- t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
24
+ if (t->s->progress_bytes_callback) {
25
+ t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
26
+ }
27
}
28
co_put_to_shres(t->s->mem, t->bytes);
29
block_copy_task_end(t, ret);
30
--
31
2.29.2
32
33
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Message-Id: <20210116214705.822267-17-vsementsov@virtuozzo.com>
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
---
8
block/backup.c | 12 +++++-------
9
1 file changed, 5 insertions(+), 7 deletions(-)
10
11
diff --git a/block/backup.c b/block/backup.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/backup.c
14
+++ b/block/backup.c
15
@@ -XXX,XX +XXX,XX @@ static void backup_init_bcs_bitmap(BackupBlockJob *job)
16
static int coroutine_fn backup_run(Job *job, Error **errp)
17
{
18
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
19
- int ret = 0;
20
+ int ret;
21
22
backup_init_bcs_bitmap(s);
23
24
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *job, Error **errp)
25
26
for (offset = 0; offset < s->len; ) {
27
if (yield_and_check(s)) {
28
- ret = -ECANCELED;
29
- goto out;
30
+ return -ECANCELED;
31
}
32
33
ret = block_copy_reset_unallocated(s->bcs, offset, &count);
34
if (ret < 0) {
35
- goto out;
36
+ return ret;
37
}
38
39
offset += count;
40
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *job, Error **errp)
41
job_yield(job);
42
}
43
} else {
44
- ret = backup_loop(s);
45
+ return backup_loop(s);
46
}
47
48
- out:
49
- return ret;
50
+ return 0;
51
}
52
53
static const BlockJobDriver backup_job_driver = {
54
--
55
2.29.2
56
57
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
This brings async request handling and block-status driven chunk sizes
4
to backup out of the box, which improves backup performance.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20210116214705.822267-18-vsementsov@virtuozzo.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
block/backup.c | 187 +++++++++++++++++++++++++++++++------------------
12
1 file changed, 120 insertions(+), 67 deletions(-)
13
14
diff --git a/block/backup.c b/block/backup.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/backup.c
17
+++ b/block/backup.c
18
@@ -XXX,XX +XXX,XX @@
19
#include "block/block-copy.h"
20
#include "qapi/error.h"
21
#include "qapi/qmp/qerror.h"
22
-#include "qemu/ratelimit.h"
23
#include "qemu/cutils.h"
24
#include "sysemu/block-backend.h"
25
#include "qemu/bitmap.h"
26
@@ -XXX,XX +XXX,XX @@ typedef struct BackupBlockJob {
27
BlockdevOnError on_source_error;
28
BlockdevOnError on_target_error;
29
uint64_t len;
30
- uint64_t bytes_read;
31
int64_t cluster_size;
32
BackupPerf perf;
33
34
BlockCopyState *bcs;
35
+
36
+ bool wait;
37
+ BlockCopyCallState *bg_bcs_call;
38
} BackupBlockJob;
39
40
static const BlockJobDriver backup_job_driver;
41
42
-static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
43
-{
44
- BackupBlockJob *s = opaque;
45
-
46
- s->bytes_read += bytes;
47
-}
48
-
49
-static int coroutine_fn backup_do_cow(BackupBlockJob *job,
50
- int64_t offset, uint64_t bytes,
51
- bool *error_is_read)
52
-{
53
- int ret = 0;
54
- int64_t start, end; /* bytes */
55
-
56
- start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
57
- end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size);
58
-
59
- trace_backup_do_cow_enter(job, start, offset, bytes);
60
-
61
- ret = block_copy(job->bcs, start, end - start, true, error_is_read);
62
-
63
- trace_backup_do_cow_return(job, offset, bytes, ret);
64
-
65
- return ret;
66
-}
67
-
68
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
69
{
70
BdrvDirtyBitmap *bm;
71
@@ -XXX,XX +XXX,XX @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
72
}
73
}
74
75
-static bool coroutine_fn yield_and_check(BackupBlockJob *job)
76
+static void coroutine_fn backup_block_copy_callback(void *opaque)
77
{
78
- uint64_t delay_ns;
79
-
80
- if (job_is_cancelled(&job->common.job)) {
81
- return true;
82
- }
83
-
84
- /*
85
- * We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
86
- * return. Without a yield, the VM would not reboot.
87
- */
88
- delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
89
- job->bytes_read = 0;
90
- job_sleep_ns(&job->common.job, delay_ns);
91
+ BackupBlockJob *s = opaque;
92
93
- if (job_is_cancelled(&job->common.job)) {
94
- return true;
95
+ if (s->wait) {
96
+ s->wait = false;
97
+ aio_co_wake(s->common.job.co);
98
+ } else {
99
+ job_enter(&s->common.job);
100
}
101
-
102
- return false;
103
}
104
105
static int coroutine_fn backup_loop(BackupBlockJob *job)
106
{
107
- bool error_is_read;
108
- int64_t offset;
109
- BdrvDirtyBitmapIter *bdbi;
110
+ BlockCopyCallState *s = NULL;
111
int ret = 0;
112
+ bool error_is_read;
113
+ BlockErrorAction act;
114
+
115
+ while (true) { /* retry loop */
116
+ job->bg_bcs_call = s = block_copy_async(job->bcs, 0,
117
+ QEMU_ALIGN_UP(job->len, job->cluster_size),
118
+ job->perf.max_workers, job->perf.max_chunk,
119
+ backup_block_copy_callback, job);
120
+
121
+ while (!block_copy_call_finished(s) &&
122
+ !job_is_cancelled(&job->common.job))
123
+ {
124
+ job_yield(&job->common.job);
125
+ }
126
127
- bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs));
128
- while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) {
129
- do {
130
- if (yield_and_check(job)) {
131
- goto out;
132
- }
133
- ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read);
134
- if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
135
- BLOCK_ERROR_ACTION_REPORT)
136
- {
137
- goto out;
138
- }
139
- } while (ret < 0);
140
+ if (!block_copy_call_finished(s)) {
141
+ assert(job_is_cancelled(&job->common.job));
142
+ /*
143
+ * Note that we can't use job_yield() here, as it doesn't work for
144
+ * cancelled job.
145
+ */
146
+ block_copy_call_cancel(s);
147
+ job->wait = true;
148
+ qemu_coroutine_yield();
149
+ assert(block_copy_call_finished(s));
150
+ ret = 0;
151
+ goto out;
152
+ }
153
+
154
+ if (job_is_cancelled(&job->common.job) ||
155
+ block_copy_call_succeeded(s))
156
+ {
157
+ ret = 0;
158
+ goto out;
159
+ }
160
+
161
+ if (block_copy_call_cancelled(s)) {
162
+ /*
163
+ * Job is not cancelled but only block-copy call. This is possible
164
+ * after job pause. Now the pause is finished, start new block-copy
165
+ * iteration.
166
+ */
167
+ block_copy_call_free(s);
168
+ continue;
169
+ }
170
+
171
+ /* The only remaining case is failed block-copy call. */
172
+ assert(block_copy_call_failed(s));
173
+
174
+ ret = block_copy_call_status(s, &error_is_read);
175
+ act = backup_error_action(job, error_is_read, -ret);
176
+ switch (act) {
177
+ case BLOCK_ERROR_ACTION_REPORT:
178
+ goto out;
179
+ case BLOCK_ERROR_ACTION_STOP:
180
+ /*
181
+ * Go to pause prior to starting new block-copy call on the next
182
+ * iteration.
183
+ */
184
+ job_pause_point(&job->common.job);
185
+ break;
186
+ case BLOCK_ERROR_ACTION_IGNORE:
187
+ /* Proceed to new block-copy call to retry. */
188
+ break;
189
+ default:
190
+ abort();
191
+ }
192
+
193
+ block_copy_call_free(s);
194
}
195
196
- out:
197
- bdrv_dirty_iter_free(bdbi);
198
+out:
199
+ block_copy_call_free(s);
200
+ job->bg_bcs_call = NULL;
201
return ret;
202
}
203
204
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *job, Error **errp)
205
int64_t count;
206
207
for (offset = 0; offset < s->len; ) {
208
- if (yield_and_check(s)) {
209
+ if (job_is_cancelled(job)) {
210
+ return -ECANCELED;
211
+ }
212
+
213
+ job_pause_point(job);
214
+
215
+ if (job_is_cancelled(job)) {
216
return -ECANCELED;
217
}
218
219
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *job, Error **errp)
220
return 0;
221
}
222
223
+static void coroutine_fn backup_pause(Job *job)
224
+{
225
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
226
+
227
+ if (s->bg_bcs_call && !block_copy_call_finished(s->bg_bcs_call)) {
228
+ block_copy_call_cancel(s->bg_bcs_call);
229
+ s->wait = true;
230
+ qemu_coroutine_yield();
231
+ }
232
+}
233
+
234
+static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
235
+{
236
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
237
+
238
+ /*
239
+ * block_job_set_speed() is called first from block_job_create(), when we
240
+ * don't yet have s->bcs.
241
+ */
242
+ if (s->bcs) {
243
+ block_copy_set_speed(s->bcs, speed);
244
+ if (s->bg_bcs_call) {
245
+ block_copy_kick(s->bg_bcs_call);
246
+ }
247
+ }
248
+}
249
+
250
static const BlockJobDriver backup_job_driver = {
251
.job_driver = {
252
.instance_size = sizeof(BackupBlockJob),
253
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
254
.commit = backup_commit,
255
.abort = backup_abort,
256
.clean = backup_clean,
257
- }
258
+ .pause = backup_pause,
259
+ },
260
+ .set_speed = backup_set_speed,
261
};
262
263
static int64_t backup_calculate_cluster_size(BlockDriverState *target,
264
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
265
job->len = len;
266
job->perf = *perf;
267
268
- block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job);
269
block_copy_set_progress_meter(bcs, &job->common.job.progress);
270
+ block_copy_set_speed(bcs, speed);
271
272
/* Required permissions are already taken by backup-top target */
273
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
274
--
275
2.29.2
276
277
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Further commit will add a benchmark
4
(scripts/simplebench/bench-backup.py), which will show that backup
5
works better with async parallel requests (previous commit) and
6
disabled copy_range. So, let's disable copy_range by default.
7
8
Note: the option was added several commits ago with default to true,
9
to follow old behavior (the feature was enabled unconditionally), and
10
only now we are going to change the default behavior.
11
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Message-Id: <20210116214705.822267-19-vsementsov@virtuozzo.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
17
qapi/block-core.json | 2 +-
18
blockdev.c | 2 +-
19
2 files changed, 2 insertions(+), 2 deletions(-)
20
21
diff --git a/qapi/block-core.json b/qapi/block-core.json
22
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/block-core.json
24
+++ b/qapi/block-core.json
25
@@ -XXX,XX +XXX,XX @@
26
# Optional parameters for backup. These parameters don't affect
27
# functionality, but may significantly affect performance.
28
#
29
-# @use-copy-range: Use copy offloading. Default true.
30
+# @use-copy-range: Use copy offloading. Default false.
31
#
32
# @max-workers: Maximum number of parallel requests for the sustained background
33
# copying process. Doesn't influence copy-before-write operations.
34
diff --git a/blockdev.c b/blockdev.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/blockdev.c
37
+++ b/blockdev.c
38
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
39
{
40
BlockJob *job = NULL;
41
BdrvDirtyBitmap *bmap = NULL;
42
- BackupPerf perf = { .use_copy_range = true, .max_workers = 64 };
43
+ BackupPerf perf = { .max_workers = 64 };
44
int job_flags = JOB_DEFAULT;
45
46
if (!backup->has_speed) {
47
--
48
2.29.2
49
50
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Drop unused code.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210116214705.822267-20-vsementsov@virtuozzo.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
include/block/block-copy.h | 6 ------
11
block/block-copy.c | 15 ---------------
12
2 files changed, 21 deletions(-)
13
14
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/block-copy.h
17
+++ b/include/block/block-copy.h
18
@@ -XXX,XX +XXX,XX @@
19
#include "block/block.h"
20
#include "qemu/co-shared-resource.h"
21
22
-typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
23
typedef void (*BlockCopyAsyncCallbackFunc)(void *opaque);
24
typedef struct BlockCopyState BlockCopyState;
25
typedef struct BlockCopyCallState BlockCopyCallState;
26
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
27
BdrvRequestFlags write_flags,
28
Error **errp);
29
30
-void block_copy_set_progress_callback(
31
- BlockCopyState *s,
32
- ProgressBytesCallbackFunc progress_bytes_callback,
33
- void *progress_opaque);
34
-
35
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm);
36
37
void block_copy_state_free(BlockCopyState *s);
38
diff --git a/block/block-copy.c b/block/block-copy.c
39
index XXXXXXX..XXXXXXX 100644
40
--- a/block/block-copy.c
41
+++ b/block/block-copy.c
42
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
43
bool skip_unallocated;
44
45
ProgressMeter *progress;
46
- /* progress_bytes_callback: called when some copying progress is done. */
47
- ProgressBytesCallbackFunc progress_bytes_callback;
48
- void *progress_opaque;
49
50
SharedResource *mem;
51
52
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
53
return s;
54
}
55
56
-void block_copy_set_progress_callback(
57
- BlockCopyState *s,
58
- ProgressBytesCallbackFunc progress_bytes_callback,
59
- void *progress_opaque)
60
-{
61
- s->progress_bytes_callback = progress_bytes_callback;
62
- s->progress_opaque = progress_opaque;
63
-}
64
-
65
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
66
{
67
s->progress = pm;
68
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
69
t->call_state->error_is_read = error_is_read;
70
} else {
71
progress_work_done(t->s->progress, t->bytes);
72
- if (t->s->progress_bytes_callback) {
73
- t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
74
- }
75
}
76
co_put_to_shres(t->s->mem, t->bytes);
77
block_copy_task_end(t, ret);
78
--
79
2.29.2
80
81
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Pino Toscano <ptoscano@redhat.com>
2
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
3
Rewrite the implementation of the ssh block driver to use libssh instead
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
4
of libssh2. The libssh library has various advantages over libssh2:
5
Message-Id: <20210116214705.822267-21-vsementsov@virtuozzo.com>
5
- easier API for authentication (for example for using ssh-agent)
6
- easier API for known_hosts handling
7
- supports newer types of keys in known_hosts
8
9
Use APIs/features available in libssh 0.8 conditionally, to support
10
older versions (which are not recommended though).
11
12
Adjust the iotest 207 according to the different error message, and to
13
find the default key type for localhost (to properly compare the
14
fingerprint with).
15
Contributed-by: Max Reitz <mreitz@redhat.com>
16
17
Adjust the various Docker/Travis scripts to use libssh when available
18
instead of libssh2. The mingw/mxe testing is dropped for now, as there
19
are no packages for it.
20
21
Signed-off-by: Pino Toscano <ptoscano@redhat.com>
22
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
23
Acked-by: Alex Bennée <alex.bennee@linaro.org>
24
Message-id: 20190620200840.17655-1-ptoscano@redhat.com
25
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
26
Message-id: 5873173.t2JhDm7DL7@lindworm.usersys.redhat.com
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
---
28
---
8
include/block/block-copy.h | 2 +-
29
configure | 65 +-
9
block/backup-top.c | 2 +-
30
block/Makefile.objs | 6 +-
10
block/block-copy.c | 10 ++--------
31
block/ssh.c | 652 ++++++++++--------
11
3 files changed, 4 insertions(+), 10 deletions(-)
32
.travis.yml | 4 +-
33
block/trace-events | 14 +-
34
docs/qemu-block-drivers.texi | 2 +-
35
.../dockerfiles/debian-win32-cross.docker | 1 -
36
.../dockerfiles/debian-win64-cross.docker | 1 -
37
tests/docker/dockerfiles/fedora.docker | 4 +-
38
tests/docker/dockerfiles/ubuntu.docker | 2 +-
39
tests/docker/dockerfiles/ubuntu1804.docker | 2 +-
40
tests/qemu-iotests/207 | 54 +-
41
tests/qemu-iotests/207.out | 2 +-
42
13 files changed, 449 insertions(+), 360 deletions(-)
12
43
13
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
44
diff --git a/configure b/configure
45
index XXXXXXX..XXXXXXX 100755
46
--- a/configure
47
+++ b/configure
48
@@ -XXX,XX +XXX,XX @@ auth_pam=""
49
vte=""
50
virglrenderer=""
51
tpm=""
52
-libssh2=""
53
+libssh=""
54
live_block_migration="yes"
55
numa=""
56
tcmalloc="no"
57
@@ -XXX,XX +XXX,XX @@ for opt do
58
;;
59
--enable-tpm) tpm="yes"
60
;;
61
- --disable-libssh2) libssh2="no"
62
+ --disable-libssh) libssh="no"
63
;;
64
- --enable-libssh2) libssh2="yes"
65
+ --enable-libssh) libssh="yes"
66
;;
67
--disable-live-block-migration) live_block_migration="no"
68
;;
69
@@ -XXX,XX +XXX,XX @@ disabled with --disable-FEATURE, default is enabled if available:
70
coroutine-pool coroutine freelist (better performance)
71
glusterfs GlusterFS backend
72
tpm TPM support
73
- libssh2 ssh block device support
74
+ libssh ssh block device support
75
numa libnuma support
76
libxml2 for Parallels image format
77
tcmalloc tcmalloc support
78
@@ -XXX,XX +XXX,XX @@ EOF
79
fi
80
81
##########################################
82
-# libssh2 probe
83
-min_libssh2_version=1.2.8
84
-if test "$libssh2" != "no" ; then
85
- if $pkg_config --atleast-version=$min_libssh2_version libssh2; then
86
- libssh2_cflags=$($pkg_config libssh2 --cflags)
87
- libssh2_libs=$($pkg_config libssh2 --libs)
88
- libssh2=yes
89
+# libssh probe
90
+if test "$libssh" != "no" ; then
91
+ if $pkg_config --exists libssh; then
92
+ libssh_cflags=$($pkg_config libssh --cflags)
93
+ libssh_libs=$($pkg_config libssh --libs)
94
+ libssh=yes
95
else
96
- if test "$libssh2" = "yes" ; then
97
- error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
98
+ if test "$libssh" = "yes" ; then
99
+ error_exit "libssh required for --enable-libssh"
100
fi
101
- libssh2=no
102
+ libssh=no
103
fi
104
fi
105
106
##########################################
107
-# libssh2_sftp_fsync probe
108
+# Check for libssh 0.8
109
+# This is done like this instead of using the LIBSSH_VERSION_* and
110
+# SSH_VERSION_* macros because some distributions in the past shipped
111
+# snapshots of the future 0.8 from Git, and those snapshots did not
112
+# have updated version numbers (still referring to 0.7.0).
113
114
-if test "$libssh2" = "yes"; then
115
+if test "$libssh" = "yes"; then
116
cat > $TMPC <<EOF
117
-#include <stdio.h>
118
-#include <libssh2.h>
119
-#include <libssh2_sftp.h>
120
-int main(void) {
121
- LIBSSH2_SESSION *session;
122
- LIBSSH2_SFTP *sftp;
123
- LIBSSH2_SFTP_HANDLE *sftp_handle;
124
- session = libssh2_session_init ();
125
- sftp = libssh2_sftp_init (session);
126
- sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0);
127
- libssh2_sftp_fsync (sftp_handle);
128
- return 0;
129
-}
130
+#include <libssh/libssh.h>
131
+int main(void) { return ssh_get_server_publickey(NULL, NULL); }
132
EOF
133
- # libssh2_cflags/libssh2_libs defined in previous test.
134
- if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
135
- QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS"
136
+ if compile_prog "$libssh_cflags" "$libssh_libs"; then
137
+ libssh_cflags="-DHAVE_LIBSSH_0_8 $libssh_cflags"
138
fi
139
fi
140
141
@@ -XXX,XX +XXX,XX @@ echo "GlusterFS support $glusterfs"
142
echo "gcov $gcov_tool"
143
echo "gcov enabled $gcov"
144
echo "TPM support $tpm"
145
-echo "libssh2 support $libssh2"
146
+echo "libssh support $libssh"
147
echo "QOM debugging $qom_cast_debug"
148
echo "Live block migration $live_block_migration"
149
echo "lzo support $lzo"
150
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs_iocb_has_stat" = "yes" ; then
151
echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
152
fi
153
154
-if test "$libssh2" = "yes" ; then
155
- echo "CONFIG_LIBSSH2=m" >> $config_host_mak
156
- echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
157
- echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
158
+if test "$libssh" = "yes" ; then
159
+ echo "CONFIG_LIBSSH=m" >> $config_host_mak
160
+ echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak
161
+ echo "LIBSSH_LIBS=$libssh_libs" >> $config_host_mak
162
fi
163
164
if test "$live_block_migration" = "yes" ; then
165
diff --git a/block/Makefile.objs b/block/Makefile.objs
14
index XXXXXXX..XXXXXXX 100644
166
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/block-copy.h
167
--- a/block/Makefile.objs
16
+++ b/include/block/block-copy.h
168
+++ b/block/Makefile.objs
17
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
169
@@ -XXX,XX +XXX,XX @@ block-obj-$(CONFIG_CURL) += curl.o
18
int64_t offset, int64_t *count);
170
block-obj-$(CONFIG_RBD) += rbd.o
19
171
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
20
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
172
block-obj-$(CONFIG_VXHS) += vxhs.o
21
- bool ignore_ratelimit, bool *error_is_read);
173
-block-obj-$(CONFIG_LIBSSH2) += ssh.o
22
+ bool ignore_ratelimit);
174
+block-obj-$(CONFIG_LIBSSH) += ssh.o
175
block-obj-y += accounting.o dirty-bitmap.o
176
block-obj-y += write-threshold.o
177
block-obj-y += backup.o
178
@@ -XXX,XX +XXX,XX @@ rbd.o-libs := $(RBD_LIBS)
179
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
180
gluster.o-libs := $(GLUSTERFS_LIBS)
181
vxhs.o-libs := $(VXHS_LIBS)
182
-ssh.o-cflags := $(LIBSSH2_CFLAGS)
183
-ssh.o-libs := $(LIBSSH2_LIBS)
184
+ssh.o-cflags := $(LIBSSH_CFLAGS)
185
+ssh.o-libs := $(LIBSSH_LIBS)
186
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
187
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
188
dmg-bz2.o-libs := $(BZIP2_LIBS)
189
diff --git a/block/ssh.c b/block/ssh.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/block/ssh.c
192
+++ b/block/ssh.c
193
@@ -XXX,XX +XXX,XX @@
194
195
#include "qemu/osdep.h"
196
197
-#include <libssh2.h>
198
-#include <libssh2_sftp.h>
199
+#include <libssh/libssh.h>
200
+#include <libssh/sftp.h>
201
202
#include "block/block_int.h"
203
#include "block/qdict.h"
204
@@ -XXX,XX +XXX,XX @@
205
#include "trace.h"
23
206
24
/*
207
/*
25
* Run block-copy in a coroutine, create corresponding BlockCopyCallState
208
- * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note
26
diff --git a/block/backup-top.c b/block/backup-top.c
209
- * that this requires that libssh2 was specially compiled with the
27
index XXXXXXX..XXXXXXX 100644
210
- * `./configure --enable-debug' option, so most likely you will have
28
--- a/block/backup-top.c
211
- * to compile it yourself. The meaning of <bitmask> is described
29
+++ b/block/backup-top.c
212
- * here: http://www.libssh2.org/libssh2_trace.html
30
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
213
+ * TRACE_LIBSSH=<level> enables tracing in libssh itself.
31
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
214
+ * The meaning of <level> is described here:
32
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
215
+ * http://api.libssh.org/master/group__libssh__log.html
33
216
*/
34
- return block_copy(s->bcs, off, end - off, true, NULL);
217
-#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
35
+ return block_copy(s->bcs, off, end - off, true);
218
+#define TRACE_LIBSSH 0 /* see: SSH_LOG_* */
219
220
typedef struct BDRVSSHState {
221
/* Coroutine. */
222
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVSSHState {
223
224
/* SSH connection. */
225
int sock; /* socket */
226
- LIBSSH2_SESSION *session; /* ssh session */
227
- LIBSSH2_SFTP *sftp; /* sftp session */
228
- LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */
229
+ ssh_session session; /* ssh session */
230
+ sftp_session sftp; /* sftp session */
231
+ sftp_file sftp_handle; /* sftp remote file handle */
232
233
- /* See ssh_seek() function below. */
234
- int64_t offset;
235
- bool offset_op_read;
236
-
237
- /* File attributes at open. We try to keep the .filesize field
238
+ /*
239
+ * File attributes at open. We try to keep the .size field
240
* updated if it changes (eg by writing at the end of the file).
241
*/
242
- LIBSSH2_SFTP_ATTRIBUTES attrs;
243
+ sftp_attributes attrs;
244
245
InetSocketAddress *inet;
246
247
@@ -XXX,XX +XXX,XX @@ static void ssh_state_init(BDRVSSHState *s)
248
{
249
memset(s, 0, sizeof *s);
250
s->sock = -1;
251
- s->offset = -1;
252
qemu_co_mutex_init(&s->lock);
36
}
253
}
37
254
38
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
255
@@ -XXX,XX +XXX,XX @@ static void ssh_state_free(BDRVSSHState *s)
39
diff --git a/block/block-copy.c b/block/block-copy.c
256
{
40
index XXXXXXX..XXXXXXX 100644
257
g_free(s->user);
41
--- a/block/block-copy.c
258
42
+++ b/block/block-copy.c
259
+ if (s->attrs) {
43
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
260
+ sftp_attributes_free(s->attrs);
261
+ }
262
if (s->sftp_handle) {
263
- libssh2_sftp_close(s->sftp_handle);
264
+ sftp_close(s->sftp_handle);
265
}
266
if (s->sftp) {
267
- libssh2_sftp_shutdown(s->sftp);
268
+ sftp_free(s->sftp);
269
}
270
if (s->session) {
271
- libssh2_session_disconnect(s->session,
272
- "from qemu ssh client: "
273
- "user closed the connection");
274
- libssh2_session_free(s->session);
275
- }
276
- if (s->sock >= 0) {
277
- close(s->sock);
278
+ ssh_disconnect(s->session);
279
+ ssh_free(s->session); /* This frees s->sock */
280
}
44
}
281
}
45
282
46
int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
283
@@ -XXX,XX +XXX,XX @@ session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
47
- bool ignore_ratelimit, bool *error_is_read)
284
va_end(args);
48
+ bool ignore_ratelimit)
285
286
if (s->session) {
287
- char *ssh_err;
288
+ const char *ssh_err;
289
int ssh_err_code;
290
291
- /* This is not an errno. See <libssh2.h>. */
292
- ssh_err_code = libssh2_session_last_error(s->session,
293
- &ssh_err, NULL, 0);
294
- error_setg(errp, "%s: %s (libssh2 error code: %d)",
295
+ /* This is not an errno. See <libssh/libssh.h>. */
296
+ ssh_err = ssh_get_error(s->session);
297
+ ssh_err_code = ssh_get_error_code(s->session);
298
+ error_setg(errp, "%s: %s (libssh error code: %d)",
299
msg, ssh_err, ssh_err_code);
300
} else {
301
error_setg(errp, "%s", msg);
302
@@ -XXX,XX +XXX,XX @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
303
va_end(args);
304
305
if (s->sftp) {
306
- char *ssh_err;
307
+ const char *ssh_err;
308
int ssh_err_code;
309
- unsigned long sftp_err_code;
310
+ int sftp_err_code;
311
312
- /* This is not an errno. See <libssh2.h>. */
313
- ssh_err_code = libssh2_session_last_error(s->session,
314
- &ssh_err, NULL, 0);
315
- /* See <libssh2_sftp.h>. */
316
- sftp_err_code = libssh2_sftp_last_error((s)->sftp);
317
+ /* This is not an errno. See <libssh/libssh.h>. */
318
+ ssh_err = ssh_get_error(s->session);
319
+ ssh_err_code = ssh_get_error_code(s->session);
320
+ /* See <libssh/sftp.h>. */
321
+ sftp_err_code = sftp_get_error(s->sftp);
322
323
error_setg(errp,
324
- "%s: %s (libssh2 error code: %d, sftp error code: %lu)",
325
+ "%s: %s (libssh error code: %d, sftp error code: %d)",
326
msg, ssh_err, ssh_err_code, sftp_err_code);
327
} else {
328
error_setg(errp, "%s", msg);
329
@@ -XXX,XX +XXX,XX @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
330
331
static void sftp_error_trace(BDRVSSHState *s, const char *op)
49
{
332
{
50
BlockCopyCallState call_state = {
333
- char *ssh_err;
51
.s = s,
334
+ const char *ssh_err;
52
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
335
int ssh_err_code;
53
.max_workers = BLOCK_COPY_MAX_WORKERS,
336
- unsigned long sftp_err_code;
54
};
337
+ int sftp_err_code;
55
338
56
- int ret = block_copy_common(&call_state);
339
- /* This is not an errno. See <libssh2.h>. */
340
- ssh_err_code = libssh2_session_last_error(s->session,
341
- &ssh_err, NULL, 0);
342
- /* See <libssh2_sftp.h>. */
343
- sftp_err_code = libssh2_sftp_last_error((s)->sftp);
344
+ /* This is not an errno. See <libssh/libssh.h>. */
345
+ ssh_err = ssh_get_error(s->session);
346
+ ssh_err_code = ssh_get_error_code(s->session);
347
+ /* See <libssh/sftp.h>. */
348
+ sftp_err_code = sftp_get_error(s->sftp);
349
350
trace_sftp_error(op, ssh_err, ssh_err_code, sftp_err_code);
351
}
352
@@ -XXX,XX +XXX,XX @@ static void ssh_parse_filename(const char *filename, QDict *options,
353
parse_uri(filename, options, errp);
354
}
355
356
-static int check_host_key_knownhosts(BDRVSSHState *s,
357
- const char *host, int port, Error **errp)
358
+static int check_host_key_knownhosts(BDRVSSHState *s, Error **errp)
359
{
360
- const char *home;
361
- char *knh_file = NULL;
362
- LIBSSH2_KNOWNHOSTS *knh = NULL;
363
- struct libssh2_knownhost *found;
364
- int ret, r;
365
- const char *hostkey;
366
- size_t len;
367
- int type;
57
-
368
-
58
- if (error_is_read && ret < 0) {
369
- hostkey = libssh2_session_hostkey(s->session, &len, &type);
59
- *error_is_read = call_state.error_is_read;
370
- if (!hostkey) {
371
+ int ret;
372
+#ifdef HAVE_LIBSSH_0_8
373
+ enum ssh_known_hosts_e state;
374
+ int r;
375
+ ssh_key pubkey;
376
+ enum ssh_keytypes_e pubkey_type;
377
+ unsigned char *server_hash = NULL;
378
+ size_t server_hash_len;
379
+ char *fingerprint = NULL;
380
+
381
+ state = ssh_session_is_known_server(s->session);
382
+ trace_ssh_server_status(state);
383
+
384
+ switch (state) {
385
+ case SSH_KNOWN_HOSTS_OK:
386
+ /* OK */
387
+ trace_ssh_check_host_key_knownhosts();
388
+ break;
389
+ case SSH_KNOWN_HOSTS_CHANGED:
390
ret = -EINVAL;
391
- session_error_setg(errp, s, "failed to read remote host key");
392
+ r = ssh_get_server_publickey(s->session, &pubkey);
393
+ if (r == 0) {
394
+ r = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256,
395
+ &server_hash, &server_hash_len);
396
+ pubkey_type = ssh_key_type(pubkey);
397
+ ssh_key_free(pubkey);
398
+ }
399
+ if (r == 0) {
400
+ fingerprint = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256,
401
+ server_hash,
402
+ server_hash_len);
403
+ ssh_clean_pubkey_hash(&server_hash);
404
+ }
405
+ if (fingerprint) {
406
+ error_setg(errp,
407
+ "host key (%s key with fingerprint %s) does not match "
408
+ "the one in known_hosts; this may be a possible attack",
409
+ ssh_key_type_to_char(pubkey_type), fingerprint);
410
+ ssh_string_free_char(fingerprint);
411
+ } else {
412
+ error_setg(errp,
413
+ "host key does not match the one in known_hosts; this "
414
+ "may be a possible attack");
415
+ }
416
goto out;
60
- }
417
- }
61
-
418
-
62
- return ret;
419
- knh = libssh2_knownhost_init(s->session);
63
+ return block_copy_common(&call_state);
420
- if (!knh) {
421
+ case SSH_KNOWN_HOSTS_OTHER:
422
ret = -EINVAL;
423
- session_error_setg(errp, s,
424
- "failed to initialize known hosts support");
425
+ error_setg(errp,
426
+ "host key for this server not found, another type exists");
427
+ goto out;
428
+ case SSH_KNOWN_HOSTS_UNKNOWN:
429
+ ret = -EINVAL;
430
+ error_setg(errp, "no host key was found in known_hosts");
431
+ goto out;
432
+ case SSH_KNOWN_HOSTS_NOT_FOUND:
433
+ ret = -ENOENT;
434
+ error_setg(errp, "known_hosts file not found");
435
+ goto out;
436
+ case SSH_KNOWN_HOSTS_ERROR:
437
+ ret = -EINVAL;
438
+ error_setg(errp, "error while checking the host");
439
+ goto out;
440
+ default:
441
+ ret = -EINVAL;
442
+ error_setg(errp, "error while checking for known server (%d)", state);
443
goto out;
444
}
445
+#else /* !HAVE_LIBSSH_0_8 */
446
+ int state;
447
448
- home = getenv("HOME");
449
- if (home) {
450
- knh_file = g_strdup_printf("%s/.ssh/known_hosts", home);
451
- } else {
452
- knh_file = g_strdup_printf("/root/.ssh/known_hosts");
453
- }
454
-
455
- /* Read all known hosts from OpenSSH-style known_hosts file. */
456
- libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
457
+ state = ssh_is_server_known(s->session);
458
+ trace_ssh_server_status(state);
459
460
- r = libssh2_knownhost_checkp(knh, host, port, hostkey, len,
461
- LIBSSH2_KNOWNHOST_TYPE_PLAIN|
462
- LIBSSH2_KNOWNHOST_KEYENC_RAW,
463
- &found);
464
- switch (r) {
465
- case LIBSSH2_KNOWNHOST_CHECK_MATCH:
466
+ switch (state) {
467
+ case SSH_SERVER_KNOWN_OK:
468
/* OK */
469
- trace_ssh_check_host_key_knownhosts(found->key);
470
+ trace_ssh_check_host_key_knownhosts();
471
break;
472
- case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
473
+ case SSH_SERVER_KNOWN_CHANGED:
474
ret = -EINVAL;
475
- session_error_setg(errp, s,
476
- "host key does not match the one in known_hosts"
477
- " (found key %s)", found->key);
478
+ error_setg(errp,
479
+ "host key does not match the one in known_hosts; this "
480
+ "may be a possible attack");
481
goto out;
482
- case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
483
+ case SSH_SERVER_FOUND_OTHER:
484
ret = -EINVAL;
485
- session_error_setg(errp, s, "no host key was found in known_hosts");
486
+ error_setg(errp,
487
+ "host key for this server not found, another type exists");
488
+ goto out;
489
+ case SSH_SERVER_FILE_NOT_FOUND:
490
+ ret = -ENOENT;
491
+ error_setg(errp, "known_hosts file not found");
492
goto out;
493
- case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
494
+ case SSH_SERVER_NOT_KNOWN:
495
ret = -EINVAL;
496
- session_error_setg(errp, s,
497
- "failure matching the host key with known_hosts");
498
+ error_setg(errp, "no host key was found in known_hosts");
499
+ goto out;
500
+ case SSH_SERVER_ERROR:
501
+ ret = -EINVAL;
502
+ error_setg(errp, "server error");
503
goto out;
504
default:
505
ret = -EINVAL;
506
- session_error_setg(errp, s, "unknown error matching the host key"
507
- " with known_hosts (%d)", r);
508
+ error_setg(errp, "error while checking for known server (%d)", state);
509
goto out;
510
}
511
+#endif /* !HAVE_LIBSSH_0_8 */
512
513
/* known_hosts checking successful. */
514
ret = 0;
515
516
out:
517
- if (knh != NULL) {
518
- libssh2_knownhost_free(knh);
519
- }
520
- g_free(knh_file);
521
return ret;
64
}
522
}
65
523
66
static void coroutine_fn block_copy_async_co_entry(void *opaque)
524
@@ -XXX,XX +XXX,XX @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
525
526
static int
527
check_host_key_hash(BDRVSSHState *s, const char *hash,
528
- int hash_type, size_t fingerprint_len, Error **errp)
529
+ enum ssh_publickey_hash_type type, Error **errp)
530
{
531
- const char *fingerprint;
532
-
533
- fingerprint = libssh2_hostkey_hash(s->session, hash_type);
534
- if (!fingerprint) {
535
+ int r;
536
+ ssh_key pubkey;
537
+ unsigned char *server_hash;
538
+ size_t server_hash_len;
539
+
540
+#ifdef HAVE_LIBSSH_0_8
541
+ r = ssh_get_server_publickey(s->session, &pubkey);
542
+#else
543
+ r = ssh_get_publickey(s->session, &pubkey);
544
+#endif
545
+ if (r != SSH_OK) {
546
session_error_setg(errp, s, "failed to read remote host key");
547
return -EINVAL;
548
}
549
550
- if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
551
- hash) != 0) {
552
+ r = ssh_get_publickey_hash(pubkey, type, &server_hash, &server_hash_len);
553
+ ssh_key_free(pubkey);
554
+ if (r != 0) {
555
+ session_error_setg(errp, s,
556
+ "failed reading the hash of the server SSH key");
557
+ return -EINVAL;
558
+ }
559
+
560
+ r = compare_fingerprint(server_hash, server_hash_len, hash);
561
+ ssh_clean_pubkey_hash(&server_hash);
562
+ if (r != 0) {
563
error_setg(errp, "remote host key does not match host_key_check '%s'",
564
hash);
565
return -EPERM;
566
@@ -XXX,XX +XXX,XX @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
567
return 0;
568
}
569
570
-static int check_host_key(BDRVSSHState *s, const char *host, int port,
571
- SshHostKeyCheck *hkc, Error **errp)
572
+static int check_host_key(BDRVSSHState *s, SshHostKeyCheck *hkc, Error **errp)
573
{
574
SshHostKeyCheckMode mode;
575
576
@@ -XXX,XX +XXX,XX @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
577
case SSH_HOST_KEY_CHECK_MODE_HASH:
578
if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
579
return check_host_key_hash(s, hkc->u.hash.hash,
580
- LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
581
+ SSH_PUBLICKEY_HASH_MD5, errp);
582
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
583
return check_host_key_hash(s, hkc->u.hash.hash,
584
- LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
585
+ SSH_PUBLICKEY_HASH_SHA1, errp);
586
}
587
g_assert_not_reached();
588
break;
589
case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
590
- return check_host_key_knownhosts(s, host, port, errp);
591
+ return check_host_key_knownhosts(s, errp);
592
default:
593
g_assert_not_reached();
594
}
595
@@ -XXX,XX +XXX,XX @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
596
return -EINVAL;
597
}
598
599
-static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
600
+static int authenticate(BDRVSSHState *s, Error **errp)
601
{
602
int r, ret;
603
- const char *userauthlist;
604
- LIBSSH2_AGENT *agent = NULL;
605
- struct libssh2_agent_publickey *identity;
606
- struct libssh2_agent_publickey *prev_identity = NULL;
607
+ int method;
608
609
- userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
610
- if (strstr(userauthlist, "publickey") == NULL) {
611
+ /* Try to authenticate with the "none" method. */
612
+ r = ssh_userauth_none(s->session, NULL);
613
+ if (r == SSH_AUTH_ERROR) {
614
ret = -EPERM;
615
- error_setg(errp,
616
- "remote server does not support \"publickey\" authentication");
617
+ session_error_setg(errp, s, "failed to authenticate using none "
618
+ "authentication");
619
goto out;
620
- }
621
-
622
- /* Connect to ssh-agent and try each identity in turn. */
623
- agent = libssh2_agent_init(s->session);
624
- if (!agent) {
625
- ret = -EINVAL;
626
- session_error_setg(errp, s, "failed to initialize ssh-agent support");
627
- goto out;
628
- }
629
- if (libssh2_agent_connect(agent)) {
630
- ret = -ECONNREFUSED;
631
- session_error_setg(errp, s, "failed to connect to ssh-agent");
632
- goto out;
633
- }
634
- if (libssh2_agent_list_identities(agent)) {
635
- ret = -EINVAL;
636
- session_error_setg(errp, s,
637
- "failed requesting identities from ssh-agent");
638
+ } else if (r == SSH_AUTH_SUCCESS) {
639
+ /* Authenticated! */
640
+ ret = 0;
641
goto out;
642
}
643
644
- for(;;) {
645
- r = libssh2_agent_get_identity(agent, &identity, prev_identity);
646
- if (r == 1) { /* end of list */
647
- break;
648
- }
649
- if (r < 0) {
650
+ method = ssh_userauth_list(s->session, NULL);
651
+ trace_ssh_auth_methods(method);
652
+
653
+ /*
654
+ * Try to authenticate with publickey, using the ssh-agent
655
+ * if available.
656
+ */
657
+ if (method & SSH_AUTH_METHOD_PUBLICKEY) {
658
+ r = ssh_userauth_publickey_auto(s->session, NULL, NULL);
659
+ if (r == SSH_AUTH_ERROR) {
660
ret = -EINVAL;
661
- session_error_setg(errp, s,
662
- "failed to obtain identity from ssh-agent");
663
+ session_error_setg(errp, s, "failed to authenticate using "
664
+ "publickey authentication");
665
goto out;
666
- }
667
- r = libssh2_agent_userauth(agent, user, identity);
668
- if (r == 0) {
669
+ } else if (r == SSH_AUTH_SUCCESS) {
670
/* Authenticated! */
671
ret = 0;
672
goto out;
673
}
674
- /* Failed to authenticate with this identity, try the next one. */
675
- prev_identity = identity;
676
}
677
678
ret = -EPERM;
679
@@ -XXX,XX +XXX,XX @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
680
"and the identities held by your ssh-agent");
681
682
out:
683
- if (agent != NULL) {
684
- /* Note: libssh2 implementation implicitly calls
685
- * libssh2_agent_disconnect if necessary.
686
- */
687
- libssh2_agent_free(agent);
688
- }
689
-
690
return ret;
691
}
692
693
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
694
int ssh_flags, int creat_mode, Error **errp)
695
{
696
int r, ret;
697
- long port = 0;
698
+ unsigned int port = 0;
699
+ int new_sock = -1;
700
701
if (opts->has_user) {
702
s->user = g_strdup(opts->user);
703
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
704
s->inet = opts->server;
705
opts->server = NULL;
706
707
- if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
708
+ if (qemu_strtoui(s->inet->port, NULL, 10, &port) < 0) {
709
error_setg(errp, "Use only numeric port value");
710
ret = -EINVAL;
711
goto err;
712
}
713
714
/* Open the socket and connect. */
715
- s->sock = inet_connect_saddr(s->inet, errp);
716
- if (s->sock < 0) {
717
+ new_sock = inet_connect_saddr(s->inet, errp);
718
+ if (new_sock < 0) {
719
ret = -EIO;
720
goto err;
721
}
722
723
+ /*
724
+ * Try to disable the Nagle algorithm on TCP sockets to reduce latency,
725
+ * but do not fail if it cannot be disabled.
726
+ */
727
+ r = socket_set_nodelay(new_sock);
728
+ if (r < 0) {
729
+ warn_report("can't set TCP_NODELAY for the ssh server %s: %s",
730
+ s->inet->host, strerror(errno));
731
+ }
732
+
733
/* Create SSH session. */
734
- s->session = libssh2_session_init();
735
+ s->session = ssh_new();
736
if (!s->session) {
737
ret = -EINVAL;
738
- session_error_setg(errp, s, "failed to initialize libssh2 session");
739
+ session_error_setg(errp, s, "failed to initialize libssh session");
740
goto err;
741
}
742
743
-#if TRACE_LIBSSH2 != 0
744
- libssh2_trace(s->session, TRACE_LIBSSH2);
745
-#endif
746
+ /*
747
+ * Make sure we are in blocking mode during the connection and
748
+ * authentication phases.
749
+ */
750
+ ssh_set_blocking(s->session, 1);
751
752
- r = libssh2_session_handshake(s->session, s->sock);
753
- if (r != 0) {
754
+ r = ssh_options_set(s->session, SSH_OPTIONS_USER, s->user);
755
+ if (r < 0) {
756
+ ret = -EINVAL;
757
+ session_error_setg(errp, s,
758
+ "failed to set the user in the libssh session");
759
+ goto err;
760
+ }
761
+
762
+ r = ssh_options_set(s->session, SSH_OPTIONS_HOST, s->inet->host);
763
+ if (r < 0) {
764
+ ret = -EINVAL;
765
+ session_error_setg(errp, s,
766
+ "failed to set the host in the libssh session");
767
+ goto err;
768
+ }
769
+
770
+ if (port > 0) {
771
+ r = ssh_options_set(s->session, SSH_OPTIONS_PORT, &port);
772
+ if (r < 0) {
773
+ ret = -EINVAL;
774
+ session_error_setg(errp, s,
775
+ "failed to set the port in the libssh session");
776
+ goto err;
777
+ }
778
+ }
779
+
780
+ r = ssh_options_set(s->session, SSH_OPTIONS_COMPRESSION, "none");
781
+ if (r < 0) {
782
+ ret = -EINVAL;
783
+ session_error_setg(errp, s,
784
+ "failed to disable the compression in the libssh "
785
+ "session");
786
+ goto err;
787
+ }
788
+
789
+ /* Read ~/.ssh/config. */
790
+ r = ssh_options_parse_config(s->session, NULL);
791
+ if (r < 0) {
792
+ ret = -EINVAL;
793
+ session_error_setg(errp, s, "failed to parse ~/.ssh/config");
794
+ goto err;
795
+ }
796
+
797
+ r = ssh_options_set(s->session, SSH_OPTIONS_FD, &new_sock);
798
+ if (r < 0) {
799
+ ret = -EINVAL;
800
+ session_error_setg(errp, s,
801
+ "failed to set the socket in the libssh session");
802
+ goto err;
803
+ }
804
+ /* libssh took ownership of the socket. */
805
+ s->sock = new_sock;
806
+ new_sock = -1;
807
+
808
+ /* Connect. */
809
+ r = ssh_connect(s->session);
810
+ if (r != SSH_OK) {
811
ret = -EINVAL;
812
session_error_setg(errp, s, "failed to establish SSH session");
813
goto err;
814
}
815
816
/* Check the remote host's key against known_hosts. */
817
- ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
818
+ ret = check_host_key(s, opts->host_key_check, errp);
819
if (ret < 0) {
820
goto err;
821
}
822
823
/* Authenticate. */
824
- ret = authenticate(s, s->user, errp);
825
+ ret = authenticate(s, errp);
826
if (ret < 0) {
827
goto err;
828
}
829
830
/* Start SFTP. */
831
- s->sftp = libssh2_sftp_init(s->session);
832
+ s->sftp = sftp_new(s->session);
833
if (!s->sftp) {
834
- session_error_setg(errp, s, "failed to initialize sftp handle");
835
+ session_error_setg(errp, s, "failed to create sftp handle");
836
+ ret = -EINVAL;
837
+ goto err;
838
+ }
839
+
840
+ r = sftp_init(s->sftp);
841
+ if (r < 0) {
842
+ sftp_error_setg(errp, s, "failed to initialize sftp handle");
843
ret = -EINVAL;
844
goto err;
845
}
846
847
/* Open the remote file. */
848
trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode);
849
- s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
850
- creat_mode);
851
+ s->sftp_handle = sftp_open(s->sftp, opts->path, ssh_flags, creat_mode);
852
if (!s->sftp_handle) {
853
- session_error_setg(errp, s, "failed to open remote file '%s'",
854
- opts->path);
855
+ sftp_error_setg(errp, s, "failed to open remote file '%s'",
856
+ opts->path);
857
ret = -EINVAL;
858
goto err;
859
}
860
861
- r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
862
- if (r < 0) {
863
+ /* Make sure the SFTP file is handled in blocking mode. */
864
+ sftp_file_set_blocking(s->sftp_handle);
865
+
866
+ s->attrs = sftp_fstat(s->sftp_handle);
867
+ if (!s->attrs) {
868
sftp_error_setg(errp, s, "failed to read file attributes");
869
return -EINVAL;
870
}
871
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
872
return 0;
873
874
err:
875
+ if (s->attrs) {
876
+ sftp_attributes_free(s->attrs);
877
+ }
878
+ s->attrs = NULL;
879
if (s->sftp_handle) {
880
- libssh2_sftp_close(s->sftp_handle);
881
+ sftp_close(s->sftp_handle);
882
}
883
s->sftp_handle = NULL;
884
if (s->sftp) {
885
- libssh2_sftp_shutdown(s->sftp);
886
+ sftp_free(s->sftp);
887
}
888
s->sftp = NULL;
889
if (s->session) {
890
- libssh2_session_disconnect(s->session,
891
- "from qemu ssh client: "
892
- "error opening connection");
893
- libssh2_session_free(s->session);
894
+ ssh_disconnect(s->session);
895
+ ssh_free(s->session);
896
}
897
s->session = NULL;
898
+ s->sock = -1;
899
+ if (new_sock >= 0) {
900
+ close(new_sock);
901
+ }
902
903
return ret;
904
}
905
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
906
907
ssh_state_init(s);
908
909
- ssh_flags = LIBSSH2_FXF_READ;
910
+ ssh_flags = 0;
911
if (bdrv_flags & BDRV_O_RDWR) {
912
- ssh_flags |= LIBSSH2_FXF_WRITE;
913
+ ssh_flags |= O_RDWR;
914
+ } else {
915
+ ssh_flags |= O_RDONLY;
916
}
917
918
opts = ssh_parse_options(options, errp);
919
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
920
}
921
922
/* Go non-blocking. */
923
- libssh2_session_set_blocking(s->session, 0);
924
+ ssh_set_blocking(s->session, 0);
925
926
qapi_free_BlockdevOptionsSsh(opts);
927
928
return 0;
929
930
err:
931
- if (s->sock >= 0) {
932
- close(s->sock);
933
- }
934
- s->sock = -1;
935
-
936
qapi_free_BlockdevOptionsSsh(opts);
937
938
return ret;
939
@@ -XXX,XX +XXX,XX @@ static int ssh_grow_file(BDRVSSHState *s, int64_t offset, Error **errp)
940
{
941
ssize_t ret;
942
char c[1] = { '\0' };
943
- int was_blocking = libssh2_session_get_blocking(s->session);
944
+ int was_blocking = ssh_is_blocking(s->session);
945
946
/* offset must be strictly greater than the current size so we do
947
* not overwrite anything */
948
- assert(offset > 0 && offset > s->attrs.filesize);
949
+ assert(offset > 0 && offset > s->attrs->size);
950
951
- libssh2_session_set_blocking(s->session, 1);
952
+ ssh_set_blocking(s->session, 1);
953
954
- libssh2_sftp_seek64(s->sftp_handle, offset - 1);
955
- ret = libssh2_sftp_write(s->sftp_handle, c, 1);
956
+ sftp_seek64(s->sftp_handle, offset - 1);
957
+ ret = sftp_write(s->sftp_handle, c, 1);
958
959
- libssh2_session_set_blocking(s->session, was_blocking);
960
+ ssh_set_blocking(s->session, was_blocking);
961
962
if (ret < 0) {
963
sftp_error_setg(errp, s, "Failed to grow file");
964
return -EIO;
965
}
966
967
- s->attrs.filesize = offset;
968
+ s->attrs->size = offset;
969
return 0;
970
}
971
972
@@ -XXX,XX +XXX,XX @@ static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
973
ssh_state_init(&s);
974
975
ret = connect_to_ssh(&s, opts->location,
976
- LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
977
- LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
978
+ O_RDWR | O_CREAT | O_TRUNC,
979
0644, errp);
980
if (ret < 0) {
981
goto fail;
982
@@ -XXX,XX +XXX,XX @@ static int ssh_has_zero_init(BlockDriverState *bs)
983
/* Assume false, unless we can positively prove it's true. */
984
int has_zero_init = 0;
985
986
- if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
987
- if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) {
988
- has_zero_init = 1;
989
- }
990
+ if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) {
991
+ has_zero_init = 1;
992
}
993
994
return has_zero_init;
995
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
996
.co = qemu_coroutine_self()
997
};
998
999
- r = libssh2_session_block_directions(s->session);
1000
+ r = ssh_get_poll_flags(s->session);
1001
1002
- if (r & LIBSSH2_SESSION_BLOCK_INBOUND) {
1003
+ if (r & SSH_READ_PENDING) {
1004
rd_handler = restart_coroutine;
1005
}
1006
- if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
1007
+ if (r & SSH_WRITE_PENDING) {
1008
wr_handler = restart_coroutine;
1009
}
1010
1011
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
1012
trace_ssh_co_yield_back(s->sock);
1013
}
1014
1015
-/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
1016
- * in the remote file. Notice that it just updates a field in the
1017
- * sftp_handle structure, so there is no network traffic and it cannot
1018
- * fail.
1019
- *
1020
- * However, `libssh2_sftp_seek64' does have a catastrophic effect on
1021
- * performance since it causes the handle to throw away all in-flight
1022
- * reads and buffered readahead data. Therefore this function tries
1023
- * to be intelligent about when to call the underlying libssh2 function.
1024
- */
1025
-#define SSH_SEEK_WRITE 0
1026
-#define SSH_SEEK_READ 1
1027
-#define SSH_SEEK_FORCE 2
1028
-
1029
-static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
1030
-{
1031
- bool op_read = (flags & SSH_SEEK_READ) != 0;
1032
- bool force = (flags & SSH_SEEK_FORCE) != 0;
1033
-
1034
- if (force || op_read != s->offset_op_read || offset != s->offset) {
1035
- trace_ssh_seek(offset);
1036
- libssh2_sftp_seek64(s->sftp_handle, offset);
1037
- s->offset = offset;
1038
- s->offset_op_read = op_read;
1039
- }
1040
-}
1041
-
1042
static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
1043
int64_t offset, size_t size,
1044
QEMUIOVector *qiov)
1045
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
1046
1047
trace_ssh_read(offset, size);
1048
1049
- ssh_seek(s, offset, SSH_SEEK_READ);
1050
+ trace_ssh_seek(offset);
1051
+ sftp_seek64(s->sftp_handle, offset);
1052
1053
/* This keeps track of the current iovec element ('i'), where we
1054
* will write to next ('buf'), and the end of the current iovec
1055
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
1056
buf = i->iov_base;
1057
end_of_vec = i->iov_base + i->iov_len;
1058
1059
- /* libssh2 has a hard-coded limit of 2000 bytes per request,
1060
- * although it will also do readahead behind our backs. Therefore
1061
- * we may have to do repeated reads here until we have read 'size'
1062
- * bytes.
1063
- */
1064
for (got = 0; got < size; ) {
1065
+ size_t request_read_size;
1066
again:
1067
- trace_ssh_read_buf(buf, end_of_vec - buf);
1068
- r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf);
1069
- trace_ssh_read_return(r);
1070
+ /*
1071
+ * The size of SFTP packets is limited to 32K bytes, so limit
1072
+ * the amount of data requested to 16K, as libssh currently
1073
+ * does not handle multiple requests on its own.
1074
+ */
1075
+ request_read_size = MIN(end_of_vec - buf, 16384);
1076
+ trace_ssh_read_buf(buf, end_of_vec - buf, request_read_size);
1077
+ r = sftp_read(s->sftp_handle, buf, request_read_size);
1078
+ trace_ssh_read_return(r, sftp_get_error(s->sftp));
1079
1080
- if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1081
+ if (r == SSH_AGAIN) {
1082
co_yield(s, bs);
1083
goto again;
1084
}
1085
- if (r < 0) {
1086
- sftp_error_trace(s, "read");
1087
- s->offset = -1;
1088
- return -EIO;
1089
- }
1090
- if (r == 0) {
1091
+ if (r == SSH_EOF || (r == 0 && sftp_get_error(s->sftp) == SSH_FX_EOF)) {
1092
/* EOF: Short read so pad the buffer with zeroes and return it. */
1093
qemu_iovec_memset(qiov, got, 0, size - got);
1094
return 0;
1095
}
1096
+ if (r <= 0) {
1097
+ sftp_error_trace(s, "read");
1098
+ return -EIO;
1099
+ }
1100
1101
got += r;
1102
buf += r;
1103
- s->offset += r;
1104
if (buf >= end_of_vec && got < size) {
1105
i++;
1106
buf = i->iov_base;
1107
@@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
1108
1109
trace_ssh_write(offset, size);
1110
1111
- ssh_seek(s, offset, SSH_SEEK_WRITE);
1112
+ trace_ssh_seek(offset);
1113
+ sftp_seek64(s->sftp_handle, offset);
1114
1115
/* This keeps track of the current iovec element ('i'), where we
1116
* will read from next ('buf'), and the end of the current iovec
1117
@@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
1118
end_of_vec = i->iov_base + i->iov_len;
1119
1120
for (written = 0; written < size; ) {
1121
+ size_t request_write_size;
1122
again:
1123
- trace_ssh_write_buf(buf, end_of_vec - buf);
1124
- r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf);
1125
- trace_ssh_write_return(r);
1126
+ /*
1127
+ * Avoid too large data packets, as libssh currently does not
1128
+ * handle multiple requests on its own.
1129
+ */
1130
+ request_write_size = MIN(end_of_vec - buf, 131072);
1131
+ trace_ssh_write_buf(buf, end_of_vec - buf, request_write_size);
1132
+ r = sftp_write(s->sftp_handle, buf, request_write_size);
1133
+ trace_ssh_write_return(r, sftp_get_error(s->sftp));
1134
1135
- if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1136
+ if (r == SSH_AGAIN) {
1137
co_yield(s, bs);
1138
goto again;
1139
}
1140
if (r < 0) {
1141
sftp_error_trace(s, "write");
1142
- s->offset = -1;
1143
return -EIO;
1144
}
1145
- /* The libssh2 API is very unclear about this. A comment in
1146
- * the code says "nothing was acked, and no EAGAIN was
1147
- * received!" which apparently means that no data got sent
1148
- * out, and the underlying channel didn't return any EAGAIN
1149
- * indication. I think this is a bug in either libssh2 or
1150
- * OpenSSH (server-side). In any case, forcing a seek (to
1151
- * discard libssh2 internal buffers), and then trying again
1152
- * works for me.
1153
- */
1154
- if (r == 0) {
1155
- ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
1156
- co_yield(s, bs);
1157
- goto again;
1158
- }
1159
1160
written += r;
1161
buf += r;
1162
- s->offset += r;
1163
if (buf >= end_of_vec && written < size) {
1164
i++;
1165
buf = i->iov_base;
1166
end_of_vec = i->iov_base + i->iov_len;
1167
}
1168
1169
- if (offset + written > s->attrs.filesize)
1170
- s->attrs.filesize = offset + written;
1171
+ if (offset + written > s->attrs->size) {
1172
+ s->attrs->size = offset + written;
1173
+ }
1174
}
1175
1176
return 0;
1177
@@ -XXX,XX +XXX,XX @@ static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
1178
}
1179
}
1180
1181
-#ifdef HAS_LIBSSH2_SFTP_FSYNC
1182
+#ifdef HAVE_LIBSSH_0_8
1183
1184
static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
1185
{
1186
int r;
1187
1188
trace_ssh_flush();
1189
+
1190
+ if (!sftp_extension_supported(s->sftp, "fsync@openssh.com", "1")) {
1191
+ unsafe_flush_warning(s, "OpenSSH >= 6.3");
1192
+ return 0;
1193
+ }
1194
again:
1195
- r = libssh2_sftp_fsync(s->sftp_handle);
1196
- if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1197
+ r = sftp_fsync(s->sftp_handle);
1198
+ if (r == SSH_AGAIN) {
1199
co_yield(s, bs);
1200
goto again;
1201
}
1202
- if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
1203
- libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) {
1204
- unsafe_flush_warning(s, "OpenSSH >= 6.3");
1205
- return 0;
1206
- }
1207
if (r < 0) {
1208
sftp_error_trace(s, "fsync");
1209
return -EIO;
1210
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
1211
return ret;
1212
}
1213
1214
-#else /* !HAS_LIBSSH2_SFTP_FSYNC */
1215
+#else /* !HAVE_LIBSSH_0_8 */
1216
1217
static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
1218
{
1219
BDRVSSHState *s = bs->opaque;
1220
1221
- unsafe_flush_warning(s, "libssh2 >= 1.4.4");
1222
+ unsafe_flush_warning(s, "libssh >= 0.8.0");
1223
return 0;
1224
}
1225
1226
-#endif /* !HAS_LIBSSH2_SFTP_FSYNC */
1227
+#endif /* !HAVE_LIBSSH_0_8 */
1228
1229
static int64_t ssh_getlength(BlockDriverState *bs)
1230
{
1231
BDRVSSHState *s = bs->opaque;
1232
int64_t length;
1233
1234
- /* Note we cannot make a libssh2 call here. */
1235
- length = (int64_t) s->attrs.filesize;
1236
+ /* Note we cannot make a libssh call here. */
1237
+ length = (int64_t) s->attrs->size;
1238
trace_ssh_getlength(length);
1239
1240
return length;
1241
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
1242
return -ENOTSUP;
1243
}
1244
1245
- if (offset < s->attrs.filesize) {
1246
+ if (offset < s->attrs->size) {
1247
error_setg(errp, "ssh driver does not support shrinking files");
1248
return -ENOTSUP;
1249
}
1250
1251
- if (offset == s->attrs.filesize) {
1252
+ if (offset == s->attrs->size) {
1253
return 0;
1254
}
1255
1256
@@ -XXX,XX +XXX,XX @@ static void bdrv_ssh_init(void)
1257
{
1258
int r;
1259
1260
- r = libssh2_init(0);
1261
+ r = ssh_init();
1262
if (r != 0) {
1263
- fprintf(stderr, "libssh2 initialization failed, %d\n", r);
1264
+ fprintf(stderr, "libssh initialization failed, %d\n", r);
1265
exit(EXIT_FAILURE);
1266
}
1267
1268
+#if TRACE_LIBSSH != 0
1269
+ ssh_set_log_level(TRACE_LIBSSH);
1270
+#endif
1271
+
1272
bdrv_register(&bdrv_ssh);
1273
}
1274
1275
diff --git a/.travis.yml b/.travis.yml
1276
index XXXXXXX..XXXXXXX 100644
1277
--- a/.travis.yml
1278
+++ b/.travis.yml
1279
@@ -XXX,XX +XXX,XX @@ addons:
1280
- libseccomp-dev
1281
- libspice-protocol-dev
1282
- libspice-server-dev
1283
- - libssh2-1-dev
1284
+ - libssh-dev
1285
- liburcu-dev
1286
- libusb-1.0-0-dev
1287
- libvte-2.91-dev
1288
@@ -XXX,XX +XXX,XX @@ matrix:
1289
- libseccomp-dev
1290
- libspice-protocol-dev
1291
- libspice-server-dev
1292
- - libssh2-1-dev
1293
+ - libssh-dev
1294
- liburcu-dev
1295
- libusb-1.0-0-dev
1296
- libvte-2.91-dev
1297
diff --git a/block/trace-events b/block/trace-events
1298
index XXXXXXX..XXXXXXX 100644
1299
--- a/block/trace-events
1300
+++ b/block/trace-events
1301
@@ -XXX,XX +XXX,XX @@ nbd_client_connect_success(const char *export_name) "export '%s'"
1302
# ssh.c
1303
ssh_restart_coroutine(void *co) "co=%p"
1304
ssh_flush(void) "fsync"
1305
-ssh_check_host_key_knownhosts(const char *key) "host key OK: %s"
1306
+ssh_check_host_key_knownhosts(void) "host key OK"
1307
ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o"
1308
ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p"
1309
ssh_co_yield_back(int sock) "s->sock=%d - back"
1310
ssh_getlength(int64_t length) "length=%" PRIi64
1311
ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64
1312
ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
1313
-ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu"
1314
-ssh_read_return(ssize_t ret) "sftp_read returned %zd"
1315
+ssh_read_buf(void *buf, size_t size, size_t actual_size) "sftp_read buf=%p size=%zu (actual size=%zu)"
1316
+ssh_read_return(ssize_t ret, int sftp_err) "sftp_read returned %zd (sftp error=%d)"
1317
ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
1318
-ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu"
1319
-ssh_write_return(ssize_t ret) "sftp_write returned %zd"
1320
+ssh_write_buf(void *buf, size_t size, size_t actual_size) "sftp_write buf=%p size=%zu (actual size=%zu)"
1321
+ssh_write_return(ssize_t ret, int sftp_err) "sftp_write returned %zd (sftp error=%d)"
1322
ssh_seek(int64_t offset) "seeking to offset=%" PRIi64
1323
+ssh_auth_methods(int methods) "auth methods=0x%x"
1324
+ssh_server_status(int status) "server status=%d"
1325
1326
# curl.c
1327
curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld"
1328
@@ -XXX,XX +XXX,XX @@ sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s"
1329
sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32
1330
1331
# ssh.c
1332
-sftp_error(const char *op, const char *ssh_err, int ssh_err_code, unsigned long sftp_err_code) "%s failed: %s (libssh2 error code: %d, sftp error code: %lu)"
1333
+sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)"
1334
diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi
1335
index XXXXXXX..XXXXXXX 100644
1336
--- a/docs/qemu-block-drivers.texi
1337
+++ b/docs/qemu-block-drivers.texi
1338
@@ -XXX,XX +XXX,XX @@ print a warning when @code{fsync} is not supported:
1339
1340
warning: ssh server @code{ssh.example.com:22} does not support fsync
1341
1342
-With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is
1343
+With sufficiently new versions of libssh and OpenSSH, @code{fsync} is
1344
supported.
1345
1346
@node disk_images_nvme
1347
diff --git a/tests/docker/dockerfiles/debian-win32-cross.docker b/tests/docker/dockerfiles/debian-win32-cross.docker
1348
index XXXXXXX..XXXXXXX 100644
1349
--- a/tests/docker/dockerfiles/debian-win32-cross.docker
1350
+++ b/tests/docker/dockerfiles/debian-win32-cross.docker
1351
@@ -XXX,XX +XXX,XX @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
1352
mxe-$TARGET-w64-mingw32.shared-curl \
1353
mxe-$TARGET-w64-mingw32.shared-glib \
1354
mxe-$TARGET-w64-mingw32.shared-libgcrypt \
1355
- mxe-$TARGET-w64-mingw32.shared-libssh2 \
1356
mxe-$TARGET-w64-mingw32.shared-libusb1 \
1357
mxe-$TARGET-w64-mingw32.shared-lzo \
1358
mxe-$TARGET-w64-mingw32.shared-nettle \
1359
diff --git a/tests/docker/dockerfiles/debian-win64-cross.docker b/tests/docker/dockerfiles/debian-win64-cross.docker
1360
index XXXXXXX..XXXXXXX 100644
1361
--- a/tests/docker/dockerfiles/debian-win64-cross.docker
1362
+++ b/tests/docker/dockerfiles/debian-win64-cross.docker
1363
@@ -XXX,XX +XXX,XX @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
1364
mxe-$TARGET-w64-mingw32.shared-curl \
1365
mxe-$TARGET-w64-mingw32.shared-glib \
1366
mxe-$TARGET-w64-mingw32.shared-libgcrypt \
1367
- mxe-$TARGET-w64-mingw32.shared-libssh2 \
1368
mxe-$TARGET-w64-mingw32.shared-libusb1 \
1369
mxe-$TARGET-w64-mingw32.shared-lzo \
1370
mxe-$TARGET-w64-mingw32.shared-nettle \
1371
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
1372
index XXXXXXX..XXXXXXX 100644
1373
--- a/tests/docker/dockerfiles/fedora.docker
1374
+++ b/tests/docker/dockerfiles/fedora.docker
1375
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES \
1376
libpng-devel \
1377
librbd-devel \
1378
libseccomp-devel \
1379
- libssh2-devel \
1380
+ libssh-devel \
1381
libubsan \
1382
libusbx-devel \
1383
libxml2-devel \
1384
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES \
1385
mingw32-gtk3 \
1386
mingw32-libjpeg-turbo \
1387
mingw32-libpng \
1388
- mingw32-libssh2 \
1389
mingw32-libtasn1 \
1390
mingw32-nettle \
1391
mingw32-pixman \
1392
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES \
1393
mingw64-gtk3 \
1394
mingw64-libjpeg-turbo \
1395
mingw64-libpng \
1396
- mingw64-libssh2 \
1397
mingw64-libtasn1 \
1398
mingw64-nettle \
1399
mingw64-pixman \
1400
diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker
1401
index XXXXXXX..XXXXXXX 100644
1402
--- a/tests/docker/dockerfiles/ubuntu.docker
1403
+++ b/tests/docker/dockerfiles/ubuntu.docker
1404
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES flex bison \
1405
libsnappy-dev \
1406
libspice-protocol-dev \
1407
libspice-server-dev \
1408
- libssh2-1-dev \
1409
+ libssh-dev \
1410
libusb-1.0-0-dev \
1411
libusbredirhost-dev \
1412
libvdeplug-dev \
1413
diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker
1414
index XXXXXXX..XXXXXXX 100644
1415
--- a/tests/docker/dockerfiles/ubuntu1804.docker
1416
+++ b/tests/docker/dockerfiles/ubuntu1804.docker
1417
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES flex bison \
1418
libsnappy-dev \
1419
libspice-protocol-dev \
1420
libspice-server-dev \
1421
- libssh2-1-dev \
1422
+ libssh-dev \
1423
libusb-1.0-0-dev \
1424
libusbredirhost-dev \
1425
libvdeplug-dev \
1426
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
1427
index XXXXXXX..XXXXXXX 100755
1428
--- a/tests/qemu-iotests/207
1429
+++ b/tests/qemu-iotests/207
1430
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1431
1432
iotests.img_info_log(remote_path)
1433
1434
- md5_key = subprocess.check_output(
1435
- 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
1436
- 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1',
1437
- shell=True).rstrip().decode('ascii')
1438
+ keys = subprocess.check_output(
1439
+ 'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
1440
+ 'cut -d" " -f3',
1441
+ shell=True).rstrip().decode('ascii').split('\n')
1442
+
1443
+ # Mappings of base64 representations to digests
1444
+ md5_keys = {}
1445
+ sha1_keys = {}
1446
+
1447
+ for key in keys:
1448
+ md5_keys[key] = subprocess.check_output(
1449
+ 'echo %s | base64 -d | md5sum -b | cut -d" " -f1' % key,
1450
+ shell=True).rstrip().decode('ascii')
1451
+
1452
+ sha1_keys[key] = subprocess.check_output(
1453
+ 'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
1454
+ shell=True).rstrip().decode('ascii')
1455
1456
vm.launch()
1457
+
1458
+ # Find correct key first
1459
+ matching_key = None
1460
+ for key in keys:
1461
+ result = vm.qmp('blockdev-add',
1462
+ driver='ssh', node_name='node0', path=disk_path,
1463
+ server={
1464
+ 'host': '127.0.0.1',
1465
+ 'port': '22',
1466
+ }, host_key_check={
1467
+ 'mode': 'hash',
1468
+ 'type': 'md5',
1469
+ 'hash': md5_keys[key],
1470
+ })
1471
+
1472
+ if 'error' not in result:
1473
+ vm.qmp('blockdev-del', node_name='node0')
1474
+ matching_key = key
1475
+ break
1476
+
1477
+ if matching_key is None:
1478
+ vm.shutdown()
1479
+ iotests.notrun('Did not find a key that fits 127.0.0.1')
1480
+
1481
blockdev_create(vm, { 'driver': 'ssh',
1482
'location': {
1483
'path': disk_path,
1484
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1485
'host-key-check': {
1486
'mode': 'hash',
1487
'type': 'md5',
1488
- 'hash': md5_key,
1489
+ 'hash': md5_keys[matching_key],
1490
}
1491
},
1492
'size': 8388608 })
1493
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1494
1495
iotests.img_info_log(remote_path)
1496
1497
- sha1_key = subprocess.check_output(
1498
- 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
1499
- 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1',
1500
- shell=True).rstrip().decode('ascii')
1501
-
1502
vm.launch()
1503
blockdev_create(vm, { 'driver': 'ssh',
1504
'location': {
1505
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1506
'host-key-check': {
1507
'mode': 'hash',
1508
'type': 'sha1',
1509
- 'hash': sha1_key,
1510
+ 'hash': sha1_keys[matching_key],
1511
}
1512
},
1513
'size': 4194304 })
1514
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
1515
index XXXXXXX..XXXXXXX 100644
1516
--- a/tests/qemu-iotests/207.out
1517
+++ b/tests/qemu-iotests/207.out
1518
@@ -XXX,XX +XXX,XX @@ virtual size: 4 MiB (4194304 bytes)
1519
1520
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
1521
{"return": {}}
1522
-Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)
1523
+Job failed: failed to open remote file '/this/is/not/an/existing/path': SFTP server: No such file (libssh error code: 1, sftp error code: 2)
1524
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
1525
{"return": {}}
1526
67
--
1527
--
68
2.29.2
1528
2.21.0
69
1529
70
1530
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Tests should place their files into the test directory. This includes
2
Unix sockets. 205 currently fails to do so, which prevents it from
3
being run concurrently.
2
4
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Message-id: 20190618210238.9524-1-mreitz@redhat.com
5
Message-Id: <20210116214705.822267-22-vsementsov@virtuozzo.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
---
9
---
8
scripts/simplebench/bench_block_job.py | 2 +-
10
tests/qemu-iotests/205 | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
11
1 file changed, 1 insertion(+), 1 deletion(-)
10
12
11
diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py
13
diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205
12
index XXXXXXX..XXXXXXX 100755
14
index XXXXXXX..XXXXXXX 100755
13
--- a/scripts/simplebench/bench_block_job.py
15
--- a/tests/qemu-iotests/205
14
+++ b/scripts/simplebench/bench_block_job.py
16
+++ b/tests/qemu-iotests/205
15
@@ -XXX,XX +XXX,XX @@
17
@@ -XXX,XX +XXX,XX @@ import iotests
16
-#!/usr/bin/env python
18
import time
17
+#!/usr/bin/env python3
19
from iotests import qemu_img_create, qemu_io, filter_qemu_io, QemuIoInteractive
18
#
20
19
# Benchmark block jobs
21
-nbd_sock = 'nbd_sock'
20
#
22
+nbd_sock = os.path.join(iotests.test_dir, 'nbd_sock')
23
nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
24
disk = os.path.join(iotests.test_dir, 'disk')
25
21
--
26
--
22
2.29.2
27
2.21.0
23
28
24
29
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Add argument to allow additional block-job options.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210116214705.822267-23-vsementsov@virtuozzo.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
scripts/simplebench/bench-example.py | 2 +-
11
scripts/simplebench/bench_block_job.py | 11 +++++++----
12
2 files changed, 8 insertions(+), 5 deletions(-)
13
14
diff --git a/scripts/simplebench/bench-example.py b/scripts/simplebench/bench-example.py
15
index XXXXXXX..XXXXXXX 100644
16
--- a/scripts/simplebench/bench-example.py
17
+++ b/scripts/simplebench/bench-example.py
18
@@ -XXX,XX +XXX,XX @@ from bench_block_job import bench_block_copy, drv_file, drv_nbd
19
20
def bench_func(env, case):
21
""" Handle one "cell" of benchmarking table. """
22
- return bench_block_copy(env['qemu_binary'], env['cmd'],
23
+ return bench_block_copy(env['qemu_binary'], env['cmd'], {}
24
case['source'], case['target'])
25
26
27
diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py
28
index XXXXXXX..XXXXXXX 100755
29
--- a/scripts/simplebench/bench_block_job.py
30
+++ b/scripts/simplebench/bench_block_job.py
31
@@ -XXX,XX +XXX,XX @@ def bench_block_job(cmd, cmd_args, qemu_args):
32
33
34
# Bench backup or mirror
35
-def bench_block_copy(qemu_binary, cmd, source, target):
36
+def bench_block_copy(qemu_binary, cmd, cmd_options, source, target):
37
"""Helper to run bench_block_job() for mirror or backup"""
38
assert cmd in ('blockdev-backup', 'blockdev-mirror')
39
40
source['node-name'] = 'source'
41
target['node-name'] = 'target'
42
43
- return bench_block_job(cmd,
44
- {'job-id': 'job0', 'device': 'source',
45
- 'target': 'target', 'sync': 'full'},
46
+ cmd_options['job-id'] = 'job0'
47
+ cmd_options['device'] = 'source'
48
+ cmd_options['target'] = 'target'
49
+ cmd_options['sync'] = 'full'
50
+
51
+ return bench_block_job(cmd, cmd_options,
52
[qemu_binary,
53
'-blockdev', json.dumps(source),
54
'-blockdev', json.dumps(target)])
55
--
56
2.29.2
57
58
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Add script to benchmark new backup architecture.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Message-Id: <20210116214705.822267-24-vsementsov@virtuozzo.com>
7
[mreitz: s/not unsupported/not supported/]
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
scripts/simplebench/bench-backup.py | 167 ++++++++++++++++++++++++++++
11
1 file changed, 167 insertions(+)
12
create mode 100755 scripts/simplebench/bench-backup.py
13
14
diff --git a/scripts/simplebench/bench-backup.py b/scripts/simplebench/bench-backup.py
15
new file mode 100755
16
index XXXXXXX..XXXXXXX
17
--- /dev/null
18
+++ b/scripts/simplebench/bench-backup.py
19
@@ -XXX,XX +XXX,XX @@
20
+#!/usr/bin/env python3
21
+#
22
+# Bench backup block-job
23
+#
24
+# Copyright (c) 2020 Virtuozzo International GmbH.
25
+#
26
+# This program is free software; you can redistribute it and/or modify
27
+# it under the terms of the GNU General Public License as published by
28
+# the Free Software Foundation; either version 2 of the License, or
29
+# (at your option) any later version.
30
+#
31
+# This program is distributed in the hope that it will be useful,
32
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
33
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
+# GNU General Public License for more details.
35
+#
36
+# You should have received a copy of the GNU General Public License
37
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
38
+#
39
+
40
+import argparse
41
+import json
42
+
43
+import simplebench
44
+from results_to_text import results_to_text
45
+from bench_block_job import bench_block_copy, drv_file, drv_nbd
46
+
47
+
48
+def bench_func(env, case):
49
+ """ Handle one "cell" of benchmarking table. """
50
+ cmd_options = env['cmd-options'] if 'cmd-options' in env else {}
51
+ return bench_block_copy(env['qemu-binary'], env['cmd'],
52
+ cmd_options,
53
+ case['source'], case['target'])
54
+
55
+
56
+def bench(args):
57
+ test_cases = []
58
+
59
+ sources = {}
60
+ targets = {}
61
+ for d in args.dir:
62
+ label, path = d.split(':') # paths with colon not supported
63
+ sources[label] = drv_file(path + '/test-source')
64
+ targets[label] = drv_file(path + '/test-target')
65
+
66
+ if args.nbd:
67
+ nbd = args.nbd.split(':')
68
+ host = nbd[0]
69
+ port = '10809' if len(nbd) == 1 else nbd[1]
70
+ drv = drv_nbd(host, port)
71
+ sources['nbd'] = drv
72
+ targets['nbd'] = drv
73
+
74
+ for t in args.test:
75
+ src, dst = t.split(':')
76
+
77
+ test_cases.append({
78
+ 'id': t,
79
+ 'source': sources[src],
80
+ 'target': targets[dst]
81
+ })
82
+
83
+ binaries = [] # list of (<label>, <path>, [<options>])
84
+ for i, q in enumerate(args.env):
85
+ name_path = q.split(':')
86
+ if len(name_path) == 1:
87
+ label = f'q{i}'
88
+ path_opts = name_path[0].split(',')
89
+ else:
90
+ assert len(name_path) == 2 # paths with colon not supported
91
+ label = name_path[0]
92
+ path_opts = name_path[1].split(',')
93
+
94
+ binaries.append((label, path_opts[0], path_opts[1:]))
95
+
96
+ test_envs = []
97
+
98
+ bin_paths = {}
99
+ for i, q in enumerate(args.env):
100
+ opts = q.split(',')
101
+ label_path = opts[0]
102
+ opts = opts[1:]
103
+
104
+ if ':' in label_path:
105
+ # path with colon inside is not supported
106
+ label, path = label_path.split(':')
107
+ bin_paths[label] = path
108
+ elif label_path in bin_paths:
109
+ label = label_path
110
+ path = bin_paths[label]
111
+ else:
112
+ path = label_path
113
+ label = f'q{i}'
114
+ bin_paths[label] = path
115
+
116
+ x_perf = {}
117
+ is_mirror = False
118
+ for opt in opts:
119
+ if opt == 'mirror':
120
+ is_mirror = True
121
+ elif opt == 'copy-range=on':
122
+ x_perf['use-copy-range'] = True
123
+ elif opt == 'copy-range=off':
124
+ x_perf['use-copy-range'] = False
125
+ elif opt.startswith('max-workers='):
126
+ x_perf['max-workers'] = int(opt.split('=')[1])
127
+
128
+ if is_mirror:
129
+ assert not x_perf
130
+ test_envs.append({
131
+ 'id': f'mirror({label})',
132
+ 'cmd': 'blockdev-mirror',
133
+ 'qemu-binary': path
134
+ })
135
+ else:
136
+ test_envs.append({
137
+ 'id': f'backup({label})\n' + '\n'.join(opts),
138
+ 'cmd': 'blockdev-backup',
139
+ 'cmd-options': {'x-perf': x_perf} if x_perf else {},
140
+ 'qemu-binary': path
141
+ })
142
+
143
+ result = simplebench.bench(bench_func, test_envs, test_cases, count=3)
144
+ with open('results.json', 'w') as f:
145
+ json.dump(result, f, indent=4)
146
+ print(results_to_text(result))
147
+
148
+
149
+class ExtendAction(argparse.Action):
150
+ def __call__(self, parser, namespace, values, option_string=None):
151
+ items = getattr(namespace, self.dest) or []
152
+ items.extend(values)
153
+ setattr(namespace, self.dest, items)
154
+
155
+
156
+if __name__ == '__main__':
157
+ p = argparse.ArgumentParser('Backup benchmark', epilog='''
158
+ENV format
159
+
160
+ (LABEL:PATH|LABEL|PATH)[,max-workers=N][,use-copy-range=(on|off)][,mirror]
161
+
162
+ LABEL short name for the binary
163
+ PATH path to the binary
164
+ max-workers set x-perf.max-workers of backup job
165
+ use-copy-range set x-perf.use-copy-range of backup job
166
+ mirror use mirror job instead of backup''',
167
+ formatter_class=argparse.RawTextHelpFormatter)
168
+ p.add_argument('--env', nargs='+', help='''\
169
+Qemu binaries with labels and options, see below
170
+"ENV format" section''',
171
+ action=ExtendAction)
172
+ p.add_argument('--dir', nargs='+', help='''\
173
+Directories, each containing "test-source" and/or
174
+"test-target" files, raw images to used in
175
+benchmarking. File path with label, like
176
+label:/path/to/directory''',
177
+ action=ExtendAction)
178
+ p.add_argument('--nbd', help='''\
179
+host:port for remote NBD image, (or just host, for
180
+default port 10809). Use it in tests, label is "nbd"
181
+(but you cannot create test nbd:nbd).''')
182
+ p.add_argument('--test', nargs='+', help='''\
183
+Tests, in form source-dir-label:target-dir-label''',
184
+ action=ExtendAction)
185
+
186
+ bench(p.parse_args())
187
--
188
2.29.2
189
190
diff view generated by jsdifflib
Deleted patch
1
Commit 0afec75734331 removed the 'change' QMP command, so we can no
2
longer test it in 118.
3
1
4
Fixes: 0afec75734331a0b52fa3aa4235220eda8c7846f
5
('qmp: remove deprecated "change" command')
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20210126104833.57026-1-mreitz@redhat.com>
8
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
9
---
10
tests/qemu-iotests/118 | 20 +-------------------
11
tests/qemu-iotests/118.out | 4 ++--
12
2 files changed, 3 insertions(+), 21 deletions(-)
13
14
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
15
index XXXXXXX..XXXXXXX 100755
16
--- a/tests/qemu-iotests/118
17
+++ b/tests/qemu-iotests/118
18
@@ -XXX,XX +XXX,XX @@
19
#!/usr/bin/env python3
20
# group: rw
21
#
22
-# Test case for the QMP 'change' command and all other associated
23
-# commands
24
+# Test case for media change monitor commands
25
#
26
# Copyright (C) 2015 Red Hat, Inc.
27
#
28
@@ -XXX,XX +XXX,XX @@ class ChangeBaseClass(iotests.QMPTestCase):
29
30
class GeneralChangeTestsBaseClass(ChangeBaseClass):
31
32
- def test_change(self):
33
- # 'change' requires a drive name, so skip the test for blockdev
34
- if not self.use_drive:
35
- return
36
-
37
- result = self.vm.qmp('change', device='drive0', target=new_img,
38
- arg=iotests.imgfmt)
39
- self.assert_qmp(result, 'return', {})
40
-
41
- self.wait_for_open()
42
- self.wait_for_close()
43
-
44
- result = self.vm.qmp('query-block')
45
- if self.has_real_tray:
46
- self.assert_qmp(result, 'return[0]/tray_open', False)
47
- self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
48
-
49
def test_blockdev_change_medium(self):
50
result = self.vm.qmp('blockdev-change-medium',
51
id=self.device_name, filename=new_img,
52
diff --git a/tests/qemu-iotests/118.out b/tests/qemu-iotests/118.out
53
index XXXXXXX..XXXXXXX 100644
54
--- a/tests/qemu-iotests/118.out
55
+++ b/tests/qemu-iotests/118.out
56
@@ -XXX,XX +XXX,XX @@
57
-.......................................................................................................................................................................
58
+...........................................................................................................................................................
59
----------------------------------------------------------------------
60
-Ran 167 tests
61
+Ran 155 tests
62
63
OK
64
--
65
2.29.2
66
67
diff view generated by jsdifflib
Deleted patch
1
ccd3b3b8112 has deprecated short-hand boolean options (i.e., options
2
with values). All options without values are interpreted as boolean
3
options, so this includes the invalid option "snapshot.foo" used in
4
iotest 178.
5
1
6
So after ccd3b3b8112, 178 fails with:
7
8
+qemu-img: warning: short-form boolean option 'snapshot.foo' deprecated
9
+Please use snapshot.foo=on instead
10
11
Suppress that deprecation warning by passing some value to it (it does
12
not matter which, because the option is invalid anyway).
13
14
Fixes: ccd3b3b8112b670fdccf8a392b8419b173ffccb4
15
("qemu-option: warn for short-form boolean options")
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
Message-Id: <20210126123834.115915-1-mreitz@redhat.com>
18
---
19
tests/qemu-iotests/178 | 2 +-
20
tests/qemu-iotests/178.out.qcow2 | 2 +-
21
tests/qemu-iotests/178.out.raw | 2 +-
22
3 files changed, 3 insertions(+), 3 deletions(-)
23
24
diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178
25
index XXXXXXX..XXXXXXX 100755
26
--- a/tests/qemu-iotests/178
27
+++ b/tests/qemu-iotests/178
28
@@ -XXX,XX +XXX,XX @@ $QEMU_IMG measure --image-opts # missing filename
29
$QEMU_IMG measure -f qcow2 # missing filename
30
$QEMU_IMG measure -l snap1 # missing filename
31
$QEMU_IMG measure -o , # invalid option list
32
-$QEMU_IMG measure -l snapshot.foo # invalid snapshot option
33
+$QEMU_IMG measure -l snapshot.foo=bar # invalid snapshot option
34
$QEMU_IMG measure --output foo # invalid output format
35
$QEMU_IMG measure --size -1 # invalid image size
36
$QEMU_IMG measure -O foo "$TEST_IMG" # unknown image file format
37
diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2
38
index XXXXXXX..XXXXXXX 100644
39
--- a/tests/qemu-iotests/178.out.qcow2
40
+++ b/tests/qemu-iotests/178.out.qcow2
41
@@ -XXX,XX +XXX,XX @@ qemu-img: --image-opts, -f, and -l require a filename argument.
42
qemu-img: --image-opts, -f, and -l require a filename argument.
43
qemu-img: Invalid option list: ,
44
qemu-img: Invalid parameter 'snapshot.foo'
45
-qemu-img: Failed in parsing snapshot param 'snapshot.foo'
46
+qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
47
qemu-img: --output must be used with human or json as argument.
48
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
49
qemu-img: Unknown file format 'foo'
50
diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw
51
index XXXXXXX..XXXXXXX 100644
52
--- a/tests/qemu-iotests/178.out.raw
53
+++ b/tests/qemu-iotests/178.out.raw
54
@@ -XXX,XX +XXX,XX @@ qemu-img: --image-opts, -f, and -l require a filename argument.
55
qemu-img: --image-opts, -f, and -l require a filename argument.
56
qemu-img: Invalid option list: ,
57
qemu-img: Invalid parameter 'snapshot.foo'
58
-qemu-img: Failed in parsing snapshot param 'snapshot.foo'
59
+qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
60
qemu-img: --output must be used with human or json as argument.
61
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
62
qemu-img: Unknown file format 'foo'
63
--
64
2.29.2
65
66
diff view generated by jsdifflib