1
The following changes since commit bae31bfa48b9caecee25da3d5333901a126a06b4:
1
The following changes since commit 31ee895047bdcf7387e3570cbd2a473c6f744b08:
2
2
3
Merge remote-tracking branch 'remotes/kraxel/tags/audio-20200619-pull-request' into staging (2020-06-19 22:56:59 +0100)
3
Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging (2021-01-25 15:56:13 +0000)
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-2020-06-22
7
https://github.com/XanClic/qemu.git tags/pull-block-2021-01-26
8
8
9
for you to fetch changes up to 74c55e4142a7bb835c38d3770c74210cbb1e4fab:
9
for you to fetch changes up to bb24cdc5efee580e81f71c5ff0fd980f2cc179d0:
10
10
11
iotests: don't test qcow2.py inside 291 (2020-06-22 16:05:23 +0200)
11
iotests/178: Pass value to invalid option (2021-01-26 14:36:37 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Block patches:
15
- Support modifying a LUKS-encrypted image's keyslots
15
- Make backup block jobs use asynchronous requests with the block-copy
16
- iotest fixes
16
module
17
- Use COR filter node for stream block jobs
18
- Make coroutine-sigaltstack’s qemu_coroutine_new() function thread-safe
19
- Report error string when file locking fails with an unexpected errno
20
- iotest fixes, additions, and some refactoring
17
21
18
----------------------------------------------------------------
22
----------------------------------------------------------------
19
Max Reitz (1):
23
Alberto Garcia (1):
20
iotests: Make _filter_img_create more active
24
iotests: Add test for the regression fixed in c8bf9a9169
21
25
22
Maxim Levitsky (14):
26
Andrey Shinkevich (10):
23
iotests: filter few more luks specific create options
27
copy-on-read: support preadv/pwritev_part functions
24
qcrypto/core: add generic infrastructure for crypto options amendment
28
block: add API function to insert a node
25
qcrypto/luks: implement encryption key management
29
copy-on-read: add filter drop function
26
block/amend: add 'force' option
30
qapi: add filter-node-name to block-stream
27
block/amend: separate amend and create options for qemu-img
31
qapi: copy-on-read filter: add 'bottom' option
28
block/amend: refactor qcow2 amend options
32
iotests: add #310 to test bottom node in COR driver
29
block/crypto: rename two functions
33
block: include supported_read_flags into BDS structure
30
block/crypto: implement the encryption key management
34
copy-on-read: skip non-guest reads if no copy needed
31
block/qcow2: extend qemu-img amend interface with crypto options
35
stream: rework backing-file changing
32
iotests: qemu-img tests for luks key management
36
block: apply COR-filter to block-stream jobs
33
block/core: add generic infrastructure for x-blockdev-amend qmp
34
command
35
block/crypto: implement blockdev-amend
36
block/qcow2: implement blockdev-amend
37
iotests: add tests for blockdev-amend
38
37
39
Philippe Mathieu-Daudé (1):
38
David Edmondson (1):
40
iotests: Fix 051 output after qdev_init_nofail() removal
39
block: report errno when flock fcntl fails
41
40
42
Vladimir Sementsov-Ogievskiy (2):
41
Max Reitz (14):
43
block/block-copy: block_copy_dirty_clusters: fix failure check
42
iotests.py: Assume a couple of variables as given
44
iotests: don't test qcow2.py inside 291
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
45
56
46
docs/tools/qemu-img.rst | 5 +-
57
Vladimir Sementsov-Ogievskiy (27):
47
qapi/block-core.json | 68 +++++
58
iotests: fix _check_o_direct
48
qapi/crypto.json | 73 +++++-
59
qapi: block-stream: add "bottom" argument
49
qapi/job.json | 4 +-
60
iotests: 30: prepare to COR filter insertion by stream job
50
block/crypto.h | 37 +++
61
block/stream: add s->target_bs
51
crypto/blockpriv.h | 8 +
62
qapi: backup: add perf.use-copy-range parameter
52
include/block/block.h | 1 +
63
block/block-copy: More explicit call_state
53
include/block/block_int.h | 24 +-
64
block/block-copy: implement block_copy_async
54
include/crypto/block.h | 22 ++
65
block/block-copy: add max_chunk and max_workers parameters
55
block.c | 4 +-
66
block/block-copy: add list of all call-states
56
block/amend.c | 113 +++++++++
67
block/block-copy: add ratelimit to block-copy
57
block/block-copy.c | 4 +-
68
block/block-copy: add block_copy_cancel
58
block/crypto.c | 206 +++++++++++++--
69
blockjob: add set_speed to BlockJobDriver
59
block/qcow2.c | 332 +++++++++++++-----------
70
job: call job_enter from job_pause
60
crypto/block-luks.c | 416 ++++++++++++++++++++++++++++++-
71
qapi: backup: add max-chunk and max-workers to x-perf struct
61
crypto/block.c | 29 +++
72
iotests: 56: prepare for backup over block-copy
62
qemu-img.c | 44 +++-
73
iotests: 185: prepare for backup over block-copy
63
block/Makefile.objs | 2 +-
74
iotests: 219: prepare for backup over block-copy
64
qemu-img-cmds.hx | 4 +-
75
iotests: 257: prepare for backup over block-copy
65
tests/qemu-iotests/049.out | 102 ++++----
76
block/block-copy: make progress_bytes_callback optional
66
tests/qemu-iotests/051.pc.out | 4 +-
77
block/backup: drop extra gotos from backup_run()
67
tests/qemu-iotests/061.out | 12 +-
78
backup: move to block-copy
68
tests/qemu-iotests/082.out | 185 ++++----------
79
qapi: backup: disable copy_range by default
69
tests/qemu-iotests/085.out | 38 +--
80
block/block-copy: drop unused block_copy_set_progress_callback()
70
tests/qemu-iotests/087.out | 6 +-
81
block/block-copy: drop unused argument of block_copy()
71
tests/qemu-iotests/112.out | 2 +-
82
simplebench/bench_block_job: use correct shebang line with python3
72
tests/qemu-iotests/134.out | 2 +-
83
simplebench: bench_block_job: add cmd_options argument
73
tests/qemu-iotests/141 | 2 +-
84
simplebench: add bench-backup.py
74
tests/qemu-iotests/144.out | 4 +-
85
75
tests/qemu-iotests/153 | 9 +-
86
qapi/block-core.json | 66 +++++-
76
tests/qemu-iotests/158.out | 4 +-
87
block/backup-top.h | 1 +
77
tests/qemu-iotests/182.out | 2 +-
88
block/copy-on-read.h | 32 +++
78
tests/qemu-iotests/185.out | 8 +-
89
include/block/block-copy.h | 61 ++++-
79
tests/qemu-iotests/188.out | 2 +-
90
include/block/block.h | 10 +-
80
tests/qemu-iotests/189.out | 4 +-
91
include/block/block_int.h | 15 +-
81
tests/qemu-iotests/198.out | 4 +-
92
include/block/blockjob_int.h | 2 +
82
tests/qemu-iotests/255.out | 8 +-
93
block.c | 25 ++
83
tests/qemu-iotests/263.out | 4 +-
94
block/backup-top.c | 6 +-
84
tests/qemu-iotests/274.out | 46 ++--
95
block/backup.c | 233 ++++++++++++-------
85
tests/qemu-iotests/280.out | 2 +-
96
block/block-copy.c | 227 +++++++++++++++---
86
tests/qemu-iotests/284.out | 6 +-
97
block/copy-on-read.c | 184 ++++++++++++++-
87
tests/qemu-iotests/291 | 4 -
98
block/file-posix.c | 38 ++-
88
tests/qemu-iotests/291.out | 33 ---
99
block/io.c | 10 +-
89
tests/qemu-iotests/293 | 207 +++++++++++++++
100
block/monitor/block-hmp-cmds.c | 7 +-
90
tests/qemu-iotests/293.out | 99 ++++++++
101
block/replication.c | 2 +
91
tests/qemu-iotests/294 | 90 +++++++
102
block/stream.c | 185 +++++++++------
92
tests/qemu-iotests/294.out | 30 +++
103
blockdev.c | 83 +++++--
93
tests/qemu-iotests/295 | 279 +++++++++++++++++++++
104
blockjob.c | 6 +
94
tests/qemu-iotests/295.out | 40 +++
105
job.c | 3 +
95
tests/qemu-iotests/296 | 234 +++++++++++++++++
106
util/coroutine-sigaltstack.c | 9 +
96
tests/qemu-iotests/296.out | 33 +++
107
scripts/simplebench/bench-backup.py | 167 ++++++++++++++
97
tests/qemu-iotests/common.filter | 93 +++++--
108
scripts/simplebench/bench-example.py | 2 +-
98
tests/qemu-iotests/group | 4 +
109
scripts/simplebench/bench_block_job.py | 13 +-
99
53 files changed, 2482 insertions(+), 516 deletions(-)
110
tests/qemu-iotests/030 | 12 +-
100
create mode 100644 block/amend.c
111
tests/qemu-iotests/056 | 9 +-
101
create mode 100755 tests/qemu-iotests/293
112
tests/qemu-iotests/109.out | 24 ++
102
create mode 100644 tests/qemu-iotests/293.out
113
tests/qemu-iotests/118 | 20 +-
103
create mode 100755 tests/qemu-iotests/294
114
tests/qemu-iotests/118.out | 4 +-
104
create mode 100644 tests/qemu-iotests/294.out
115
tests/qemu-iotests/124 | 8 +-
105
create mode 100755 tests/qemu-iotests/295
116
tests/qemu-iotests/129 | 79 ++++---
106
create mode 100644 tests/qemu-iotests/295.out
117
tests/qemu-iotests/141.out | 2 +-
107
create mode 100755 tests/qemu-iotests/296
118
tests/qemu-iotests/178 | 2 +-
108
create mode 100644 tests/qemu-iotests/296.out
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
109
144
110
--
145
--
111
2.26.2
146
2.29.2
112
147
113
148
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
2
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
2
3
'force' option will be used for some unsafe amend operations.
3
Provide API for insertion a node to backing chain.
4
4
5
This includes things like erasing last keyslot in luks based formats
5
Suggested-by: Max Reitz <mreitz@redhat.com>
6
(which destroys the data, unless the master key is backed up
6
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
7
by external means), but that _might_ be desired result.
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
9
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
10
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Message-Id: <20200608094030.670121-4-mlevitsk@redhat.com>
9
Message-Id: <20201216061703.70908-3-vsementsov@virtuozzo.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
11
---
15
docs/tools/qemu-img.rst | 5 ++++-
12
include/block/block.h | 2 ++
16
include/block/block.h | 1 +
13
block.c | 25 +++++++++++++++++++++++++
17
include/block/block_int.h | 1 +
14
2 files changed, 27 insertions(+)
18
block.c | 4 +++-
19
block/qcow2.c | 1 +
20
qemu-img.c | 8 +++++++-
21
qemu-img-cmds.hx | 4 ++--
22
7 files changed, 19 insertions(+), 5 deletions(-)
23
15
24
diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst
25
index XXXXXXX..XXXXXXX 100644
26
--- a/docs/tools/qemu-img.rst
27
+++ b/docs/tools/qemu-img.rst
28
@@ -XXX,XX +XXX,XX @@ Command description:
29
30
.. program:: qemu-img-commands
31
32
-.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME
33
+.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] [--force] -o OPTIONS FILENAME
34
35
Amends the image format specific *OPTIONS* for the image file
36
*FILENAME*. Not all file formats support this operation.
37
38
+ --force allows some unsafe operations. Currently for -f luks, it allows to
39
+ erase the last encryption key, and to overwrite an active encryption key.
40
+
41
.. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-i AIO] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME
42
43
Run a simple sequential I/O benchmark on the specified image. If ``-w`` is
44
diff --git a/include/block/block.h b/include/block/block.h
16
diff --git a/include/block/block.h b/include/block/block.h
45
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
46
--- a/include/block/block.h
18
--- a/include/block/block.h
47
+++ b/include/block/block.h
19
+++ b/include/block/block.h
48
@@ -XXX,XX +XXX,XX @@ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset,
20
@@ -XXX,XX +XXX,XX @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
49
int64_t total_work_size, void *opaque);
21
Error **errp);
50
int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
22
void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
51
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
52
+ bool force,
53
Error **errp);
23
Error **errp);
54
24
+BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
55
/* check if a named node can be replaced when doing drive-mirror */
25
+ int flags, Error **errp);
56
diff --git a/include/block/block_int.h b/include/block/block_int.h
26
57
index XXXXXXX..XXXXXXX 100644
27
int bdrv_parse_aio(const char *mode, int *flags);
58
--- a/include/block/block_int.h
28
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
59
+++ b/include/block/block_int.h
60
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
61
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
62
BlockDriverAmendStatusCB *status_cb,
63
void *cb_opaque,
64
+ bool force,
65
Error **errp);
66
67
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
68
diff --git a/block.c b/block.c
29
diff --git a/block.c b/block.c
69
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
70
--- a/block.c
31
--- a/block.c
71
+++ b/block.c
32
+++ b/block.c
72
@@ -XXX,XX +XXX,XX @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_delete(BlockDriverState *bs)
73
34
g_free(bs);
74
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
75
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
76
+ bool force,
77
Error **errp)
78
{
79
if (!bs->drv) {
80
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
81
bs->drv->format_name);
82
return -ENOTSUP;
83
}
84
- return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
85
+ return bs->drv->bdrv_amend_options(bs, opts, status_cb,
86
+ cb_opaque, force, errp);
87
}
35
}
88
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
+
89
/*
62
/*
90
diff --git a/block/qcow2.c b/block/qcow2.c
63
* Run consistency checks on an image
91
index XXXXXXX..XXXXXXX 100644
64
*
92
--- a/block/qcow2.c
93
+++ b/block/qcow2.c
94
@@ -XXX,XX +XXX,XX @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
95
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
96
BlockDriverAmendStatusCB *status_cb,
97
void *cb_opaque,
98
+ bool force,
99
Error **errp)
100
{
101
BDRVQcow2State *s = bs->opaque;
102
diff --git a/qemu-img.c b/qemu-img.c
103
index XXXXXXX..XXXXXXX 100644
104
--- a/qemu-img.c
105
+++ b/qemu-img.c
106
@@ -XXX,XX +XXX,XX @@ enum {
107
OPTION_DISABLE = 273,
108
OPTION_MERGE = 274,
109
OPTION_BITMAPS = 275,
110
+ OPTION_FORCE = 276,
111
};
112
113
typedef enum OutputFormat {
114
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
115
BlockBackend *blk = NULL;
116
BlockDriverState *bs = NULL;
117
bool image_opts = false;
118
+ bool force = false;
119
120
cache = BDRV_DEFAULT_CACHE;
121
for (;;) {
122
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
123
{"help", no_argument, 0, 'h'},
124
{"object", required_argument, 0, OPTION_OBJECT},
125
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
126
+ {"force", no_argument, 0, OPTION_FORCE},
127
{0, 0, 0, 0}
128
};
129
c = getopt_long(argc, argv, ":ho:f:t:pq",
130
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
131
case OPTION_IMAGE_OPTS:
132
image_opts = true;
133
break;
134
+ case OPTION_FORCE:
135
+ force = true;
136
+ break;
137
}
138
}
139
140
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
141
142
/* In case the driver does not call amend_status_cb() */
143
qemu_progress_print(0.f, 0);
144
- ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err);
145
+ ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, force, &err);
146
qemu_progress_print(100.f, 0);
147
if (ret < 0) {
148
error_report_err(err);
149
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
150
index XXXXXXX..XXXXXXX 100644
151
--- a/qemu-img-cmds.hx
152
+++ b/qemu-img-cmds.hx
153
@@ -XXX,XX +XXX,XX @@ HXCOMM When amending the rST sections, please remember to copy the usage
154
HXCOMM over to the per-command sections in docs/tools/qemu-img.rst.
155
156
DEF("amend", img_amend,
157
- "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename")
158
+ "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] [--force] -o options filename")
159
SRST
160
-.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME
161
+.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] [--force] -o OPTIONS FILENAME
162
ERST
163
164
DEF("bench", img_bench,
165
--
65
--
166
2.26.2
66
2.29.2
167
67
168
68
diff view generated by jsdifflib
New patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
2
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
New patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
2
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
2
3
Currently the implementation only supports amending the encryption
3
Add an option to limit copy-on-read operations to specified sub-chain
4
options, unlike the qemu-img version
4
of backing-chain, to make copy-on-read filter useful for block-stream
5
5
job.
6
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
6
7
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
7
Suggested-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20200608094030.670121-14-mlevitsk@redhat.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>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
15
---
12
qapi/block-core.json | 16 +++++++++++++++-
16
qapi/block-core.json | 20 ++++++++-
13
block/qcow2.c | 39 +++++++++++++++++++++++++++++++++++++++
17
block/copy-on-read.c | 98 +++++++++++++++++++++++++++++++++++++++++++-
14
2 files changed, 54 insertions(+), 1 deletion(-)
18
2 files changed, 115 insertions(+), 3 deletions(-)
15
19
16
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
18
--- a/qapi/block-core.json
22
--- a/qapi/block-core.json
19
+++ b/qapi/block-core.json
23
+++ b/qapi/block-core.json
20
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
21
'data': { }
25
'data': { 'throttle-group': 'str',
22
}
26
'file' : 'BlockdevRef'
23
27
} }
28
+
24
+##
29
+##
25
+# @BlockdevAmendOptionsQcow2:
30
+# @BlockdevOptionsCor:
26
+#
31
+#
27
+# Driver specific image amend options for qcow2.
32
+# Driver specific block device options for the copy-on-read driver.
28
+# For now, only encryption options can be amended
29
+#
33
+#
30
+# @encrypt Encryption options to be amended
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.
31
+#
39
+#
32
+# Since: 5.1
40
+# Since: 6.0
33
+##
41
+##
34
+{ 'struct': 'BlockdevAmendOptionsQcow2',
42
+{ 'struct': 'BlockdevOptionsCor',
35
+ 'data': { '*encrypt': 'QCryptoBlockAmendOptions' } }
43
+ 'base': 'BlockdevOptionsGenericFormat',
44
+ 'data': { '*bottom': 'str' } }
36
+
45
+
37
##
46
##
38
# @BlockdevAmendOptions:
47
# @BlockdevOptions:
39
#
48
#
40
@@ -XXX,XX +XXX,XX @@
49
@@ -XXX,XX +XXX,XX @@
41
'driver': 'BlockdevDriver' },
50
'bochs': 'BlockdevOptionsGenericFormat',
42
'discriminator': 'driver',
51
'cloop': 'BlockdevOptionsGenericFormat',
43
'data': {
52
'compress': 'BlockdevOptionsGenericFormat',
44
- 'luks': 'BlockdevAmendOptionsLUKS' } }
53
- 'copy-on-read':'BlockdevOptionsGenericFormat',
45
+ 'luks': 'BlockdevAmendOptionsLUKS',
54
+ 'copy-on-read':'BlockdevOptionsCor',
46
+ 'qcow2': 'BlockdevAmendOptionsQcow2' } }
55
'dmg': 'BlockdevOptionsGenericFormat',
47
56
'file': 'BlockdevOptionsFile',
48
##
57
'ftp': 'BlockdevOptionsCurlFtp',
49
# @x-blockdev-amend:
58
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
50
diff --git a/block/qcow2.c b/block/qcow2.c
51
index XXXXXXX..XXXXXXX 100644
59
index XXXXXXX..XXXXXXX 100644
52
--- a/block/qcow2.c
60
--- a/block/copy-on-read.c
53
+++ b/block/qcow2.c
61
+++ b/block/copy-on-read.c
54
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
62
@@ -XXX,XX +XXX,XX @@
55
return 0;
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;
56
}
172
}
57
173
58
+static int coroutine_fn qcow2_co_amend(BlockDriverState *bs,
174
59
+ BlockdevAmendOptions *opts,
175
@@ -XXX,XX +XXX,XX @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
60
+ bool force,
176
}
61
+ Error **errp)
177
178
179
+static void cor_close(BlockDriverState *bs)
62
+{
180
+{
63
+ BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2;
181
+ BDRVStateCOR *s = bs->opaque;
64
+ BDRVQcow2State *s = bs->opaque;
182
+
65
+ int ret = 0;
183
+ if (s->chain_frozen) {
66
+
184
+ s->chain_frozen = false;
67
+ if (qopts->has_encrypt) {
185
+ bdrv_unfreeze_backing_chain(bs, s->bottom_bs);
68
+ if (!s->crypto) {
186
+ }
69
+ error_setg(errp, "image is not encrypted, can't amend");
187
+
70
+ return -EOPNOTSUPP;
188
+ bdrv_unref(s->bottom_bs);
71
+ }
72
+
73
+ if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
74
+ error_setg(errp,
75
+ "Amend can't be used to change the qcow2 encryption format");
76
+ return -EOPNOTSUPP;
77
+ }
78
+
79
+ if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
80
+ error_setg(errp,
81
+ "Only LUKS encryption options can be amended for qcow2 with blockdev-amend");
82
+ return -EOPNOTSUPP;
83
+ }
84
+
85
+ ret = qcrypto_block_amend_options(s->crypto,
86
+ qcow2_crypto_hdr_read_func,
87
+ qcow2_crypto_hdr_write_func,
88
+ bs,
89
+ qopts->encrypt,
90
+ force,
91
+ errp);
92
+ }
93
+ return ret;
94
+}
189
+}
95
+
190
+
96
/*
191
+
97
* If offset or size are negative, respectively, they will not be included in
192
static BlockDriver bdrv_copy_on_read = {
98
* the BLOCK_IMAGE_CORRUPTED event emitted.
193
.format_name = "copy-on-read",
99
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
194
.instance_size = sizeof(BDRVStateCOR),
100
.mutable_opts = mutable_opts,
195
101
.bdrv_co_check = qcow2_co_check,
196
.bdrv_open = cor_open,
102
.bdrv_amend_options = qcow2_amend_options,
197
+ .bdrv_close = cor_close,
103
+ .bdrv_co_amend = qcow2_co_amend,
198
.bdrv_child_perm = cor_child_perm,
104
199
105
.bdrv_detach_aio_context = qcow2_detach_aio_context,
200
.bdrv_getlength = cor_getlength,
106
.bdrv_attach_aio_context = qcow2_attach_aio_context,
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
107
--
213
--
108
2.26.2
214
2.29.2
109
215
110
216
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
2
3
This commit adds two tests that cover the
3
The test case #310 is similar to #216 by Max Reitz. The difference is
4
new blockdev-amend functionality of luks and qcow2 driver
4
that the test #310 involves a bottom node to the COR filter driver.
5
5
6
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
6
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
7
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20200608094030.670121-15-mlevitsk@redhat.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]
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
12
---
11
tests/qemu-iotests/295 | 279 +++++++++++++++++++++++++++++++++++++
13
tests/qemu-iotests/310 | 117 +++++++++++++++++++++++++++++++++++++
12
tests/qemu-iotests/295.out | 40 ++++++
14
tests/qemu-iotests/310.out | 15 +++++
13
tests/qemu-iotests/296 | 234 +++++++++++++++++++++++++++++++
15
tests/qemu-iotests/group | 1 +
14
tests/qemu-iotests/296.out | 33 +++++
16
3 files changed, 133 insertions(+)
15
tests/qemu-iotests/group | 2 +
17
create mode 100755 tests/qemu-iotests/310
16
5 files changed, 588 insertions(+)
18
create mode 100644 tests/qemu-iotests/310.out
17
create mode 100755 tests/qemu-iotests/295
18
create mode 100644 tests/qemu-iotests/295.out
19
create mode 100755 tests/qemu-iotests/296
20
create mode 100644 tests/qemu-iotests/296.out
21
19
22
diff --git a/tests/qemu-iotests/295 b/tests/qemu-iotests/295
20
diff --git a/tests/qemu-iotests/310 b/tests/qemu-iotests/310
23
new file mode 100755
21
new file mode 100755
24
index XXXXXXX..XXXXXXX
22
index XXXXXXX..XXXXXXX
25
--- /dev/null
23
--- /dev/null
26
+++ b/tests/qemu-iotests/295
24
+++ b/tests/qemu-iotests/310
27
@@ -XXX,XX +XXX,XX @@
25
@@ -XXX,XX +XXX,XX @@
28
+#!/usr/bin/env python3
26
+#!/usr/bin/env python3
27
+# group: rw quick
29
+#
28
+#
30
+# Test case QMP's encrypted key management
29
+# Copy-on-read tests using a COR filter with a bottom node
31
+#
30
+#
32
+# Copyright (C) 2019 Red Hat, Inc.
31
+# Copyright (C) 2018 Red Hat, Inc.
32
+# Copyright (c) 2020 Virtuozzo International GmbH
33
+#
33
+#
34
+# This program is free software; you can redistribute it and/or modify
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
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
36
+# the Free Software Foundation; either version 2 of the License, or
37
+# (at your option) any later version.
37
+# (at your option) any later version.
...
...
44
+# You should have received a copy of the GNU General Public License
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/>.
45
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
46
+#
46
+#
47
+
47
+
48
+import iotests
48
+import iotests
49
+import os
49
+from iotests import log, qemu_img, qemu_io_silent
50
+import time
51
+import json
52
+
50
+
53
+test_img = os.path.join(iotests.test_dir, 'test.img')
51
+# Need backing file support
52
+iotests.script_initialize(supported_fmts=['qcow2'],
53
+ supported_platforms=['linux'])
54
+
54
+
55
+class Secret:
55
+log('')
56
+ def __init__(self, index):
56
+log('=== Copy-on-read across nodes ===')
57
+ self._id = "keysec" + str(index)
57
+log('')
58
+ # you are not supposed to see the password...
59
+ self._secret = "hunter" + str(index)
60
+
58
+
61
+ def id(self):
59
+# This test is similar to the 216 one by Max Reitz <mreitz@redhat.com>
62
+ return self._id
60
+# The difference is that this test case involves a bottom node to the
61
+# COR filter driver.
63
+
62
+
64
+ def secret(self):
63
+with iotests.FilePath('base.img') as base_img_path, \
65
+ return self._secret
64
+ iotests.FilePath('mid.img') as mid_img_path, \
65
+ iotests.FilePath('top.img') as top_img_path, \
66
+ iotests.VM() as vm:
66
+
67
+
67
+ def to_cmdline_object(self):
68
+ log('--- Setting up images ---')
68
+ return [ "secret,id=" + self._id + ",data=" + self._secret]
69
+ log('')
69
+
70
+
70
+ def to_qmp_object(self):
71
+ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
71
+ return { "qom_type" : "secret", "id": self.id(),
72
+ assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
72
+ "props": { "data": self.secret() } }
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
73
+
81
+
74
+################################################################################
82
+# 0 1 2 3 4
75
+class EncryptionSetupTestCase(iotests.QMPTestCase):
83
+# top 2
84
+# mid 3 3
85
+# base 1 1
76
+
86
+
77
+ # test case startup
87
+ log('Done')
78
+ def setUp(self):
79
+ # start the VM
80
+ self.vm = iotests.VM()
81
+ self.vm.launch()
82
+
88
+
83
+ # create the secrets and load 'em into the VM
89
+ log('')
84
+ self.secrets = [ Secret(i) for i in range(0, 6) ]
90
+ log('--- Doing COR ---')
85
+ for secret in self.secrets:
91
+ log('')
86
+ result = self.vm.qmp("object-add", **secret.to_qmp_object())
87
+ self.assert_qmp(result, 'return', {})
88
+
92
+
89
+ if iotests.imgfmt == "qcow2":
93
+ vm.launch()
90
+ self.pfx = "encrypt."
91
+ self.img_opts = [ '-o', "encrypt.format=luks" ]
92
+ else:
93
+ self.pfx = ""
94
+ self.img_opts = []
95
+
94
+
96
+ # test case shutdown
95
+ log(vm.qmp('blockdev-add',
97
+ def tearDown(self):
96
+ node_name='node0',
98
+ # stop the VM
97
+ driver='copy-on-read',
99
+ self.vm.shutdown()
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
+ }))
100
+
121
+
101
+ ###########################################################################
122
+ # Trigger COR
102
+ # create the encrypted block device
123
+ log(vm.qmp('human-monitor-command',
103
+ def createImg(self, file, secret):
124
+ command_line='qemu-io node0 "read 0 5M"'))
104
+
125
+
105
+ iotests.qemu_img(
126
+ vm.shutdown()
106
+ 'create',
107
+ '--object', *secret.to_cmdline_object(),
108
+ '-f', iotests.imgfmt,
109
+ '-o', self.pfx + 'key-secret=' + secret.id(),
110
+ '-o', self.pfx + 'iter-time=10',
111
+ *self.img_opts,
112
+ file,
113
+ '1M')
114
+
127
+
115
+ ###########################################################################
128
+ log('')
116
+ # open an encrypted block device
129
+ log('--- Checking COR result ---')
117
+ def openImageQmp(self, id, file, secret, read_only = False):
130
+ log('')
118
+
131
+
119
+ encrypt_options = {
132
+ # Detach backing to check that we can read the data from the top level now
120
+ 'key-secret' : secret.id()
133
+ assert qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt,
121
+ }
134
+ top_img_path) == 0
122
+
135
+
123
+ if iotests.imgfmt == "qcow2":
136
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0
124
+ encrypt_options = {
137
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
125
+ 'encrypt': {
138
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0
126
+ 'format':'luks',
139
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0
127
+ **encrypt_options
140
+ assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0
128
+ }
129
+ }
130
+
141
+
131
+ result = self.vm.qmp('blockdev-add', **
142
+ log('Done')
132
+ {
143
diff --git a/tests/qemu-iotests/310.out b/tests/qemu-iotests/310.out
133
+ 'driver': iotests.imgfmt,
134
+ 'node-name': id,
135
+ 'read-only': read_only,
136
+
137
+ **encrypt_options,
138
+
139
+ 'file': {
140
+ 'driver': 'file',
141
+ 'filename': test_img,
142
+ }
143
+ }
144
+ )
145
+ self.assert_qmp(result, 'return', {})
146
+
147
+ # close the encrypted block device
148
+ def closeImageQmp(self, id):
149
+ result = self.vm.qmp('blockdev-del', **{ 'node-name': id })
150
+ self.assert_qmp(result, 'return', {})
151
+
152
+ ###########################################################################
153
+ # add a key to an encrypted block device
154
+ def addKeyQmp(self, id, new_secret, secret = None,
155
+ slot = None, force = False):
156
+
157
+ crypt_options = {
158
+ 'state' : 'active',
159
+ 'new-secret' : new_secret.id(),
160
+ 'iter-time' : 10
161
+ }
162
+
163
+ if slot != None:
164
+ crypt_options['keyslot'] = slot
165
+
166
+
167
+ if secret != None:
168
+ crypt_options['secret'] = secret.id()
169
+
170
+ if iotests.imgfmt == "qcow2":
171
+ crypt_options['format'] = 'luks'
172
+ crypt_options = {
173
+ 'encrypt': crypt_options
174
+ }
175
+
176
+ args = {
177
+ 'node-name': id,
178
+ 'job-id' : 'job_add_key',
179
+ 'options' : {
180
+ 'driver' : iotests.imgfmt,
181
+ **crypt_options
182
+ },
183
+ }
184
+
185
+ if force == True:
186
+ args['force'] = True
187
+
188
+ #TODO: check what jobs return
189
+ result = self.vm.qmp('x-blockdev-amend', **args)
190
+ assert result['return'] == {}
191
+ self.vm.run_job('job_add_key')
192
+
193
+ # erase a key from an encrypted block device
194
+ def eraseKeyQmp(self, id, old_secret = None, slot = None, force = False):
195
+
196
+ crypt_options = {
197
+ 'state' : 'inactive',
198
+ }
199
+
200
+ if slot != None:
201
+ crypt_options['keyslot'] = slot
202
+ if old_secret != None:
203
+ crypt_options['old-secret'] = old_secret.id()
204
+
205
+ if iotests.imgfmt == "qcow2":
206
+ crypt_options['format'] = 'luks'
207
+ crypt_options = {
208
+ 'encrypt': crypt_options
209
+ }
210
+
211
+ args = {
212
+ 'node-name': id,
213
+ 'job-id' : 'job_erase_key',
214
+ 'options' : {
215
+ 'driver' : iotests.imgfmt,
216
+ **crypt_options
217
+ },
218
+ }
219
+
220
+ if force == True:
221
+ args['force'] = True
222
+
223
+ result = self.vm.qmp('x-blockdev-amend', **args)
224
+ assert result['return'] == {}
225
+ self.vm.run_job('job_erase_key')
226
+
227
+ ###########################################################################
228
+ # create image, and change its key
229
+ def testChangeKey(self):
230
+
231
+ # create the image with secret0 and open it
232
+ self.createImg(test_img, self.secrets[0]);
233
+ self.openImageQmp("testdev", test_img, self.secrets[0])
234
+
235
+ # add key to slot 1
236
+ self.addKeyQmp("testdev", new_secret = self.secrets[1])
237
+
238
+ # add key to slot 5
239
+ self.addKeyQmp("testdev", new_secret = self.secrets[2], slot=5)
240
+
241
+ # erase key from slot 0
242
+ self.eraseKeyQmp("testdev", old_secret = self.secrets[0])
243
+
244
+ #reopen the image with secret1
245
+ self.closeImageQmp("testdev")
246
+ self.openImageQmp("testdev", test_img, self.secrets[1])
247
+
248
+ # close and erase the image for good
249
+ self.closeImageQmp("testdev")
250
+ os.remove(test_img)
251
+
252
+ # test that if we erase the old password,
253
+ # we can still change the encryption keys using 'old-secret'
254
+ def testOldPassword(self):
255
+
256
+ # create the image with secret0 and open it
257
+ self.createImg(test_img, self.secrets[0]);
258
+ self.openImageQmp("testdev", test_img, self.secrets[0])
259
+
260
+ # add key to slot 1
261
+ self.addKeyQmp("testdev", new_secret = self.secrets[1])
262
+
263
+ # erase key from slot 0
264
+ self.eraseKeyQmp("testdev", old_secret = self.secrets[0])
265
+
266
+ # this will fail as the old password is no longer valid
267
+ self.addKeyQmp("testdev", new_secret = self.secrets[2])
268
+
269
+ # this will work
270
+ self.addKeyQmp("testdev", new_secret = self.secrets[2], secret = self.secrets[1])
271
+
272
+ # close and erase the image for good
273
+ self.closeImageQmp("testdev")
274
+ os.remove(test_img)
275
+
276
+ def testUseForceLuke(self):
277
+
278
+ self.createImg(test_img, self.secrets[0]);
279
+ self.openImageQmp("testdev", test_img, self.secrets[0])
280
+
281
+ # Add bunch of secrets
282
+ self.addKeyQmp("testdev", new_secret = self.secrets[1], slot=4)
283
+ self.addKeyQmp("testdev", new_secret = self.secrets[4], slot=2)
284
+
285
+ # overwrite an active secret
286
+ self.addKeyQmp("testdev", new_secret = self.secrets[5], slot=2)
287
+ self.addKeyQmp("testdev", new_secret = self.secrets[5], slot=2, force=True)
288
+
289
+ self.addKeyQmp("testdev", new_secret = self.secrets[0])
290
+
291
+ # Now erase all the secrets
292
+ self.eraseKeyQmp("testdev", old_secret = self.secrets[5])
293
+ self.eraseKeyQmp("testdev", slot=4)
294
+
295
+ # erase last keyslot
296
+ self.eraseKeyQmp("testdev", old_secret = self.secrets[0])
297
+ self.eraseKeyQmp("testdev", old_secret = self.secrets[0], force=True)
298
+
299
+ self.closeImageQmp("testdev")
300
+ os.remove(test_img)
301
+
302
+
303
+if __name__ == '__main__':
304
+ # Encrypted formats support
305
+ iotests.activate_logging()
306
+ iotests.main(supported_fmts = ['qcow2', 'luks'])
307
diff --git a/tests/qemu-iotests/295.out b/tests/qemu-iotests/295.out
308
new file mode 100644
144
new file mode 100644
309
index XXXXXXX..XXXXXXX
145
index XXXXXXX..XXXXXXX
310
--- /dev/null
146
--- /dev/null
311
+++ b/tests/qemu-iotests/295.out
147
+++ b/tests/qemu-iotests/310.out
312
@@ -XXX,XX +XXX,XX @@
148
@@ -XXX,XX +XXX,XX @@
313
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
149
+
150
+=== Copy-on-read across nodes ===
151
+
152
+--- Setting up images ---
153
+
154
+Done
155
+
156
+--- Doing COR ---
157
+
314
+{"return": {}}
158
+{"return": {}}
315
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
159
+{"return": ""}
316
+{"return": {}}
317
+{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}}
318
+{"return": {}}
319
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
320
+{"return": {}}
321
+{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}}
322
+{"return": {}}
323
+Job failed: Invalid password, cannot unlock any keyslot
324
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
325
+{"return": {}}
326
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
327
+{"return": {}}
328
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
329
+{"return": {}}
330
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
331
+{"return": {}}
332
+Job failed: Refusing to overwrite active keyslot 2 - please erase it first
333
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
334
+{"return": {}}
335
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
336
+{"return": {}}
337
+{"execute": "job-dismiss", "arguments": {"id": "job_add_key"}}
338
+{"return": {}}
339
+{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}}
340
+{"return": {}}
341
+{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}}
342
+{"return": {}}
343
+Job failed: All the active keyslots match the (old) password that was given and erasing them will erase all the data in the image irreversibly - refusing operation
344
+{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}}
345
+{"return": {}}
346
+{"execute": "job-dismiss", "arguments": {"id": "job_erase_key"}}
347
+{"return": {}}
348
+...
349
+----------------------------------------------------------------------
350
+Ran 3 tests
351
+
160
+
352
+OK
161
+--- Checking COR result ---
353
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
354
new file mode 100755
355
index XXXXXXX..XXXXXXX
356
--- /dev/null
357
+++ b/tests/qemu-iotests/296
358
@@ -XXX,XX +XXX,XX @@
359
+#!/usr/bin/env python3
360
+#
361
+# Test case for encryption key management versus image sharing
362
+#
363
+# Copyright (C) 2019 Red Hat, Inc.
364
+#
365
+# This program is free software; you can redistribute it and/or modify
366
+# it under the terms of the GNU General Public License as published by
367
+# the Free Software Foundation; either version 2 of the License, or
368
+# (at your option) any later version.
369
+#
370
+# This program is distributed in the hope that it will be useful,
371
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
372
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
373
+# GNU General Public License for more details.
374
+#
375
+# You should have received a copy of the GNU General Public License
376
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
377
+#
378
+
162
+
379
+import iotests
163
+Done
380
+import os
381
+import time
382
+import json
383
+
384
+test_img = os.path.join(iotests.test_dir, 'test.img')
385
+
386
+class Secret:
387
+ def __init__(self, index):
388
+ self._id = "keysec" + str(index)
389
+ # you are not supposed to see the password...
390
+ self._secret = "hunter" + str(index)
391
+
392
+ def id(self):
393
+ return self._id
394
+
395
+ def secret(self):
396
+ return self._secret
397
+
398
+ def to_cmdline_object(self):
399
+ return [ "secret,id=" + self._id + ",data=" + self._secret]
400
+
401
+ def to_qmp_object(self):
402
+ return { "qom_type" : "secret", "id": self.id(),
403
+ "props": { "data": self.secret() } }
404
+
405
+################################################################################
406
+
407
+class EncryptionSetupTestCase(iotests.QMPTestCase):
408
+
409
+ # test case startup
410
+ def setUp(self):
411
+
412
+ # start the VMs
413
+ self.vm1 = iotests.VM(path_suffix = 'VM1')
414
+ self.vm2 = iotests.VM(path_suffix = 'VM2')
415
+ self.vm1.launch()
416
+ self.vm2.launch()
417
+
418
+ # create the secrets and load 'em into the VMs
419
+ self.secrets = [ Secret(i) for i in range(0, 4) ]
420
+ for secret in self.secrets:
421
+ result = self.vm1.qmp("object-add", **secret.to_qmp_object())
422
+ self.assert_qmp(result, 'return', {})
423
+ result = self.vm2.qmp("object-add", **secret.to_qmp_object())
424
+ self.assert_qmp(result, 'return', {})
425
+
426
+ # test case shutdown
427
+ def tearDown(self):
428
+ # stop the VM
429
+ self.vm1.shutdown()
430
+ self.vm2.shutdown()
431
+
432
+ ###########################################################################
433
+ # create the encrypted block device using qemu-img
434
+ def createImg(self, file, secret):
435
+
436
+ output = iotests.qemu_img_pipe(
437
+ 'create',
438
+ '--object', *secret.to_cmdline_object(),
439
+ '-f', iotests.imgfmt,
440
+ '-o', 'key-secret=' + secret.id(),
441
+ '-o', 'iter-time=10',
442
+ file,
443
+ '1M')
444
+
445
+ iotests.log(output, filters=[iotests.filter_test_dir])
446
+
447
+ # attempts to add a key using qemu-img
448
+ def addKey(self, file, secret, new_secret):
449
+
450
+ image_options = {
451
+ 'key-secret' : secret.id(),
452
+ 'driver' : iotests.imgfmt,
453
+ 'file' : {
454
+ 'driver':'file',
455
+ 'filename': file,
456
+ }
457
+ }
458
+
459
+ output = iotests.qemu_img_pipe(
460
+ 'amend',
461
+ '--object', *secret.to_cmdline_object(),
462
+ '--object', *new_secret.to_cmdline_object(),
463
+
464
+ '-o', 'state=active',
465
+ '-o', 'new-secret=' + new_secret.id(),
466
+ '-o', 'iter-time=10',
467
+
468
+ "json:" + json.dumps(image_options)
469
+ )
470
+
471
+ iotests.log(output, filters=[iotests.filter_test_dir])
472
+
473
+ ###########################################################################
474
+ # open an encrypted block device
475
+ def openImageQmp(self, vm, id, file, secret,
476
+ readOnly = False, reOpen = False):
477
+
478
+ command = 'x-blockdev-reopen' if reOpen else 'blockdev-add'
479
+
480
+ result = vm.qmp(command, **
481
+ {
482
+ 'driver': iotests.imgfmt,
483
+ 'node-name': id,
484
+ 'read-only': readOnly,
485
+ 'key-secret' : secret.id(),
486
+ 'file': {
487
+ 'driver': 'file',
488
+ 'filename': test_img,
489
+ }
490
+ }
491
+ )
492
+ self.assert_qmp(result, 'return', {})
493
+
494
+ # close the encrypted block device
495
+ def closeImageQmp(self, vm, id):
496
+ result = vm.qmp('blockdev-del', **{ 'node-name': id })
497
+ self.assert_qmp(result, 'return', {})
498
+
499
+ ###########################################################################
500
+
501
+ # add a key to an encrypted block device
502
+ def addKeyQmp(self, vm, id, new_secret):
503
+
504
+ args = {
505
+ 'node-name': id,
506
+ 'job-id' : 'job0',
507
+ 'options' : {
508
+ 'state' : 'active',
509
+ 'driver' : iotests.imgfmt,
510
+ 'new-secret': new_secret.id(),
511
+ 'iter-time' : 10
512
+ },
513
+ }
514
+
515
+ result = vm.qmp('x-blockdev-amend', **args)
516
+ assert result['return'] == {}
517
+ vm.run_job('job0')
518
+
519
+ # test that when the image opened by two qemu processes,
520
+ # neither of them can update the image
521
+ def test1(self):
522
+ self.createImg(test_img, self.secrets[0]);
523
+
524
+ # VM1 opens the image and adds a key
525
+ self.openImageQmp(self.vm1, "testdev", test_img, self.secrets[0])
526
+ self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[1])
527
+
528
+
529
+ # VM2 opens the image
530
+ self.openImageQmp(self.vm2, "testdev", test_img, self.secrets[0])
531
+
532
+
533
+ # neither VMs now should be able to add a key
534
+ self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2])
535
+ self.addKeyQmp(self.vm2, "testdev", new_secret = self.secrets[2])
536
+
537
+
538
+ # VM 1 closes the image
539
+ self.closeImageQmp(self.vm1, "testdev")
540
+
541
+
542
+ # now VM2 can add the key
543
+ self.addKeyQmp(self.vm2, "testdev", new_secret = self.secrets[2])
544
+
545
+
546
+ # qemu-img should also not be able to add a key
547
+ self.addKey(test_img, self.secrets[0], self.secrets[2])
548
+
549
+ # cleanup
550
+ self.closeImageQmp(self.vm2, "testdev")
551
+ os.remove(test_img)
552
+
553
+
554
+ def test2(self):
555
+ self.createImg(test_img, self.secrets[0]);
556
+
557
+ # VM1 opens the image readonly
558
+ self.openImageQmp(self.vm1, "testdev", test_img, self.secrets[0],
559
+ readOnly = True)
560
+
561
+ # VM2 opens the image
562
+ self.openImageQmp(self.vm2, "testdev", test_img, self.secrets[0])
563
+
564
+ # VM1 can't add a key since image is readonly
565
+ self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2])
566
+
567
+ # VM2 can't add a key since VM is has the image opened
568
+ self.addKeyQmp(self.vm2, "testdev", new_secret = self.secrets[2])
569
+
570
+
571
+ #VM1 reopens the image read-write
572
+ self.openImageQmp(self.vm1, "testdev", test_img, self.secrets[0],
573
+ reOpen = True, readOnly = False)
574
+
575
+ # VM1 still can't add the key
576
+ self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2])
577
+
578
+ # VM2 gets away
579
+ self.closeImageQmp(self.vm2, "testdev")
580
+
581
+ # VM1 now can add the key
582
+ self.addKeyQmp(self.vm1, "testdev", new_secret = self.secrets[2])
583
+
584
+ self.closeImageQmp(self.vm1, "testdev")
585
+ os.remove(test_img)
586
+
587
+
588
+if __name__ == '__main__':
589
+ # support only raw luks since luks encrypted qcow2 is a proper
590
+ # format driver which doesn't allow any sharing
591
+ iotests.activate_logging()
592
+ iotests.main(supported_fmts = ['luks'])
593
diff --git a/tests/qemu-iotests/296.out b/tests/qemu-iotests/296.out
594
new file mode 100644
595
index XXXXXXX..XXXXXXX
596
--- /dev/null
597
+++ b/tests/qemu-iotests/296.out
598
@@ -XXX,XX +XXX,XX @@
599
+Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10
600
+
601
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
602
+{"return": {}}
603
+Job failed: Failed to get shared "consistent read" lock
604
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
605
+{"return": {}}
606
+Job failed: Failed to get shared "consistent read" lock
607
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
608
+{"return": {}}
609
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
610
+{"return": {}}
611
+qemu-img: Failed to get shared "consistent read" lock
612
+Is another process using the image [TEST_DIR/test.img]?
613
+
614
+Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10
615
+
616
+Job failed: Block node is read-only
617
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
618
+{"return": {}}
619
+Job failed: Failed to get shared "consistent read" lock
620
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
621
+{"return": {}}
622
+Job failed: Failed to get shared "consistent read" lock
623
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
624
+{"return": {}}
625
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
626
+{"return": {}}
627
+..
628
+----------------------------------------------------------------------
629
+Ran 2 tests
630
+
631
+OK
632
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
164
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
633
index XXXXXXX..XXXXXXX 100644
165
index XXXXXXX..XXXXXXX 100644
634
--- a/tests/qemu-iotests/group
166
--- a/tests/qemu-iotests/group
635
+++ b/tests/qemu-iotests/group
167
+++ b/tests/qemu-iotests/group
636
@@ -XXX,XX +XXX,XX @@
168
@@ -XXX,XX +XXX,XX @@
637
292 rw auto quick
169
307 rw quick export
638
293 rw auto
170
308 rw
639
294 rw auto quick
171
309 rw auto quick
640
+295 rw auto
172
+310 rw quick
641
+296 rw auto
173
312 rw quick
642
297 meta
643
--
174
--
644
2.26.2
175
2.29.2
645
176
646
177
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
2
3
Some options are only useful for creation
3
Add the new member supported_read_flags to the BlockDriverState
4
(or hard to be amended, like cluster size for qcow2), while some other
4
structure. It will control the flags set for copy-on-read operations.
5
options are only useful for amend, like upcoming keyslot management
5
Make the block generic layer evaluate supported read flags before they
6
options for luks
6
go to a block driver.
7
7
8
Since currently only qcow2 supports amend, move all its options
8
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
to a common macro and then include it in each action option list.
9
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
10
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
In future it might be useful to remove some options which are
11
[vsementsov: use assert instead of abort]
12
not supported anyway from amend list, which currently
13
cause an error message if amended.
14
15
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
16
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
18
Message-Id: <20200608094030.670121-5-mlevitsk@redhat.com>
13
Message-Id: <20201216061703.70908-8-vsementsov@virtuozzo.com>
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
---
15
---
21
include/block/block_int.h | 4 +
16
include/block/block_int.h | 4 ++++
22
block/qcow2.c | 173 +++++++++++++++++++++-----------------
17
block/io.c | 10 ++++++++--
23
qemu-img.c | 18 ++--
18
2 files changed, 12 insertions(+), 2 deletions(-)
24
3 files changed, 107 insertions(+), 88 deletions(-)
25
19
26
diff --git a/include/block/block_int.h b/include/block/block_int.h
20
diff --git a/include/block/block_int.h b/include/block/block_int.h
27
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/block_int.h
22
--- a/include/block/block_int.h
29
+++ b/include/block/block_int.h
23
+++ b/include/block/block_int.h
30
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
24
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
31
25
/* I/O Limits */
32
/* List of options for creating images, terminated by name == NULL */
26
BlockLimits bl;
33
QemuOptsList *create_opts;
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;
34
+
45
+
35
+ /* List of options for image amend */
46
ret = bdrv_is_allocated(bs, offset, bytes, &pnum);
36
+ QemuOptsList *amend_opts;
47
if (ret < 0) {
37
+
48
goto out;
38
/*
49
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
39
* If this driver supports reopening images this contains a
40
* NULL-terminated list of the runtime options that can be
41
diff --git a/block/qcow2.c b/block/qcow2.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/block/qcow2.c
44
+++ b/block/qcow2.c
45
@@ -XXX,XX +XXX,XX @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
46
s->signaled_corruption = true;
47
}
48
49
+#define QCOW_COMMON_OPTIONS \
50
+ { \
51
+ .name = BLOCK_OPT_SIZE, \
52
+ .type = QEMU_OPT_SIZE, \
53
+ .help = "Virtual disk size" \
54
+ }, \
55
+ { \
56
+ .name = BLOCK_OPT_COMPAT_LEVEL, \
57
+ .type = QEMU_OPT_STRING, \
58
+ .help = "Compatibility level (v2 [0.10] or v3 [1.1])" \
59
+ }, \
60
+ { \
61
+ .name = BLOCK_OPT_BACKING_FILE, \
62
+ .type = QEMU_OPT_STRING, \
63
+ .help = "File name of a base image" \
64
+ }, \
65
+ { \
66
+ .name = BLOCK_OPT_BACKING_FMT, \
67
+ .type = QEMU_OPT_STRING, \
68
+ .help = "Image format of the base image" \
69
+ }, \
70
+ { \
71
+ .name = BLOCK_OPT_DATA_FILE, \
72
+ .type = QEMU_OPT_STRING, \
73
+ .help = "File name of an external data file" \
74
+ }, \
75
+ { \
76
+ .name = BLOCK_OPT_DATA_FILE_RAW, \
77
+ .type = QEMU_OPT_BOOL, \
78
+ .help = "The external data file must stay valid " \
79
+ "as a raw image" \
80
+ }, \
81
+ { \
82
+ .name = BLOCK_OPT_ENCRYPT, \
83
+ .type = QEMU_OPT_BOOL, \
84
+ .help = "Encrypt the image with format 'aes'. (Deprecated " \
85
+ "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \
86
+ }, \
87
+ { \
88
+ .name = BLOCK_OPT_ENCRYPT_FORMAT, \
89
+ .type = QEMU_OPT_STRING, \
90
+ .help = "Encrypt the image, format choices: 'aes', 'luks'", \
91
+ }, \
92
+ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \
93
+ "ID of secret providing qcow AES key or LUKS passphrase"), \
94
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \
95
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \
96
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \
97
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \
98
+ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \
99
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \
100
+ { \
101
+ .name = BLOCK_OPT_CLUSTER_SIZE, \
102
+ .type = QEMU_OPT_SIZE, \
103
+ .help = "qcow2 cluster size", \
104
+ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \
105
+ }, \
106
+ { \
107
+ .name = BLOCK_OPT_PREALLOC, \
108
+ .type = QEMU_OPT_STRING, \
109
+ .help = "Preallocation mode (allowed values: off, " \
110
+ "metadata, falloc, full)" \
111
+ }, \
112
+ { \
113
+ .name = BLOCK_OPT_LAZY_REFCOUNTS, \
114
+ .type = QEMU_OPT_BOOL, \
115
+ .help = "Postpone refcount updates", \
116
+ .def_value_str = "off" \
117
+ }, \
118
+ { \
119
+ .name = BLOCK_OPT_REFCOUNT_BITS, \
120
+ .type = QEMU_OPT_NUMBER, \
121
+ .help = "Width of a reference count entry in bits", \
122
+ .def_value_str = "16" \
123
+ }, \
124
+ { \
125
+ .name = BLOCK_OPT_COMPRESSION_TYPE, \
126
+ .type = QEMU_OPT_STRING, \
127
+ .help = "Compression method used for image cluster " \
128
+ "compression", \
129
+ .def_value_str = "zlib" \
130
+ }
131
+
132
static QemuOptsList qcow2_create_opts = {
133
.name = "qcow2-create-opts",
134
.head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
135
.desc = {
136
- {
137
- .name = BLOCK_OPT_SIZE,
138
- .type = QEMU_OPT_SIZE,
139
- .help = "Virtual disk size"
140
- },
141
- {
142
- .name = BLOCK_OPT_COMPAT_LEVEL,
143
- .type = QEMU_OPT_STRING,
144
- .help = "Compatibility level (v2 [0.10] or v3 [1.1])"
145
- },
146
- {
147
- .name = BLOCK_OPT_BACKING_FILE,
148
- .type = QEMU_OPT_STRING,
149
- .help = "File name of a base image"
150
- },
151
- {
152
- .name = BLOCK_OPT_BACKING_FMT,
153
- .type = QEMU_OPT_STRING,
154
- .help = "Image format of the base image"
155
- },
156
- {
157
- .name = BLOCK_OPT_DATA_FILE,
158
- .type = QEMU_OPT_STRING,
159
- .help = "File name of an external data file"
160
- },
161
- {
162
- .name = BLOCK_OPT_DATA_FILE_RAW,
163
- .type = QEMU_OPT_BOOL,
164
- .help = "The external data file must stay valid as a raw image"
165
- },
166
- {
167
- .name = BLOCK_OPT_ENCRYPT,
168
- .type = QEMU_OPT_BOOL,
169
- .help = "Encrypt the image with format 'aes'. (Deprecated "
170
- "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)",
171
- },
172
- {
173
- .name = BLOCK_OPT_ENCRYPT_FORMAT,
174
- .type = QEMU_OPT_STRING,
175
- .help = "Encrypt the image, format choices: 'aes', 'luks'",
176
- },
177
- BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",
178
- "ID of secret providing qcow AES key or LUKS passphrase"),
179
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),
180
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),
181
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),
182
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
183
- BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
184
- BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
185
- {
186
- .name = BLOCK_OPT_CLUSTER_SIZE,
187
- .type = QEMU_OPT_SIZE,
188
- .help = "qcow2 cluster size",
189
- .def_value_str = stringify(DEFAULT_CLUSTER_SIZE)
190
- },
191
- {
192
- .name = BLOCK_OPT_PREALLOC,
193
- .type = QEMU_OPT_STRING,
194
- .help = "Preallocation mode (allowed values: off, metadata, "
195
- "falloc, full)"
196
- },
197
- {
198
- .name = BLOCK_OPT_LAZY_REFCOUNTS,
199
- .type = QEMU_OPT_BOOL,
200
- .help = "Postpone refcount updates",
201
- .def_value_str = "off"
202
- },
203
- {
204
- .name = BLOCK_OPT_REFCOUNT_BITS,
205
- .type = QEMU_OPT_NUMBER,
206
- .help = "Width of a reference count entry in bits",
207
- .def_value_str = "16"
208
- },
209
- {
210
- .name = BLOCK_OPT_COMPRESSION_TYPE,
211
- .type = QEMU_OPT_STRING,
212
- .help = "Compression method used for image cluster compression",
213
- .def_value_str = "zlib"
214
- },
215
+ QCOW_COMMON_OPTIONS,
216
+ { /* end of list */ }
217
+ }
218
+};
219
+
220
+static QemuOptsList qcow2_amend_opts = {
221
+ .name = "qcow2-amend-opts",
222
+ .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
223
+ .desc = {
224
+ QCOW_COMMON_OPTIONS,
225
{ /* end of list */ }
226
}
227
};
228
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
229
.bdrv_inactivate = qcow2_inactivate,
230
231
.create_opts = &qcow2_create_opts,
232
+ .amend_opts = &qcow2_amend_opts,
233
.strong_runtime_opts = qcow2_strong_runtime_opts,
234
.mutable_opts = mutable_opts,
235
.bdrv_co_check = qcow2_co_check,
236
diff --git a/qemu-img.c b/qemu-img.c
237
index XXXXXXX..XXXXXXX 100644
238
--- a/qemu-img.c
239
+++ b/qemu-img.c
240
@@ -XXX,XX +XXX,XX @@ static int print_amend_option_help(const char *format)
241
return 1;
242
}
243
244
- /* Every driver supporting amendment must have create_opts */
245
- assert(drv->create_opts);
246
+ /* Every driver supporting amendment must have amend_opts */
247
+ assert(drv->amend_opts);
248
249
printf("Creation options for '%s':\n", format);
250
- qemu_opts_print_help(drv->create_opts, false);
251
+ qemu_opts_print_help(drv->amend_opts, false);
252
printf("\nNote that not all of these options may be amendable.\n");
253
return 0;
254
}
255
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
256
Error *err = NULL;
257
int c, ret = 0;
258
char *options = NULL;
259
- QemuOptsList *create_opts = NULL;
260
+ QemuOptsList *amend_opts = NULL;
261
QemuOpts *opts = NULL;
262
const char *fmt = NULL, *filename, *cache;
263
int flags;
264
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
265
goto out;
50
goto out;
266
}
51
}
267
52
268
- /* Every driver supporting amendment must have create_opts */
53
+ assert(!(flags & ~bs->supported_read_flags));
269
- assert(bs->drv->create_opts);
54
+
270
+ /* Every driver supporting amendment must have amend_opts */
55
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
271
+ assert(bs->drv->amend_opts);
56
if (bytes <= max_bytes && bytes <= max_transfer) {
272
57
- ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, 0);
273
- create_opts = qemu_opts_append(create_opts, bs->drv->create_opts);
58
+ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, flags);
274
- opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
59
goto out;
275
+ amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts);
60
}
276
+ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort);
61
277
qemu_opts_do_parse(opts, options, NULL, &err);
62
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
278
if (err) {
63
279
error_report_err(err);
64
ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
280
@@ -XXX,XX +XXX,XX @@ out:
65
num, qiov,
281
out_no_progress:
66
- qiov_offset + bytes - bytes_remaining, 0);
282
blk_unref(blk);
67
+ qiov_offset + bytes - bytes_remaining,
283
qemu_opts_del(opts);
68
+ flags);
284
- qemu_opts_free(create_opts);
69
max_bytes -= num;
285
+ qemu_opts_free(amend_opts);
70
} else {
286
g_free(options);
71
num = bytes_remaining;
287
288
if (ret) {
289
--
72
--
290
2.26.2
73
2.29.2
291
74
292
75
diff view generated by jsdifflib
New patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
2
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
New patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
2
3
Stream in stream_prepare calls bdrv_change_backing_file() to change
4
backing-file in the metadata of bs.
5
6
It may use either backing-file parameter given by user or just take
7
filename of base on job start.
8
9
Backing file format is determined by base on job finish.
10
11
There are some problems with this design, we solve only two by this
12
patch:
13
14
1. Consider scenario with backing-file unset. Current concept of stream
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>
31
Signed-off-by: Max Reitz <mreitz@redhat.com>
32
---
33
block/stream.c | 9 +++++----
34
blockdev.c | 8 +-------
35
2 files changed, 6 insertions(+), 11 deletions(-)
36
37
diff --git a/block/stream.c b/block/stream.c
38
index XXXXXXX..XXXXXXX 100644
39
--- a/block/stream.c
40
+++ b/block/stream.c
41
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
42
BlockDriverState *bs = blk_bs(bjob->blk);
43
BlockDriverState *unfiltered_bs = bdrv_skip_filters(bs);
44
BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base);
45
+ BlockDriverState *unfiltered_base = bdrv_skip_filters(base);
46
Error *local_err = NULL;
47
int ret = 0;
48
49
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
50
51
if (bdrv_cow_child(unfiltered_bs)) {
52
const char *base_id = NULL, *base_fmt = NULL;
53
- if (base) {
54
- base_id = s->backing_file_str;
55
- if (base->drv) {
56
- base_fmt = base->drv->format_name;
57
+ if (unfiltered_base) {
58
+ base_id = s->backing_file_str ?: unfiltered_base->filename;
59
+ if (unfiltered_base->drv) {
60
+ base_fmt = unfiltered_base->drv->format_name;
61
}
62
}
63
bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
64
diff --git a/blockdev.c b/blockdev.c
65
index XXXXXXX..XXXXXXX 100644
66
--- a/blockdev.c
67
+++ b/blockdev.c
68
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
69
BlockDriverState *base_bs = NULL;
70
AioContext *aio_context;
71
Error *local_err = NULL;
72
- const char *base_name = NULL;
73
int job_flags = JOB_DEFAULT;
74
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
--
112
2.29.2
113
114
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
blockdev-amend will be used similiar to blockdev-create
3
The code already don't freeze base node and we try to make it prepared
4
to allow on the fly changes of the structure of the format based block devices.
4
for the situation when base node is changed during the operation. In
5
5
other words, block-stream doesn't own base node.
6
Current plan is to first support encryption keyslot management for luks
6
7
based formats (raw and embedded in qcow2)
7
Let's introduce a new interface which should replace the current one,
8
8
which will in better relations with the code. Specifying bottom node
9
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
9
instead of base, and requiring it to be non-filter gives us the
10
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
10
following benefits:
11
Message-Id: <20200608094030.670121-12-mlevitsk@redhat.com>
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
23
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
24
Message-Id: <20201216061703.70908-11-vsementsov@virtuozzo.com>
25
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
27
---
14
qapi/block-core.json | 42 ++++++++++++++
28
qapi/block-core.json | 12 ++++---
15
qapi/job.json | 4 +-
29
include/block/block_int.h | 1 +
16
include/block/block_int.h | 21 +++++--
30
block/monitor/block-hmp-cmds.c | 3 +-
17
block/amend.c | 113 ++++++++++++++++++++++++++++++++++++++
31
block/stream.c | 50 +++++++++++++++++++---------
18
block/Makefile.objs | 2 +-
32
blockdev.c | 59 ++++++++++++++++++++++++++++------
19
5 files changed, 174 insertions(+), 8 deletions(-)
33
5 files changed, 94 insertions(+), 31 deletions(-)
20
create mode 100644 block/amend.c
21
34
22
diff --git a/qapi/block-core.json b/qapi/block-core.json
35
diff --git a/qapi/block-core.json b/qapi/block-core.json
23
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
24
--- a/qapi/block-core.json
37
--- a/qapi/block-core.json
25
+++ b/qapi/block-core.json
38
+++ b/qapi/block-core.json
26
@@ -XXX,XX +XXX,XX @@
39
@@ -XXX,XX +XXX,XX @@
27
'data': { 'job-id': 'str',
40
# @device: the device or node name of the top image
28
'options': 'BlockdevCreateOptions' } }
41
#
29
42
# @base: the common backing file name.
30
+##
43
-# It cannot be set if @base-node is also set.
31
+# @BlockdevAmendOptions:
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)
32
+#
49
+#
33
+# Options for amending an image format
50
+# @bottom: the last node in the chain that should be streamed into
34
+#
51
+# top. It cannot be set if @base or @base-node is also set.
35
+# @driver: Block driver of the node to amend.
52
+# It cannot be filter node. (Since 6.0)
36
+#
53
#
37
+# Since: 5.1
54
# @backing-file: The backing file string to write into the top
38
+##
55
# image. This filename is not validated.
39
+{ 'union': 'BlockdevAmendOptions',
56
@@ -XXX,XX +XXX,XX @@
40
+ 'base': {
41
+ 'driver': 'BlockdevDriver' },
42
+ 'discriminator': 'driver',
43
+ 'data': {
44
+ } }
45
+
46
+##
47
+# @x-blockdev-amend:
48
+#
49
+# Starts a job to amend format specific options of an existing open block device
50
+# The job is automatically finalized, but a manual job-dismiss is required.
51
+#
52
+# @job-id: Identifier for the newly created job.
53
+#
54
+# @node-name: Name of the block node to work on
55
+#
56
+# @options: Options (driver specific)
57
+#
58
+# @force: Allow unsafe operations, format specific
59
+# For luks that allows erase of the last active keyslot
60
+# (permanent loss of data),
61
+# and replacement of an active keyslot
62
+# (possible loss of data if IO error happens)
63
+#
64
+# Since: 5.1
65
+##
66
+{ 'command': 'x-blockdev-amend',
67
+ 'data': { 'job-id': 'str',
68
+ 'node-name': 'str',
69
+ 'options': 'BlockdevAmendOptions',
70
+ '*force': 'bool' } }
71
+
72
##
57
##
73
# @BlockErrorAction:
58
{ 'command': 'block-stream',
74
#
59
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
75
diff --git a/qapi/job.json b/qapi/job.json
60
- '*base-node': 'str', '*backing-file': 'str', '*speed': 'int',
76
index XXXXXXX..XXXXXXX 100644
61
- '*on-error': 'BlockdevOnError',
77
--- a/qapi/job.json
62
+ '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str',
78
+++ b/qapi/job.json
63
+ '*speed': 'int', '*on-error': 'BlockdevOnError',
79
@@ -XXX,XX +XXX,XX @@
64
'*filter-node-name': 'str',
80
#
65
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
81
# @create: image creation job type, see "blockdev-create" (since 3.0)
66
82
#
83
+# @amend: image options amend job type, see "x-blockdev-amend" (since 5.1)
84
+#
85
# Since: 1.7
86
##
87
{ 'enum': 'JobType',
88
- 'data': ['commit', 'stream', 'mirror', 'backup', 'create'] }
89
+ 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend'] }
90
91
##
92
# @JobStatus:
93
diff --git a/include/block/block_int.h b/include/block/block_int.h
67
diff --git a/include/block/block_int.h b/include/block/block_int.h
94
index XXXXXXX..XXXXXXX 100644
68
index XXXXXXX..XXXXXXX 100644
95
--- a/include/block/block_int.h
69
--- a/include/block/block_int.h
96
+++ b/include/block/block_int.h
70
+++ b/include/block/block_int.h
97
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
71
@@ -XXX,XX +XXX,XX @@ int is_windows_drive(const char *filename);
98
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
72
*/
99
Error **errp);
73
void stream_start(const char *job_id, BlockDriverState *bs,
100
void (*bdrv_close)(BlockDriverState *bs);
74
BlockDriverState *base, const char *backing_file_str,
101
+
75
+ BlockDriverState *bottom,
102
+
76
int creation_flags, int64_t speed,
103
int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts,
77
BlockdevOnError on_error,
104
Error **errp);
78
const char *filter_node_name,
105
int coroutine_fn (*bdrv_co_create_opts)(BlockDriver *drv,
79
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
106
const char *filename,
80
index XXXXXXX..XXXXXXX 100644
107
QemuOpts *opts,
81
--- a/block/monitor/block-hmp-cmds.c
108
Error **errp);
82
+++ b/block/monitor/block-hmp-cmds.c
109
+
83
@@ -XXX,XX +XXX,XX @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
110
+ int coroutine_fn (*bdrv_co_amend)(BlockDriverState *bs,
84
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
111
+ BlockdevAmendOptions *opts,
85
112
+ bool force,
86
qmp_block_stream(true, device, device, base != NULL, base, false, NULL,
113
+ Error **errp);
87
- false, NULL, qdict_haskey(qdict, "speed"), speed, true,
114
+
88
+ false, NULL, false, NULL,
115
+ int (*bdrv_amend_options)(BlockDriverState *bs,
89
+ qdict_haskey(qdict, "speed"), speed, true,
116
+ QemuOpts *opts,
90
BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false,
117
+ BlockDriverAmendStatusCB *status_cb,
91
false, &error);
118
+ void *cb_opaque,
92
119
+ bool force,
93
diff --git a/block/stream.c b/block/stream.c
120
+ Error **errp);
94
index XXXXXXX..XXXXXXX 100644
121
+
95
--- a/block/stream.c
122
int (*bdrv_make_empty)(BlockDriverState *bs);
96
+++ b/block/stream.c
123
97
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
124
/*
98
125
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
99
void stream_start(const char *job_id, BlockDriverState *bs,
126
BdrvCheckResult *result,
100
BlockDriverState *base, const char *backing_file_str,
127
BdrvCheckMode fix);
101
+ BlockDriverState *bottom,
128
102
int creation_flags, int64_t speed,
129
- int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
103
BlockdevOnError on_error,
130
- BlockDriverAmendStatusCB *status_cb,
104
const char *filter_node_name,
131
- void *cb_opaque,
105
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
132
- bool force,
106
BlockDriverState *iter;
133
- Error **errp);
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
165
index XXXXXXX..XXXXXXX 100644
166
--- a/blockdev.c
167
+++ b/blockdev.c
168
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
169
bool has_base, const char *base,
170
bool has_base_node, const char *base_node,
171
bool has_backing_file, const char *backing_file,
172
+ bool has_bottom, const char *bottom,
173
bool has_speed, int64_t speed,
174
bool has_on_error, BlockdevOnError on_error,
175
bool has_filter_node_name, const char *filter_node_name,
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
}
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
- }
134
-
218
-
135
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
219
if (has_base) {
136
220
base_bs = bdrv_find_backing_image(bs, base);
137
/* TODO Better pass a option string/QDict/QemuOpts to add any rule? */
221
if (base_bs == NULL) {
138
diff --git a/block/amend.c b/block/amend.c
222
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
139
new file mode 100644
223
bdrv_refresh_filename(base_bs);
140
index XXXXXXX..XXXXXXX
224
}
141
--- /dev/null
225
142
+++ b/block/amend.c
226
- /* Check for op blockers in the whole chain between bs and base */
143
@@ -XXX,XX +XXX,XX @@
227
- for (iter = bs; iter && iter != base_bs;
144
+/*
228
+ if (has_bottom) {
145
+ * Block layer code related to image options amend
229
+ bottom_bs = bdrv_lookup_bs(NULL, bottom, errp);
146
+ *
230
+ if (!bottom_bs) {
147
+ * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
231
+ goto out;
148
+ * Copyright (c) 2020 Red Hat. Inc
232
+ }
149
+ *
233
+ if (!bottom_bs->drv) {
150
+ * Heavily based on create.c
234
+ error_setg(errp, "Node '%s' is not open", bottom);
151
+ *
235
+ goto out;
152
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
236
+ }
153
+ * of this software and associated documentation files (the "Software"), to deal
237
+ if (bottom_bs->drv->is_filter) {
154
+ * in the Software without restriction, including without limitation the rights
238
+ error_setg(errp, "Node '%s' is a filter, use a non-filter node "
155
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
239
+ "as 'bottom'", bottom);
156
+ * copies of the Software, and to permit persons to whom the Software is
240
+ goto out;
157
+ * furnished to do so, subject to the following conditions:
241
+ }
158
+ *
242
+ if (!bdrv_chain_contains(bs, bottom_bs)) {
159
+ * The above copyright notice and this permission notice shall be included in
243
+ error_setg(errp, "Node '%s' is not in a chain starting from '%s'",
160
+ * all copies or substantial portions of the Software.
244
+ bottom, device);
161
+ *
245
+ goto out;
162
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
246
+ }
163
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
247
+ assert(bdrv_get_aio_context(bottom_bs) == aio_context);
164
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
165
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
166
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
167
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
168
+ * THE SOFTWARE.
169
+ */
170
+
171
+#include "qemu/osdep.h"
172
+#include "block/block_int.h"
173
+#include "qemu/job.h"
174
+#include "qemu/main-loop.h"
175
+#include "qapi/qapi-commands-block-core.h"
176
+#include "qapi/qapi-visit-block-core.h"
177
+#include "qapi/clone-visitor.h"
178
+#include "qapi/error.h"
179
+
180
+typedef struct BlockdevAmendJob {
181
+ Job common;
182
+ BlockdevAmendOptions *opts;
183
+ BlockDriverState *bs;
184
+ bool force;
185
+} BlockdevAmendJob;
186
+
187
+static int coroutine_fn blockdev_amend_run(Job *job, Error **errp)
188
+{
189
+ BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common);
190
+ int ret;
191
+
192
+ job_progress_set_remaining(&s->common, 1);
193
+ ret = s->bs->drv->bdrv_co_amend(s->bs, s->opts, s->force, errp);
194
+ job_progress_update(&s->common, 1);
195
+ qapi_free_BlockdevAmendOptions(s->opts);
196
+ return ret;
197
+}
198
+
199
+static const JobDriver blockdev_amend_job_driver = {
200
+ .instance_size = sizeof(BlockdevAmendJob),
201
+ .job_type = JOB_TYPE_AMEND,
202
+ .run = blockdev_amend_run,
203
+};
204
+
205
+void qmp_x_blockdev_amend(const char *job_id,
206
+ const char *node_name,
207
+ BlockdevAmendOptions *options,
208
+ bool has_force,
209
+ bool force,
210
+ Error **errp)
211
+{
212
+ BlockdevAmendJob *s;
213
+ const char *fmt = BlockdevDriver_str(options->driver);
214
+ BlockDriver *drv = bdrv_find_format(fmt);
215
+ BlockDriverState *bs = bdrv_find_node(node_name);
216
+
217
+
218
+ if (!drv) {
219
+ error_setg(errp, "Block driver '%s' not found or not supported", fmt);
220
+ return;
221
+ }
248
+ }
222
+
249
+
223
+ /*
250
+ /*
224
+ * If the driver is in the schema, we know that it exists. But it may not
251
+ * Check for op blockers in the whole chain between bs and base (or bottom)
225
+ * be whitelisted.
226
+ */
252
+ */
227
+ if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
253
+ iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
228
+ error_setg(errp, "Driver is not whitelisted");
254
+ for (iter = bs; iter && iter != iter_end;
229
+ return;
255
iter = bdrv_filter_or_cow_bs(iter))
230
+ }
256
{
231
+
257
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
232
+ if (bs->drv != drv) {
258
@@ -XXX,XX +XXX,XX @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
233
+ error_setg(errp,
259
}
234
+ "x-blockdev-amend doesn't support changing the block driver");
260
235
+ return;
261
stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
236
+ }
262
- job_flags, has_speed ? speed : 0, on_error,
237
+
263
+ bottom_bs, job_flags, has_speed ? speed : 0, on_error,
238
+ /* Error out if the driver doesn't support .bdrv_co_amend */
264
filter_node_name, &local_err);
239
+ if (!drv->bdrv_co_amend) {
265
if (local_err) {
240
+ error_setg(errp, "Driver does not support x-blockdev-amend");
266
error_propagate(errp, local_err);
241
+ return;
242
+ }
243
+
244
+ /* Create the block job */
245
+ s = job_create(job_id, &blockdev_amend_job_driver, NULL,
246
+ bdrv_get_aio_context(bs), JOB_DEFAULT | JOB_MANUAL_DISMISS,
247
+ NULL, NULL, errp);
248
+ if (!s) {
249
+ return;
250
+ }
251
+
252
+ s->bs = bs,
253
+ s->opts = QAPI_CLONE(BlockdevAmendOptions, options),
254
+ s->force = has_force ? force : false;
255
+ job_start(&s->common);
256
+}
257
diff --git a/block/Makefile.objs b/block/Makefile.objs
258
index XXXXXXX..XXXXXXX 100644
259
--- a/block/Makefile.objs
260
+++ b/block/Makefile.objs
261
@@ -XXX,XX +XXX,XX @@ block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
262
block-obj-$(CONFIG_POSIX) += file-posix.o
263
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
264
block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o
265
-block-obj-y += null.o mirror.o commit.o io.o create.o
266
+block-obj-y += null.o mirror.o commit.o io.o create.o amend.o
267
block-obj-y += throttle-groups.o
268
block-obj-$(CONFIG_LINUX) += nvme.o
269
270
--
267
--
271
2.26.2
268
2.29.2
272
269
273
270
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
3
This patch completes the series with the COR-filter applied to
4
block-stream operations.
5
6
Adding the filter makes it possible in future implement discarding
7
copied regions in backing files during the block-stream job, to reduce
8
the disk overuse (we need control on permissions).
9
10
Also, the filter now is smart enough to do copy-on-read with specified
11
base, so we have benefit on guest reads even when doing block-stream of
12
the part of the backing chain.
13
14
Several iotests are slightly modified due to filter insertion.
15
16
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
17
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
Message-Id: <20201216061703.70908-14-vsementsov@virtuozzo.com>
19
Reviewed-by: Max Reitz <mreitz@redhat.com>
20
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
---
22
block/stream.c | 105 ++++++++++++++++++++++---------------
23
tests/qemu-iotests/030 | 8 +--
24
tests/qemu-iotests/141.out | 2 +-
25
tests/qemu-iotests/245 | 20 ++++---
26
4 files changed, 80 insertions(+), 55 deletions(-)
27
28
diff --git a/block/stream.c b/block/stream.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/block/stream.c
31
+++ b/block/stream.c
32
@@ -XXX,XX +XXX,XX @@
33
#include "block/blockjob_int.h"
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
}
109
job_progress_set_remaining(&s->common.job, len);
110
111
- /* Turn on copy-on-read for the whole block device so that guest read
112
- * requests help us make progress. Only do this when copying the entire
113
- * backing chain since the copy-on-read operation does not take base into
114
- * account.
115
- */
116
- if (enable_cor) {
117
- bdrv_enable_copy_on_read(s->target_bs);
118
- }
119
-
120
for ( ; offset < len; offset += n) {
121
bool copy;
122
int ret;
123
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
124
}
125
}
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
--
331
2.29.2
332
333
diff view generated by jsdifflib
New 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).
1
6
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
New 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.)
1
5
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
New 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(-)
1
10
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
New 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(+)
1
9
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
New 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).
1
4
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
New 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.
1
4
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
New 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.
1
4
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
New 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).
1
5
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
New patch
1
And consequentially drop it from 297's skip list.
1
2
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
New patch
1
And consequentially drop it from 297's skip list.
1
2
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
New 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.
1
7
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
3
Experiments show, that copy_range is not always making things faster.
4
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
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>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
28
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Message-Id: <20200608094030.670121-13-mlevitsk@redhat.com>
29
Message-Id: <20210116214705.822267-2-vsementsov@virtuozzo.com>
30
[mreitz: s/5\.2/6.0/]
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
31
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
---
32
---
9
qapi/block-core.json | 14 ++++++++-
33
qapi/block-core.json | 17 ++++++++++++++++-
10
block/crypto.c | 72 ++++++++++++++++++++++++++++++++------------
34
block/backup-top.h | 1 +
11
2 files changed, 66 insertions(+), 20 deletions(-)
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(-)
12
43
13
diff --git a/qapi/block-core.json b/qapi/block-core.json
44
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
15
--- a/qapi/block-core.json
46
--- a/qapi/block-core.json
16
+++ b/qapi/block-core.json
47
+++ b/qapi/block-core.json
17
@@ -XXX,XX +XXX,XX @@
48
@@ -XXX,XX +XXX,XX @@
18
'data': { 'job-id': 'str',
49
{ 'struct': 'BlockdevSnapshot',
19
'options': 'BlockdevCreateOptions' } }
50
'data': { 'node': 'str', 'overlay': 'str' } }
20
51
21
+##
52
+##
22
+# @BlockdevAmendOptionsLUKS:
53
+# @BackupPerf:
23
+#
54
+#
24
+# Driver specific image amend options for LUKS.
55
+# Optional parameters for backup. These parameters don't affect
25
+#
56
+# functionality, but may significantly affect performance.
26
+# Since: 5.1
57
+#
58
+# @use-copy-range: Use copy offloading. Default true.
59
+#
60
+# Since: 6.0
27
+##
61
+##
28
+{ 'struct': 'BlockdevAmendOptionsLUKS',
62
+{ 'struct': 'BackupPerf',
29
+ 'base': 'QCryptoBlockAmendOptionsLUKS',
63
+ 'data': { '*use-copy-range': 'bool' }}
30
+ 'data': { }
31
+}
32
+
64
+
33
##
65
##
34
# @BlockdevAmendOptions:
66
# @BackupCommon:
35
#
67
#
36
@@ -XXX,XX +XXX,XX @@
68
@@ -XXX,XX +XXX,XX @@
37
'driver': 'BlockdevDriver' },
69
# above node specified by @drive. If this option is not given,
38
'discriminator': 'driver',
70
# a node name is autogenerated. (Since: 4.2)
39
'data': {
71
#
40
- } }
72
+# @x-perf: Performance options. (Since 6.0)
41
+ 'luks': 'BlockdevAmendOptionsLUKS' } }
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' } }
42
83
43
##
84
##
44
# @x-blockdev-amend:
85
# @DriveBackup:
45
diff --git a/block/crypto.c b/block/crypto.c
86
diff --git a/block/backup-top.h b/block/backup-top.h
46
index XXXXXXX..XXXXXXX 100644
87
index XXXXXXX..XXXXXXX 100644
47
--- a/block/crypto.c
88
--- a/block/backup-top.h
48
+++ b/block/crypto.c
89
+++ b/block/backup-top.h
49
@@ -XXX,XX +XXX,XX @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
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)
50
}
197
}
51
198
52
static int
199
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
53
-block_crypto_amend_options_luks(BlockDriverState *bs,
200
- int64_t cluster_size,
54
- QemuOpts *opts,
201
+ int64_t cluster_size, bool use_copy_range,
55
- BlockDriverAmendStatusCB *status_cb,
202
BdrvRequestFlags write_flags, Error **errp)
56
- void *cb_opaque,
57
- bool force,
58
- Error **errp)
59
+block_crypto_amend_options_generic_luks(BlockDriverState *bs,
60
+ QCryptoBlockAmendOptions *amend_options,
61
+ bool force,
62
+ Error **errp)
63
{
203
{
64
BlockCrypto *crypto = bs->opaque;
204
BlockCopyState *s;
65
- QDict *cryptoopts = NULL;
205
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
66
- QCryptoBlockAmendOptions *amend_options = NULL;
206
* We enable copy-range, but keep small copy_size, until first
67
int ret;
207
* successful copy_range (look at block_copy_do_copy).
68
208
*/
69
assert(crypto);
209
- s->use_copy_range = true;
70
assert(crypto->block);
210
+ s->use_copy_range = use_copy_range;
71
- crypto->updating_keys = true;
211
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
72
73
+ /* apply for exclusive read/write permissions to the underlying file*/
74
+ crypto->updating_keys = true;
75
ret = bdrv_child_refresh_perms(bs, bs->file, errp);
76
- if (ret < 0) {
77
- goto cleanup;
78
- }
79
-
80
- cryptoopts = qemu_opts_to_qdict(opts, NULL);
81
- qdict_put_str(cryptoopts, "format", "luks");
82
- amend_options = block_crypto_amend_opts_init(cryptoopts, errp);
83
- if (!amend_options) {
84
- ret = -EINVAL;
85
+ if (ret) {
86
goto cleanup;
87
}
212
}
88
213
89
@@ -XXX,XX +XXX,XX @@ block_crypto_amend_options_luks(BlockDriverState *bs,
214
diff --git a/block/replication.c b/block/replication.c
90
force,
215
index XXXXXXX..XXXXXXX 100644
91
errp);
216
--- a/block/replication.c
92
cleanup:
217
+++ b/block/replication.c
93
+ /* release exclusive read/write permissions to the underlying file*/
218
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
94
crypto->updating_keys = false;
219
int64_t active_length, hidden_length, disk_length;
95
bdrv_child_refresh_perms(bs, bs->file, errp);
220
AioContext *aio_context;
96
- qapi_free_QCryptoBlockAmendOptions(amend_options);
221
Error *local_err = NULL;
97
+ return ret;
222
+ BackupPerf perf = { .use_copy_range = true };
98
+}
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
+ }
99
+
255
+
100
+static int
256
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
101
+block_crypto_amend_options_luks(BlockDriverState *bs,
257
(backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
102
+ QemuOpts *opts,
258
/* done before desugaring 'incremental' to print the right message */
103
+ BlockDriverAmendStatusCB *status_cb,
259
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
104
+ void *cb_opaque,
260
backup->sync, bmap, backup->bitmap_mode,
105
+ bool force,
261
backup->compress,
106
+ Error **errp)
262
backup->filter_node_name,
107
+{
263
+ &perf,
108
+ BlockCrypto *crypto = bs->opaque;
264
backup->on_source_error,
109
+ QDict *cryptoopts = NULL;
265
backup->on_target_error,
110
+ QCryptoBlockAmendOptions *amend_options = NULL;
266
job_flags, NULL, NULL, txn, errp);
111
+ int ret = -EINVAL;
112
+
113
+ assert(crypto);
114
+ assert(crypto->block);
115
+
116
+ cryptoopts = qemu_opts_to_qdict(opts, NULL);
117
+ qdict_put_str(cryptoopts, "format", "luks");
118
+ amend_options = block_crypto_amend_opts_init(cryptoopts, errp);
119
qobject_unref(cryptoopts);
120
+ if (!amend_options) {
121
+ goto cleanup;
122
+ }
123
+ ret = block_crypto_amend_options_generic_luks(bs, amend_options,
124
+ force, errp);
125
+cleanup:
126
+ qapi_free_QCryptoBlockAmendOptions(amend_options);
127
return ret;
128
}
129
130
+static int
131
+coroutine_fn block_crypto_co_amend_luks(BlockDriverState *bs,
132
+ BlockdevAmendOptions *opts,
133
+ bool force,
134
+ Error **errp)
135
+{
136
+ QCryptoBlockAmendOptions amend_opts;
137
+
138
+ amend_opts = (QCryptoBlockAmendOptions) {
139
+ .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
140
+ .u.luks = *qapi_BlockdevAmendOptionsLUKS_base(&opts->u.luks),
141
+ };
142
+ return block_crypto_amend_options_generic_luks(bs, &amend_opts,
143
+ force, errp);
144
+}
145
146
static void
147
block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
148
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_crypto_luks = {
149
.bdrv_get_info = block_crypto_get_info_luks,
150
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
151
.bdrv_amend_options = block_crypto_amend_options_luks,
152
+ .bdrv_co_amend = block_crypto_co_amend_luks,
153
154
.is_format = true,
155
156
--
267
--
157
2.26.2
268
2.29.2
158
269
159
270
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This will be used first to implement luks keyslot management.
3
Refactor common path to use BlockCopyCallState pointer as parameter, to
4
prepare it for use in asynchronous block-copy (at least, we'll need to
5
run block-copy in a coroutine, passing the whole parameters as one
6
pointer).
4
7
5
block_crypto_amend_opts_init will be used to convert
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
qemu-img cmdline to QCryptoBlockAmendOptions
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
10
Message-Id: <20210116214705.822267-3-vsementsov@virtuozzo.com>
8
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
9
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
10
Message-Id: <20200608094030.670121-2-mlevitsk@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
12
---
13
qapi/crypto.json | 16 ++++++++++++++++
13
block/block-copy.c | 51 ++++++++++++++++++++++++++++++++++------------
14
block/crypto.h | 3 +++
14
1 file changed, 38 insertions(+), 13 deletions(-)
15
crypto/blockpriv.h | 8 ++++++++
16
include/crypto/block.h | 22 ++++++++++++++++++++++
17
block/crypto.c | 17 +++++++++++++++++
18
crypto/block.c | 29 +++++++++++++++++++++++++++++
19
6 files changed, 95 insertions(+)
20
15
21
diff --git a/qapi/crypto.json b/qapi/crypto.json
16
diff --git a/block/block-copy.c b/block/block-copy.c
22
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/crypto.json
18
--- a/block/block-copy.c
24
+++ b/qapi/crypto.json
19
+++ b/block/block-copy.c
25
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@
26
'base': 'QCryptoBlockInfoBase',
21
static coroutine_fn int block_copy_task_entry(AioTask *task);
27
'discriminator': 'format',
22
28
'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
23
typedef struct BlockCopyCallState {
24
+ /* IN parameters */
25
+ BlockCopyState *s;
26
+ int64_t offset;
27
+ int64_t bytes;
29
+
28
+
29
+ /* State */
30
bool failed;
30
+
31
+
32
+ /* OUT parameters */
33
bool error_is_read;
34
} BlockCopyCallState;
35
36
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
37
* Returns 1 if dirty clusters found and successfully copied, 0 if no dirty
38
* clusters found and -errno on failure.
39
*/
40
-static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
41
- int64_t offset, int64_t bytes,
42
- bool *error_is_read)
43
+static int coroutine_fn
44
+block_copy_dirty_clusters(BlockCopyCallState *call_state)
45
{
46
+ BlockCopyState *s = call_state->s;
47
+ int64_t offset = call_state->offset;
48
+ int64_t bytes = call_state->bytes;
31
+
49
+
32
+##
50
int ret = 0;
33
+# @QCryptoBlockAmendOptions:
51
bool found_dirty = false;
34
+#
52
int64_t end = offset + bytes;
35
+# The options that are available for all encryption formats
53
AioTaskPool *aio = NULL;
36
+# when amending encryption settings
54
- BlockCopyCallState call_state = {false, false};
37
+#
55
38
+# Since: 5.1
56
/*
39
+##
57
* block_copy() user is responsible for keeping source and target in same
40
+{ 'union': 'QCryptoBlockAmendOptions',
58
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
41
+ 'base': 'QCryptoBlockOptionsBase',
59
BlockCopyTask *task;
42
+ 'discriminator': 'format',
60
int64_t status_bytes;
43
+ 'data': {
61
44
+ } }
62
- task = block_copy_task_create(s, &call_state, offset, bytes);
45
diff --git a/block/crypto.h b/block/crypto.h
63
+ task = block_copy_task_create(s, call_state, offset, bytes);
46
index XXXXXXX..XXXXXXX 100644
64
if (!task) {
47
--- a/block/crypto.h
65
/* No more dirty bits in the bitmap */
48
+++ b/block/crypto.h
66
trace_block_copy_skip_range(s, offset, bytes);
49
@@ -XXX,XX +XXX,XX @@
67
@@ -XXX,XX +XXX,XX @@ out:
50
QCryptoBlockCreateOptions *
68
51
block_crypto_create_opts_init(QDict *opts, Error **errp);
69
aio_task_pool_free(aio);
52
70
}
53
+QCryptoBlockAmendOptions *
71
- if (error_is_read && ret < 0) {
54
+block_crypto_amend_opts_init(QDict *opts, Error **errp);
72
- *error_is_read = call_state.error_is_read;
55
+
73
- }
56
QCryptoBlockOpenOptions *
74
57
block_crypto_open_opts_init(QDict *opts, Error **errp);
75
return ret < 0 ? ret : found_dirty;
58
76
}
59
diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
77
60
index XXXXXXX..XXXXXXX 100644
78
/*
61
--- a/crypto/blockpriv.h
79
- * block_copy
62
+++ b/crypto/blockpriv.h
80
+ * block_copy_common
63
@@ -XXX,XX +XXX,XX @@ struct QCryptoBlockDriver {
81
*
64
void *opaque,
82
* Copy requested region, accordingly to dirty bitmap.
65
Error **errp);
83
* Collaborate with parallel block_copy requests: if they succeed it will help
66
84
@@ -XXX,XX +XXX,XX @@ out:
67
+ int (*amend)(QCryptoBlock *block,
85
* it means that some I/O operation failed in context of _this_ block_copy call,
68
+ QCryptoBlockReadFunc readfunc,
86
* not some parallel operation.
69
+ QCryptoBlockWriteFunc writefunc,
87
*/
70
+ void *opaque,
88
-int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
71
+ QCryptoBlockAmendOptions *options,
89
- bool *error_is_read)
72
+ bool force,
90
+static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
73
+ Error **errp);
91
{
74
+
92
int ret;
75
int (*get_info)(QCryptoBlock *block,
93
76
QCryptoBlockInfo *info,
94
do {
77
Error **errp);
95
- ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read);
78
diff --git a/include/crypto/block.h b/include/crypto/block.h
96
+ ret = block_copy_dirty_clusters(call_state);
79
index XXXXXXX..XXXXXXX 100644
97
80
--- a/include/crypto/block.h
98
if (ret == 0) {
81
+++ b/include/crypto/block.h
99
- ret = block_copy_wait_one(s, offset, bytes);
82
@@ -XXX,XX +XXX,XX @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
100
+ ret = block_copy_wait_one(call_state->s, call_state->offset,
83
void *opaque,
101
+ call_state->bytes);
84
Error **errp);
102
}
85
103
86
+/**
104
/*
87
+ * qcrypto_block_amend_options:
105
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
88
+ * @block: the block encryption object
89
+ *
90
+ * @readfunc: callback for reading data from the volume header
91
+ * @writefunc: callback for writing data to the volume header
92
+ * @opaque: data to pass to @readfunc and @writefunc
93
+ * @options: the new/amended encryption options
94
+ * @force: hint for the driver to allow unsafe operation
95
+ * @errp: error pointer
96
+ *
97
+ * Changes the crypto options of the encryption format
98
+ *
99
+ */
100
+int qcrypto_block_amend_options(QCryptoBlock *block,
101
+ QCryptoBlockReadFunc readfunc,
102
+ QCryptoBlockWriteFunc writefunc,
103
+ void *opaque,
104
+ QCryptoBlockAmendOptions *options,
105
+ bool force,
106
+ Error **errp);
107
+
108
109
/**
110
* qcrypto_block_calculate_payload_offset:
111
diff --git a/block/crypto.c b/block/crypto.c
112
index XXXXXXX..XXXXXXX 100644
113
--- a/block/crypto.c
114
+++ b/block/crypto.c
115
@@ -XXX,XX +XXX,XX @@ block_crypto_create_opts_init(QDict *opts, Error **errp)
116
return ret;
106
return ret;
117
}
107
}
118
108
119
+QCryptoBlockAmendOptions *
109
+int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
120
+block_crypto_amend_opts_init(QDict *opts, Error **errp)
110
+ bool *error_is_read)
121
+{
111
+{
122
+ Visitor *v;
112
+ BlockCopyCallState call_state = {
123
+ QCryptoBlockAmendOptions *ret;
113
+ .s = s,
114
+ .offset = start,
115
+ .bytes = bytes,
116
+ };
124
+
117
+
125
+ v = qobject_input_visitor_new_flat_confused(opts, errp);
118
+ int ret = block_copy_common(&call_state);
126
+ if (!v) {
119
+
127
+ return NULL;
120
+ if (error_is_read && ret < 0) {
121
+ *error_is_read = call_state.error_is_read;
128
+ }
122
+ }
129
+
123
+
130
+ visit_type_QCryptoBlockAmendOptions(v, NULL, &ret, errp);
131
+
132
+ visit_free(v);
133
+ return ret;
124
+ return ret;
134
+}
125
+}
135
+
126
+
136
127
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
137
static int block_crypto_open_generic(QCryptoBlockFormat format,
128
{
138
QemuOptsList *opts_spec,
129
return s->copy_bitmap;
139
diff --git a/crypto/block.c b/crypto/block.c
140
index XXXXXXX..XXXXXXX 100644
141
--- a/crypto/block.c
142
+++ b/crypto/block.c
143
@@ -XXX,XX +XXX,XX @@ qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts,
144
return crypto != NULL;
145
}
146
147
+int qcrypto_block_amend_options(QCryptoBlock *block,
148
+ QCryptoBlockReadFunc readfunc,
149
+ QCryptoBlockWriteFunc writefunc,
150
+ void *opaque,
151
+ QCryptoBlockAmendOptions *options,
152
+ bool force,
153
+ Error **errp)
154
+{
155
+ if (options->format != block->format) {
156
+ error_setg(errp,
157
+ "Cannot amend encryption format");
158
+ return -1;
159
+ }
160
+
161
+ if (!block->driver->amend) {
162
+ error_setg(errp,
163
+ "Crypto format %s doesn't support format options amendment",
164
+ QCryptoBlockFormat_str(block->format));
165
+ return -1;
166
+ }
167
+
168
+ return block->driver->amend(block,
169
+ readfunc,
170
+ writefunc,
171
+ opaque,
172
+ options,
173
+ force,
174
+ errp);
175
+}
176
177
QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block,
178
Error **errp)
179
--
130
--
180
2.26.2
131
2.29.2
181
132
182
133
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Next few patches will expose that functionality to the user.
3
We'll need async block-copy invocation to use in backup directly.
4
4
5
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20200608094030.670121-3-mlevitsk@redhat.com>
7
Message-Id: <20210116214705.822267-4-vsementsov@virtuozzo.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
9
---
10
qapi/crypto.json | 59 ++++++-
10
include/block/block-copy.h | 29 ++++++++++++++
11
crypto/block-luks.c | 416 +++++++++++++++++++++++++++++++++++++++++++-
11
block/block-copy.c | 81 ++++++++++++++++++++++++++++++++++++--
12
2 files changed, 469 insertions(+), 6 deletions(-)
12
2 files changed, 106 insertions(+), 4 deletions(-)
13
13
14
diff --git a/qapi/crypto.json b/qapi/crypto.json
14
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
15
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/crypto.json
16
--- a/include/block/block-copy.h
17
+++ b/qapi/crypto.json
17
+++ b/include/block/block-copy.h
18
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@
19
'uuid': 'str',
19
#include "qemu/co-shared-resource.h"
20
'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }}
20
21
21
typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
22
-
22
+typedef void (*BlockCopyAsyncCallbackFunc)(void *opaque);
23
##
23
typedef struct BlockCopyState BlockCopyState;
24
# @QCryptoBlockInfo:
24
+typedef struct BlockCopyCallState BlockCopyCallState;
25
#
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
26
@@ -XXX,XX +XXX,XX @@
66
@@ -XXX,XX +XXX,XX @@
27
'discriminator': 'format',
67
static coroutine_fn int block_copy_task_entry(AioTask *task);
28
'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
68
29
69
typedef struct BlockCopyCallState {
30
+##
70
- /* IN parameters */
31
+# @QCryptoBlockLUKSKeyslotState:
71
+ /* IN parameters. Initialized in block_copy_async() and never changed. */
32
+#
72
BlockCopyState *s;
33
+# Defines state of keyslots that are affected by the update
73
int64_t offset;
34
+#
74
int64_t bytes;
35
+# @active: The slots contain the given password and marked as active
75
+ BlockCopyAsyncCallbackFunc cb;
36
+# @inactive: The slots are erased (contain garbage) and marked as inactive
76
+ void *cb_opaque;
37
+#
38
+# Since: 5.1
39
+##
40
+{ 'enum': 'QCryptoBlockLUKSKeyslotState',
41
+ 'data': [ 'active', 'inactive' ] }
42
+
77
+
43
78
+ /* Coroutine where async block-copy is running */
44
+##
79
+ Coroutine *co;
45
+# @QCryptoBlockAmendOptionsLUKS:
80
46
+#
81
/* State */
47
+# This struct defines the update parameters that activate/de-activate set
82
- bool failed;
48
+# of keyslots
83
+ int ret;
49
+#
84
+ bool finished;
50
+# @state: the desired state of the keyslots
85
51
+#
86
/* OUT parameters */
52
+# @new-secret: The ID of a QCryptoSecret object providing the password to be
87
bool error_is_read;
53
+# written into added active keyslots
88
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
54
+#
89
55
+# @old-secret: Optional (for deactivation only)
90
ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
56
+# If given will deactive all keyslots that
91
&error_is_read);
57
+# match password located in QCryptoSecret with this ID
92
- if (ret < 0 && !t->call_state->failed) {
58
+#
93
- t->call_state->failed = true;
59
+# @iter-time: Optional (for activation only)
94
+ if (ret < 0 && !t->call_state->ret) {
60
+# Number of milliseconds to spend in
95
+ t->call_state->ret = ret;
61
+# PBKDF passphrase processing for the newly activated keyslot.
96
t->call_state->error_is_read = error_is_read;
62
+# Currently defaults to 2000.
97
} else {
63
+#
98
progress_work_done(t->s->progress, t->bytes);
64
+# @keyslot: Optional. ID of the keyslot to activate/deactivate.
99
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
65
+# For keyslot activation, keyslot should not be active already
100
*/
66
+# (this is unsafe to update an active keyslot),
101
} while (ret > 0);
67
+# but possible if 'force' parameter is given.
102
68
+# If keyslot is not given, first free keyslot will be written.
103
+ call_state->finished = true;
69
+#
70
+# For keyslot deactivation, this parameter specifies the exact
71
+# keyslot to deactivate
72
+#
73
+# @secret: Optional. The ID of a QCryptoSecret object providing the
74
+# password to use to retrive current master key.
75
+# Defaults to the same secret that was used to open the image
76
+#
77
+#
78
+# Since 5.1
79
+##
80
+{ 'struct': 'QCryptoBlockAmendOptionsLUKS',
81
+ 'data': { 'state': 'QCryptoBlockLUKSKeyslotState',
82
+ '*new-secret': 'str',
83
+ '*old-secret': 'str',
84
+ '*keyslot': 'int',
85
+ '*iter-time': 'int',
86
+ '*secret': 'str' } }
87
88
##
89
# @QCryptoBlockAmendOptions:
90
@@ -XXX,XX +XXX,XX @@
91
'base': 'QCryptoBlockOptionsBase',
92
'discriminator': 'format',
93
'data': {
94
- } }
95
+ 'luks': 'QCryptoBlockAmendOptionsLUKS' } }
96
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/crypto/block-luks.c
99
+++ b/crypto/block-luks.c
100
@@ -XXX,XX +XXX,XX @@
101
#include "qemu/uuid.h"
102
103
#include "qemu/coroutine.h"
104
+#include "qemu/bitmap.h"
105
106
/*
107
* Reference for the LUKS format implemented here is
108
@@ -XXX,XX +XXX,XX @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
109
110
#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
111
112
+#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS 2000
113
+#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
114
+
104
+
115
static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
105
+ if (call_state->cb) {
116
'L', 'U', 'K', 'S', 0xBA, 0xBE
106
+ call_state->cb(call_state->cb_opaque);
117
};
107
+ }
118
@@ -XXX,XX +XXX,XX @@ struct QCryptoBlockLUKS {
119
120
/* Hash algorithm used in pbkdf2 function */
121
QCryptoHashAlgorithm hash_alg;
122
+
108
+
123
+ /* Name of the secret that was used to open the image */
109
return ret;
124
+ char *secret;
125
};
126
127
128
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
129
Error **errp)
130
{
131
QCryptoBlockLUKS *luks = block->opaque;
132
- QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
133
+ QCryptoBlockLUKSKeySlot *slot;
134
g_autofree uint8_t *splitkey = NULL;
135
size_t splitkeylen;
136
g_autofree uint8_t *slotkey = NULL;
137
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
138
uint64_t iters;
139
int ret = -1;
140
141
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
142
+ slot = &luks->header.key_slots[slot_idx];
143
if (qcrypto_random_bytes(slot->salt,
144
QCRYPTO_BLOCK_LUKS_SALT_LEN,
145
errp) < 0) {
146
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
147
Error **errp)
148
{
149
QCryptoBlockLUKS *luks = block->opaque;
150
- const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
151
+ const QCryptoBlockLUKSKeySlot *slot;
152
g_autofree uint8_t *splitkey = NULL;
153
size_t splitkeylen;
154
g_autofree uint8_t *possiblekey = NULL;
155
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
156
g_autoptr(QCryptoIVGen) ivgen = NULL;
157
size_t niv;
158
159
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
160
+ slot = &luks->header.key_slots[slot_idx];
161
if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
162
return 0;
163
}
164
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
165
return -1;
166
}
110
}
167
111
168
+/*
112
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
169
+ * Returns true if a slot i is marked as active
113
return ret;
170
+ * (contains encrypted copy of the master key)
114
}
171
+ */
115
172
+static bool
116
+static void coroutine_fn block_copy_async_co_entry(void *opaque)
173
+qcrypto_block_luks_slot_active(const QCryptoBlockLUKS *luks,
174
+ unsigned int slot_idx)
175
+{
117
+{
176
+ uint32_t val;
118
+ block_copy_common(opaque);
177
+
178
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
179
+ val = luks->header.key_slots[slot_idx].active;
180
+ return val == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
181
+}
119
+}
182
+
120
+
183
+/*
121
+BlockCopyCallState *block_copy_async(BlockCopyState *s,
184
+ * Returns the number of slots that are marked as active
122
+ int64_t offset, int64_t bytes,
185
+ * (slots that contain encrypted copy of the master key)
123
+ BlockCopyAsyncCallbackFunc cb,
186
+ */
124
+ void *cb_opaque)
187
+static unsigned int
188
+qcrypto_block_luks_count_active_slots(const QCryptoBlockLUKS *luks)
189
+{
125
+{
190
+ size_t i = 0;
126
+ BlockCopyCallState *call_state = g_new(BlockCopyCallState, 1);
191
+ unsigned int ret = 0;
192
+
127
+
193
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
128
+ *call_state = (BlockCopyCallState) {
194
+ if (qcrypto_block_luks_slot_active(luks, i)) {
129
+ .s = s,
195
+ ret++;
130
+ .offset = offset,
196
+ }
131
+ .bytes = bytes,
197
+ }
132
+ .cb = cb,
198
+ return ret;
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;
199
+}
141
+}
200
+
142
+
201
+/*
143
+void block_copy_call_free(BlockCopyCallState *call_state)
202
+ * Finds first key slot which is not active
203
+ * Returns the key slot index, or -1 if it doesn't exist
204
+ */
205
+static int
206
+qcrypto_block_luks_find_free_keyslot(const QCryptoBlockLUKS *luks)
207
+{
144
+{
208
+ size_t i;
145
+ if (!call_state) {
146
+ return;
147
+ }
209
+
148
+
210
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
149
+ assert(call_state->finished);
211
+ if (!qcrypto_block_luks_slot_active(luks, i)) {
150
+ g_free(call_state);
212
+ return i;
213
+ }
214
+ }
215
+ return -1;
216
+}
151
+}
217
+
152
+
218
+/*
153
+bool block_copy_call_finished(BlockCopyCallState *call_state)
219
+ * Erases an keyslot given its index
220
+ * Returns:
221
+ * 0 if the keyslot was erased successfully
222
+ * -1 if a error occurred while erasing the keyslot
223
+ *
224
+ */
225
+static int
226
+qcrypto_block_luks_erase_key(QCryptoBlock *block,
227
+ unsigned int slot_idx,
228
+ QCryptoBlockWriteFunc writefunc,
229
+ void *opaque,
230
+ Error **errp)
231
+{
154
+{
232
+ QCryptoBlockLUKS *luks = block->opaque;
155
+ return call_state->finished;
233
+ QCryptoBlockLUKSKeySlot *slot;
234
+ g_autofree uint8_t *garbagesplitkey = NULL;
235
+ size_t splitkeylen;
236
+ size_t i;
237
+ Error *local_err = NULL;
238
+ int ret;
239
+
240
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
241
+ slot = &luks->header.key_slots[slot_idx];
242
+
243
+ splitkeylen = luks->header.master_key_len * slot->stripes;
244
+ assert(splitkeylen > 0);
245
+
246
+ garbagesplitkey = g_new0(uint8_t, splitkeylen);
247
+
248
+ /* Reset the key slot header */
249
+ memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
250
+ slot->iterations = 0;
251
+ slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
252
+
253
+ ret = qcrypto_block_luks_store_header(block, writefunc,
254
+ opaque, &local_err);
255
+
256
+ if (ret < 0) {
257
+ error_propagate(errp, local_err);
258
+ }
259
+ /*
260
+ * Now try to erase the key material, even if the header
261
+ * update failed
262
+ */
263
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS; i++) {
264
+ if (qcrypto_random_bytes(garbagesplitkey,
265
+ splitkeylen, &local_err) < 0) {
266
+ /*
267
+ * If we failed to get the random data, still write
268
+ * at least zeros to the key slot at least once
269
+ */
270
+ error_propagate(errp, local_err);
271
+
272
+ if (i > 0) {
273
+ return -1;
274
+ }
275
+ }
276
+ if (writefunc(block,
277
+ slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
278
+ garbagesplitkey,
279
+ splitkeylen,
280
+ opaque,
281
+ &local_err) != splitkeylen) {
282
+ error_propagate(errp, local_err);
283
+ return -1;
284
+ }
285
+ }
286
+ return ret;
287
+}
288
289
static int
290
qcrypto_block_luks_open(QCryptoBlock *block,
291
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_open(QCryptoBlock *block,
292
293
luks = g_new0(QCryptoBlockLUKS, 1);
294
block->opaque = luks;
295
+ luks->secret = g_strdup(options->u.luks.key_secret);
296
297
if (qcrypto_block_luks_load_header(block, readfunc, opaque, errp) < 0) {
298
goto fail;
299
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_open(QCryptoBlock *block,
300
fail:
301
qcrypto_block_free_cipher(block);
302
qcrypto_ivgen_free(block->ivgen);
303
+ g_free(luks->secret);
304
g_free(luks);
305
return -1;
306
}
307
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_create(QCryptoBlock *block,
308
309
memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
310
if (!luks_opts.has_iter_time) {
311
- luks_opts.iter_time = 2000;
312
+ luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
313
}
314
if (!luks_opts.has_cipher_alg) {
315
luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
316
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_create(QCryptoBlock *block,
317
optprefix ? optprefix : "");
318
goto error;
319
}
320
+ luks->secret = g_strdup(options->u.luks.key_secret);
321
+
322
password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
323
if (!password) {
324
goto error;
325
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_create(QCryptoBlock *block,
326
qcrypto_block_free_cipher(block);
327
qcrypto_ivgen_free(block->ivgen);
328
329
+ g_free(luks->secret);
330
g_free(luks);
331
return -1;
332
}
333
334
+static int
335
+qcrypto_block_luks_amend_add_keyslot(QCryptoBlock *block,
336
+ QCryptoBlockReadFunc readfunc,
337
+ QCryptoBlockWriteFunc writefunc,
338
+ void *opaque,
339
+ QCryptoBlockAmendOptionsLUKS *opts_luks,
340
+ bool force,
341
+ Error **errp)
342
+{
343
+ QCryptoBlockLUKS *luks = block->opaque;
344
+ uint64_t iter_time = opts_luks->has_iter_time ?
345
+ opts_luks->iter_time :
346
+ QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
347
+ int keyslot;
348
+ g_autofree char *old_password = NULL;
349
+ g_autofree char *new_password = NULL;
350
+ g_autofree uint8_t *master_key = NULL;
351
+
352
+ char *secret = opts_luks->has_secret ? opts_luks->secret : luks->secret;
353
+
354
+ if (!opts_luks->has_new_secret) {
355
+ error_setg(errp, "'new-secret' is required to activate a keyslot");
356
+ return -1;
357
+ }
358
+ if (opts_luks->has_old_secret) {
359
+ error_setg(errp,
360
+ "'old-secret' must not be given when activating keyslots");
361
+ return -1;
362
+ }
363
+
364
+ if (opts_luks->has_keyslot) {
365
+ keyslot = opts_luks->keyslot;
366
+ if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
367
+ error_setg(errp,
368
+ "Invalid keyslot %u specified, must be between 0 and %u",
369
+ keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
370
+ return -1;
371
+ }
372
+ } else {
373
+ keyslot = qcrypto_block_luks_find_free_keyslot(luks);
374
+ if (keyslot == -1) {
375
+ error_setg(errp,
376
+ "Can't add a keyslot - all keyslots are in use");
377
+ return -1;
378
+ }
379
+ }
380
+
381
+ if (!force && qcrypto_block_luks_slot_active(luks, keyslot)) {
382
+ error_setg(errp,
383
+ "Refusing to overwrite active keyslot %i - "
384
+ "please erase it first",
385
+ keyslot);
386
+ return -1;
387
+ }
388
+
389
+ /* Locate the password that will be used to retrieve the master key */
390
+ old_password = qcrypto_secret_lookup_as_utf8(secret, errp);
391
+ if (!old_password) {
392
+ return -1;
393
+ }
394
+
395
+ /* Retrieve the master key */
396
+ master_key = g_new0(uint8_t, luks->header.master_key_len);
397
+
398
+ if (qcrypto_block_luks_find_key(block, old_password, master_key,
399
+ readfunc, opaque, errp) < 0) {
400
+ error_append_hint(errp, "Failed to retrieve the master key");
401
+ return -1;
402
+ }
403
+
404
+ /* Locate the new password*/
405
+ new_password = qcrypto_secret_lookup_as_utf8(opts_luks->new_secret, errp);
406
+ if (!new_password) {
407
+ return -1;
408
+ }
409
+
410
+ /* Now set the new keyslots */
411
+ if (qcrypto_block_luks_store_key(block, keyslot, new_password, master_key,
412
+ iter_time, writefunc, opaque, errp)) {
413
+ error_append_hint(errp, "Failed to write to keyslot %i", keyslot);
414
+ return -1;
415
+ }
416
+ return 0;
417
+}
156
+}
418
+
157
+
419
+static int
158
+bool block_copy_call_succeeded(BlockCopyCallState *call_state)
420
+qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block,
421
+ QCryptoBlockReadFunc readfunc,
422
+ QCryptoBlockWriteFunc writefunc,
423
+ void *opaque,
424
+ QCryptoBlockAmendOptionsLUKS *opts_luks,
425
+ bool force,
426
+ Error **errp)
427
+{
159
+{
428
+ QCryptoBlockLUKS *luks = block->opaque;
160
+ return call_state->finished && call_state->ret == 0;
429
+ g_autofree uint8_t *tmpkey = NULL;
430
+ g_autofree char *old_password = NULL;
431
+
432
+ if (opts_luks->has_new_secret) {
433
+ error_setg(errp,
434
+ "'new-secret' must not be given when erasing keyslots");
435
+ return -1;
436
+ }
437
+ if (opts_luks->has_iter_time) {
438
+ error_setg(errp,
439
+ "'iter-time' must not be given when erasing keyslots");
440
+ return -1;
441
+ }
442
+ if (opts_luks->has_secret) {
443
+ error_setg(errp,
444
+ "'secret' must not be given when erasing keyslots");
445
+ return -1;
446
+ }
447
+
448
+ /* Load the old password if given */
449
+ if (opts_luks->has_old_secret) {
450
+ old_password = qcrypto_secret_lookup_as_utf8(opts_luks->old_secret,
451
+ errp);
452
+ if (!old_password) {
453
+ return -1;
454
+ }
455
+
456
+ /*
457
+ * Allocate a temporary key buffer that we will need when
458
+ * checking if slot matches the given old password
459
+ */
460
+ tmpkey = g_new0(uint8_t, luks->header.master_key_len);
461
+ }
462
+
463
+ /* Erase an explicitly given keyslot */
464
+ if (opts_luks->has_keyslot) {
465
+ int keyslot = opts_luks->keyslot;
466
+
467
+ if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
468
+ error_setg(errp,
469
+ "Invalid keyslot %i specified, must be between 0 and %i",
470
+ keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
471
+ return -1;
472
+ }
473
+
474
+ if (opts_luks->has_old_secret) {
475
+ int rv = qcrypto_block_luks_load_key(block,
476
+ keyslot,
477
+ old_password,
478
+ tmpkey,
479
+ readfunc,
480
+ opaque,
481
+ errp);
482
+ if (rv == -1) {
483
+ return -1;
484
+ } else if (rv == 0) {
485
+ error_setg(errp,
486
+ "Given keyslot %i doesn't contain the given "
487
+ "old password for erase operation",
488
+ keyslot);
489
+ return -1;
490
+ }
491
+ }
492
+
493
+ if (!force && !qcrypto_block_luks_slot_active(luks, keyslot)) {
494
+ error_setg(errp,
495
+ "Given keyslot %i is already erased (inactive) ",
496
+ keyslot);
497
+ return -1;
498
+ }
499
+
500
+ if (!force && qcrypto_block_luks_count_active_slots(luks) == 1) {
501
+ error_setg(errp,
502
+ "Attempt to erase the only active keyslot %i "
503
+ "which will erase all the data in the image "
504
+ "irreversibly - refusing operation",
505
+ keyslot);
506
+ return -1;
507
+ }
508
+
509
+ if (qcrypto_block_luks_erase_key(block, keyslot,
510
+ writefunc, opaque, errp)) {
511
+ error_append_hint(errp, "Failed to erase keyslot %i", keyslot);
512
+ return -1;
513
+ }
514
+
515
+ /* Erase all keyslots that match the given old password */
516
+ } else if (opts_luks->has_old_secret) {
517
+
518
+ unsigned long slots_to_erase_bitmap = 0;
519
+ size_t i;
520
+ int slot_count;
521
+
522
+ assert(QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS <=
523
+ sizeof(slots_to_erase_bitmap) * 8);
524
+
525
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
526
+ int rv = qcrypto_block_luks_load_key(block,
527
+ i,
528
+ old_password,
529
+ tmpkey,
530
+ readfunc,
531
+ opaque,
532
+ errp);
533
+ if (rv == -1) {
534
+ return -1;
535
+ } else if (rv == 1) {
536
+ bitmap_set(&slots_to_erase_bitmap, i, 1);
537
+ }
538
+ }
539
+
540
+ slot_count = bitmap_count_one(&slots_to_erase_bitmap,
541
+ QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
542
+ if (slot_count == 0) {
543
+ error_setg(errp,
544
+ "No keyslots match given (old) password for erase operation");
545
+ return -1;
546
+ }
547
+
548
+ if (!force &&
549
+ slot_count == qcrypto_block_luks_count_active_slots(luks)) {
550
+ error_setg(errp,
551
+ "All the active keyslots match the (old) password that "
552
+ "was given and erasing them will erase all the data in "
553
+ "the image irreversibly - refusing operation");
554
+ return -1;
555
+ }
556
+
557
+ /* Now apply the update */
558
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
559
+ if (!test_bit(i, &slots_to_erase_bitmap)) {
560
+ continue;
561
+ }
562
+ if (qcrypto_block_luks_erase_key(block, i, writefunc,
563
+ opaque, errp)) {
564
+ error_append_hint(errp, "Failed to erase keyslot %zu", i);
565
+ return -1;
566
+ }
567
+ }
568
+ } else {
569
+ error_setg(errp,
570
+ "To erase keyslot(s), either explicit keyslot index "
571
+ "or the password currently contained in them must be given");
572
+ return -1;
573
+ }
574
+ return 0;
575
+}
161
+}
576
+
162
+
577
+static int
163
+bool block_copy_call_failed(BlockCopyCallState *call_state)
578
+qcrypto_block_luks_amend_options(QCryptoBlock *block,
579
+ QCryptoBlockReadFunc readfunc,
580
+ QCryptoBlockWriteFunc writefunc,
581
+ void *opaque,
582
+ QCryptoBlockAmendOptions *options,
583
+ bool force,
584
+ Error **errp)
585
+{
164
+{
586
+ QCryptoBlockAmendOptionsLUKS *opts_luks = &options->u.luks;
165
+ return call_state->finished && call_state->ret < 0;
166
+}
587
+
167
+
588
+ switch (opts_luks->state) {
168
+int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read)
589
+ case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE:
169
+{
590
+ return qcrypto_block_luks_amend_add_keyslot(block, readfunc,
170
+ assert(call_state->finished);
591
+ writefunc, opaque,
171
+ if (error_is_read) {
592
+ opts_luks, force, errp);
172
+ *error_is_read = call_state->error_is_read;
593
+ case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE:
594
+ return qcrypto_block_luks_amend_erase_keyslots(block, readfunc,
595
+ writefunc, opaque,
596
+ opts_luks, force, errp);
597
+ default:
598
+ g_assert_not_reached();
599
+ }
173
+ }
174
+ return call_state->ret;
600
+}
175
+}
601
176
+
602
static int qcrypto_block_luks_get_info(QCryptoBlock *block,
177
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
603
QCryptoBlockInfo *info,
604
@@ -XXX,XX +XXX,XX @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
605
606
static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
607
{
178
{
608
- g_free(block->opaque);
179
return s->copy_bitmap;
609
+ QCryptoBlockLUKS *luks = block->opaque;
610
+ if (luks) {
611
+ g_free(luks->secret);
612
+ g_free(luks);
613
+ }
614
}
615
616
617
@@ -XXX,XX +XXX,XX @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
618
const QCryptoBlockDriver qcrypto_block_driver_luks = {
619
.open = qcrypto_block_luks_open,
620
.create = qcrypto_block_luks_create,
621
+ .amend = qcrypto_block_luks_amend_options,
622
.get_info = qcrypto_block_luks_get_info,
623
.cleanup = qcrypto_block_luks_cleanup,
624
.decrypt = qcrypto_block_luks_decrypt,
625
--
180
--
626
2.26.2
181
2.29.2
627
182
628
183
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
rename the write_func to create_write_func, and init_func to create_init_func.
3
It simplifies debugging.
4
This is preparation for other write_func that will be used to update the encryption keys.
5
4
6
No functional changes
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
7
Message-Id: <20210116214705.822267-6-vsementsov@virtuozzo.com>
9
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
10
Message-Id: <20200608094030.670121-7-mlevitsk@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
9
---
13
block/crypto.c | 25 ++++++++++++-------------
10
block/block-copy.c | 11 ++++++++++-
14
1 file changed, 12 insertions(+), 13 deletions(-)
11
1 file changed, 10 insertions(+), 1 deletion(-)
15
12
16
diff --git a/block/crypto.c b/block/crypto.c
13
diff --git a/block/block-copy.c b/block/block-copy.c
17
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
18
--- a/block/crypto.c
15
--- a/block/block-copy.c
19
+++ b/block/crypto.c
16
+++ b/block/block-copy.c
20
@@ -XXX,XX +XXX,XX @@ struct BlockCryptoCreateData {
17
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
21
};
18
/* Coroutine where async block-copy is running */
22
19
Coroutine *co;
23
20
24
-static ssize_t block_crypto_write_func(QCryptoBlock *block,
21
+ /* To reference all call states from BlockCopyState */
25
- size_t offset,
22
+ QLIST_ENTRY(BlockCopyCallState) list;
26
- const uint8_t *buf,
23
+
27
- size_t buflen,
24
/* State */
28
- void *opaque,
25
int ret;
29
- Error **errp)
26
bool finished;
30
+static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
27
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
31
+ size_t offset,
28
bool use_copy_range;
32
+ const uint8_t *buf,
29
int64_t copy_size;
33
+ size_t buflen,
30
uint64_t len;
34
+ void *opaque,
31
- QLIST_HEAD(, BlockCopyTask) tasks;
35
+ Error **errp)
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)
36
{
46
{
37
struct BlockCryptoCreateData *data = opaque;
47
int ret;
38
ssize_t ret;
48
39
@@ -XXX,XX +XXX,XX @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
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
+
40
return ret;
60
return ret;
41
}
61
}
42
62
43
-
44
-static ssize_t block_crypto_init_func(QCryptoBlock *block,
45
- size_t headerlen,
46
- void *opaque,
47
- Error **errp)
48
+static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
49
+ size_t headerlen,
50
+ void *opaque,
51
+ Error **errp)
52
{
53
struct BlockCryptoCreateData *data = opaque;
54
Error *local_error = NULL;
55
@@ -XXX,XX +XXX,XX @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
56
};
57
58
crypto = qcrypto_block_create(opts, NULL,
59
- block_crypto_init_func,
60
- block_crypto_write_func,
61
+ block_crypto_create_init_func,
62
+ block_crypto_create_write_func,
63
&data,
64
errp);
65
66
--
63
--
67
2.26.2
64
2.29.2
68
65
69
66
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Now that we have all the infrastructure in place,
3
We are going to directly use one async block-copy operation for backup
4
wire it in the qcow2 driver and expose this to the user.
4
job, so we need rate limiter.
5
5
6
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
6
We want to maintain current backup behavior: only background copying is
7
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
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>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
27
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20200608094030.670121-9-mlevitsk@redhat.com>
28
Message-Id: <20210116214705.822267-7-vsementsov@virtuozzo.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
29
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
30
---
12
block/qcow2.c | 71 +++++++++++++++++++++++++++++++++-----
31
include/block/block-copy.h | 5 ++++-
13
tests/qemu-iotests/082.out | 45 ++++++++++++++++++++++++
32
block/backup-top.c | 2 +-
14
2 files changed, 107 insertions(+), 9 deletions(-)
33
block/backup.c | 2 +-
34
block/block-copy.c | 46 +++++++++++++++++++++++++++++++++++++-
35
4 files changed, 51 insertions(+), 4 deletions(-)
15
36
16
diff --git a/block/qcow2.c b/block/qcow2.c
37
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
17
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.c
39
--- a/include/block/block-copy.h
19
+++ b/block/qcow2.c
40
+++ b/include/block/block-copy.h
20
@@ -XXX,XX +XXX,XX @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
41
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
21
return ret;
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);
22
}
70
}
23
71
24
+static QDict*
72
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
25
+qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp)
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)
26
+{
151
+{
27
+ QDict *cryptoopts_qdict;
152
+ if (call_state->sleep_state) {
28
+ QDict *opts_qdict;
153
+ qemu_co_sleep_wake(call_state->sleep_state);
154
+ }
155
+}
29
+
156
+
30
+ /* Extract "encrypt." options into a qdict */
31
+ opts_qdict = qemu_opts_to_qdict(opts, NULL);
32
+ qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
33
+ qobject_unref(opts_qdict);
34
+ qdict_put_str(cryptoopts_qdict, "format", fmt);
35
+ return cryptoopts_qdict;
36
+}
37
38
/*
157
/*
39
* read qcow2 extension and fill bs
158
* block_copy_common
40
@@ -XXX,XX +XXX,XX @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
159
*
41
160
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
42
if (has_luks) {
161
}
43
g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL;
162
44
- QDict *opts_qdict;
163
int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
45
- QDict *cryptoopts;
164
- bool *error_is_read)
46
+ QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp);
165
+ bool ignore_ratelimit, bool *error_is_read)
47
size_t headerlen;
166
{
48
167
BlockCopyCallState call_state = {
49
- opts_qdict = qemu_opts_to_qdict(opts, NULL);
168
.s = s,
50
- qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt.");
169
.offset = start,
51
- qobject_unref(opts_qdict);
170
.bytes = bytes,
52
-
171
+ .ignore_ratelimit = ignore_ratelimit,
53
- qdict_put_str(cryptoopts, "format", "luks");
172
.max_workers = BLOCK_COPY_MAX_WORKERS,
54
-
55
create_opts = block_crypto_create_opts_init(cryptoopts, errp);
56
qobject_unref(cryptoopts);
57
if (!create_opts) {
58
@@ -XXX,XX +XXX,XX @@ typedef enum Qcow2AmendOperation {
59
QCOW2_NO_OPERATION = 0,
60
61
QCOW2_UPGRADING,
62
+ QCOW2_UPDATING_ENCRYPTION,
63
QCOW2_CHANGING_REFCOUNT_ORDER,
64
QCOW2_DOWNGRADING,
65
} Qcow2AmendOperation;
66
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
67
int ret;
68
QemuOptDesc *desc = opts->list->desc;
69
Qcow2AmendHelperCBInfo helper_cb_info;
70
+ bool encryption_update = false;
71
72
while (desc && desc->name) {
73
if (!qemu_opt_find(opts, desc->name)) {
74
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
75
backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE);
76
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) {
77
backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
78
+ } else if (g_str_has_prefix(desc->name, "encrypt.")) {
79
+ if (!s->crypto) {
80
+ error_setg(errp,
81
+ "Can't amend encryption options - encryption not present");
82
+ return -EINVAL;
83
+ }
84
+ if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
85
+ error_setg(errp,
86
+ "Only LUKS encryption options can be amended");
87
+ return -ENOTSUP;
88
+ }
89
+ encryption_update = true;
90
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
91
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
92
lazy_refcounts);
93
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
94
.original_status_cb = status_cb,
95
.original_cb_opaque = cb_opaque,
96
.total_operations = (new_version != old_version)
97
- + (s->refcount_bits != refcount_bits)
98
+ + (s->refcount_bits != refcount_bits) +
99
+ (encryption_update == true)
100
};
173
};
101
174
102
/* Upgrade first (some features may require compat=1.1) */
175
@@ -XXX,XX +XXX,XX @@ void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
103
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
176
{
104
}
177
s->skip_unallocated = skip;
105
}
178
}
106
107
+ if (encryption_update) {
108
+ QDict *amend_opts_dict;
109
+ QCryptoBlockAmendOptions *amend_opts;
110
+
179
+
111
+ helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
180
+void block_copy_set_speed(BlockCopyState *s, uint64_t speed)
112
+ amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
181
+{
113
+ if (!amend_opts_dict) {
182
+ s->speed = speed;
114
+ return -EINVAL;
183
+ if (speed > 0) {
115
+ }
184
+ ratelimit_set_speed(&s->rate_limit, speed, BLOCK_COPY_SLICE_TIME);
116
+ amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp);
117
+ qobject_unref(amend_opts_dict);
118
+ if (!amend_opts) {
119
+ return -EINVAL;
120
+ }
121
+ ret = qcrypto_block_amend_options(s->crypto,
122
+ qcow2_crypto_hdr_read_func,
123
+ qcow2_crypto_hdr_write_func,
124
+ bs,
125
+ amend_opts,
126
+ force,
127
+ errp);
128
+ qapi_free_QCryptoBlockAmendOptions(amend_opts);
129
+ if (ret < 0) {
130
+ return ret;
131
+ }
132
+ }
185
+ }
133
+
186
+
134
if (s->refcount_bits != refcount_bits) {
187
+ /*
135
int refcount_order = ctz32(refcount_bits);
188
+ * Note: it's good to kick all call states from here, but it should be done
136
189
+ * only from a coroutine, to not crash if s->calls list changed while
137
@@ -XXX,XX +XXX,XX @@ static QemuOptsList qcow2_amend_opts = {
190
+ * entering one call. So for now, the only user of this function kicks its
138
.name = "qcow2-amend-opts",
191
+ * only one call_state by hand.
139
.head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
192
+ */
140
.desc = {
193
+}
141
+ BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."),
142
+ BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."),
143
+ BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."),
144
+ BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."),
145
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
146
QCOW_COMMON_OPTIONS,
147
{ /* end of list */ }
148
}
149
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
150
index XXXXXXX..XXXXXXX 100644
151
--- a/tests/qemu-iotests/082.out
152
+++ b/tests/qemu-iotests/082.out
153
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
154
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
155
data_file=<str> - File name of an external data file
156
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
157
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
158
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
159
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
160
+ encrypt.old-secret=<str> - Select all keyslots that match this password
161
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
162
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
163
refcount_bits=<num> - Width of a reference count entry in bits
164
size=<size> - Virtual disk size
165
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
166
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
167
data_file=<str> - File name of an external data file
168
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
169
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
170
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
171
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
172
+ encrypt.old-secret=<str> - Select all keyslots that match this password
173
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
174
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
175
refcount_bits=<num> - Width of a reference count entry in bits
176
size=<size> - Virtual disk size
177
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
178
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
179
data_file=<str> - File name of an external data file
180
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
181
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
182
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
183
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
184
+ encrypt.old-secret=<str> - Select all keyslots that match this password
185
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
186
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
187
refcount_bits=<num> - Width of a reference count entry in bits
188
size=<size> - Virtual disk size
189
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
190
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
191
data_file=<str> - File name of an external data file
192
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
193
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
194
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
195
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
196
+ encrypt.old-secret=<str> - Select all keyslots that match this password
197
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
198
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
199
refcount_bits=<num> - Width of a reference count entry in bits
200
size=<size> - Virtual disk size
201
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
202
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
203
data_file=<str> - File name of an external data file
204
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
205
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
206
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
207
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
208
+ encrypt.old-secret=<str> - Select all keyslots that match this password
209
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
210
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
211
refcount_bits=<num> - Width of a reference count entry in bits
212
size=<size> - Virtual disk size
213
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
214
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
215
data_file=<str> - File name of an external data file
216
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
217
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
218
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
219
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
220
+ encrypt.old-secret=<str> - Select all keyslots that match this password
221
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
222
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
223
refcount_bits=<num> - Width of a reference count entry in bits
224
size=<size> - Virtual disk size
225
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
226
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
227
data_file=<str> - File name of an external data file
228
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
229
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
230
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
231
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
232
+ encrypt.old-secret=<str> - Select all keyslots that match this password
233
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
234
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
235
refcount_bits=<num> - Width of a reference count entry in bits
236
size=<size> - Virtual disk size
237
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
238
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
239
data_file=<str> - File name of an external data file
240
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
241
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
242
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
243
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
244
+ encrypt.old-secret=<str> - Select all keyslots that match this password
245
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
246
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
247
refcount_bits=<num> - Width of a reference count entry in bits
248
size=<size> - Virtual disk size
249
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
250
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
251
data_file=<str> - File name of an external data file
252
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
253
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
254
+ encrypt.keyslot=<num> - Select a single keyslot to modify explicitly
255
+ encrypt.new-secret=<str> - New secret to set in the matching keyslots. Empty string to erase
256
+ encrypt.old-secret=<str> - Select all keyslots that match this password
257
+ encrypt.state=<str> - Select new state of affected keyslots (active/inactive)
258
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
259
refcount_bits=<num> - Width of a reference count entry in bits
260
size=<size> - Virtual disk size
261
--
194
--
262
2.26.2
195
2.29.2
263
196
264
197
diff view generated by jsdifflib
1
Right now, _filter_img_create just filters out everything that looks
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
format-dependent, and applies some filename filters. That means that we
3
have to add another filter line every time some format gets a new
4
creation option. This can be avoided by instead discarding everything
5
and just keeping what we know is format-independent (format, size,
6
backing file, encryption information[1], preallocation) or just
7
interesting to have in the reference output (external data file path).
8
2
9
Furthermore, we probably want to sort these options. Format drivers are
3
Add function to cancel running async block-copy call. It will be used
10
not required to define them in any specific order, so the output is
4
in backup.
11
effectively random (although this has never bothered us until now). We
12
need a specific order for our reference outputs, though. Unfortunately,
13
just using a plain "sort" would change a lot of existing reference
14
outputs, so we have to pre-filter the option keys to keep our existing
15
order (fmt, size, backing*, data, encryption info, preallocation).
16
5
17
Finally, this makes it difficult for _filter_img_create to automagically
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
work for QMP output. Thus, this patch adds a separate
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
19
_filter_img_create_for_qmp function that echos every line verbatim that
8
Message-Id: <20210116214705.822267-8-vsementsov@virtuozzo.com>
20
does not start with "Formatting", and pipes those "Formatting" lines to
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
_filter_img_create.
10
---
11
include/block/block-copy.h | 13 +++++++++++++
12
block/block-copy.c | 24 +++++++++++++++++++-----
13
2 files changed, 32 insertions(+), 5 deletions(-)
22
14
23
[1] Actually, the only thing that is really important is whether
15
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
24
encryption is enabled or not. A patch by Maxim thus removes all
25
other "encrypt.*" options from the output:
26
https://lists.nongnu.org/archive/html/qemu-block/2020-06/msg00339.html
27
But that patch needs to come later so we can get away with changing
28
as few reference outputs in this patch here as possible.
29
30
Signed-off-by: Max Reitz <mreitz@redhat.com>
31
Message-Id: <20200618150628.2169239-2-mreitz@redhat.com>
32
---
33
tests/qemu-iotests/112.out | 2 +-
34
tests/qemu-iotests/141 | 2 +-
35
tests/qemu-iotests/153 | 9 ++-
36
tests/qemu-iotests/common.filter | 96 ++++++++++++++++++++++++--------
37
4 files changed, 78 insertions(+), 31 deletions(-)
38
39
diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out
40
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
41
--- a/tests/qemu-iotests/112.out
17
--- a/include/block/block-copy.h
42
+++ b/tests/qemu-iotests/112.out
18
+++ b/include/block/block-copy.h
43
@@ -XXX,XX +XXX,XX @@ QA output created by 112
19
@@ -XXX,XX +XXX,XX @@ void block_copy_call_free(BlockCopyCallState *call_state);
44
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
20
bool block_copy_call_finished(BlockCopyCallState *call_state);
45
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
21
bool block_copy_call_succeeded(BlockCopyCallState *call_state);
46
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
22
bool block_copy_call_failed(BlockCopyCallState *call_state);
47
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount_bits=-1
23
+bool block_copy_call_cancelled(BlockCopyCallState *call_state);
48
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
24
int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read);
49
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
25
50
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
26
void block_copy_set_speed(BlockCopyState *s, uint64_t speed);
51
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
27
void block_copy_kick(BlockCopyCallState *call_state);
52
diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
28
53
index XXXXXXX..XXXXXXX 100755
29
+/*
54
--- a/tests/qemu-iotests/141
30
+ * Cancel running block-copy call.
55
+++ b/tests/qemu-iotests/141
31
+ *
56
@@ -XXX,XX +XXX,XX @@ test_blockjob()
32
+ * Cancel leaves block-copy state valid: dirty bits are correct and you may use
57
_send_qemu_cmd $QEMU_HANDLE \
33
+ * cancel + <run block_copy with same parameters> to emulate pause/resume.
58
"$1" \
34
+ *
59
"$2" \
35
+ * Note also, that the cancel is async: it only marks block-copy call to be
60
- | _filter_img_create | _filter_qmp_empty_return
36
+ * cancelled. So, the call may be cancelled (block_copy_call_cancelled() reports
61
+ | _filter_img_create_in_qmp | _filter_qmp_empty_return
37
+ * true) but not yet finished (block_copy_call_finished() reports false).
62
38
+ */
63
# We want this to return an error because the block job is still running
39
+void block_copy_call_cancel(BlockCopyCallState *call_state);
64
_send_qemu_cmd $QEMU_HANDLE \
40
+
65
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
41
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s);
66
index XXXXXXX..XXXXXXX 100755
42
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip);
67
--- a/tests/qemu-iotests/153
43
68
+++ b/tests/qemu-iotests/153
44
diff --git a/block/block-copy.c b/block/block-copy.c
69
@@ -XXX,XX +XXX,XX @@ done
70
71
echo
72
echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir
73
-(
74
- $QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}"
75
- $QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}"
76
- $QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b"
77
-) | _filter_img_create
78
+$QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" | _filter_img_create
79
+$QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}" | _filter_img_create
80
+$QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b" \
81
+ | _filter_img_create
82
83
echo
84
echo "== Two devices sharing the same file in backing chain =="
85
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
86
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
87
--- a/tests/qemu-iotests/common.filter
46
--- a/block/block-copy.c
88
+++ b/tests/qemu-iotests/common.filter
47
+++ b/block/block-copy.c
89
@@ -XXX,XX +XXX,XX @@ _filter_actual_image_size()
48
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState {
90
# replace driver-specific options in the "Formatting..." line
49
int ret;
91
_filter_img_create()
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)
92
{
86
{
93
- data_file_filter=()
87
- return call_state->finished && call_state->ret == 0;
94
- if data_file=$(_get_data_file "$TEST_IMG"); then
88
+ return call_state->finished && !call_state->cancelled &&
95
- data_file_filter=(-e "s# data_file=$data_file##")
89
+ call_state->ret == 0;
96
+ # Split the line into the pre-options part ($filename_part, which
90
}
97
+ # precedes ", fmt=") and the options part ($options, which starts
91
98
+ # with "fmt=")
92
bool block_copy_call_failed(BlockCopyCallState *call_state)
99
+ readarray -td '' formatting_line < <(sed -e 's/, fmt=/\x0/')
93
{
100
+
94
- return call_state->finished && call_state->ret < 0;
101
+ filename_part=${formatting_line[0]}
95
+ return call_state->finished && !call_state->cancelled &&
102
+ if [ -n "${formatting_line[1]}" ]; then
96
+ call_state->ret < 0;
103
+ options="fmt=${formatting_line[1]}"
104
+ else
105
+ options=''
106
+ fi
107
+
108
+ # Set grep_data_file to '\|data_file' to keep it; make it empty
109
+ # to drop it.
110
+ # We want to drop it if it is part of the global $IMGOPTS, and we
111
+ # want to keep it otherwise (if the test specifically wants to
112
+ # test data files).
113
+ grep_data_file='\|data_file'
114
+ if _get_data_file "$TEST_IMG" > /dev/null; then
115
+ grep_data_file=''
116
fi
117
118
- $SED "${data_file_filter[@]}" \
119
+ filename_filters=(
120
-e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
121
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
122
-e "s#$TEST_DIR#TEST_DIR#g" \
123
-e "s#$SOCK_DIR#SOCK_DIR#g" \
124
-e "s#$IMGFMT#IMGFMT#g" \
125
-e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \
126
- -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' \
127
- -e "s# encryption=off##g" \
128
- -e "s# cluster_size=[0-9]\\+##g" \
129
- -e "s# table_size=[0-9]\\+##g" \
130
- -e "s# compat=[^ ]*##g" \
131
- -e "s# compat6=\\(on\\|off\\)##g" \
132
- -e "s# static=\\(on\\|off\\)##g" \
133
- -e "s# zeroed_grain=\\(on\\|off\\)##g" \
134
- -e "s# subformat=[^ ]*##g" \
135
- -e "s# adapter_type=[^ ]*##g" \
136
- -e "s# hwversion=[^ ]*##g" \
137
- -e "s# lazy_refcounts=\\(on\\|off\\)##g" \
138
- -e "s# block_size=[0-9]\\+##g" \
139
- -e "s# block_state_zero=\\(on\\|off\\)##g" \
140
- -e "s# log_size=[0-9]\\+##g" \
141
- -e "s# refcount_bits=[0-9]\\+##g" \
142
- -e "s# key-secret=[a-zA-Z0-9]\\+##g" \
143
- -e "s# iter-time=[0-9]\\+##g" \
144
- -e "s# force_size=\\(on\\|off\\)##g" \
145
- -e "s# compression_type=[a-zA-Z0-9]\\+##g"
146
+ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g'
147
+ )
148
+
149
+ filename_part=$(echo "$filename_part" | $SED "${filename_filters[@]}")
150
+
151
+ # Break the option line before each option (preserving pre-existing
152
+ # line breaks by replacing them by \0 and restoring them at the end),
153
+ # then filter out the options we want to keep and sort them according
154
+ # to some order that all block drivers used at the time of writing
155
+ # this function.
156
+ options=$(
157
+ echo "$options" \
158
+ | tr '\n' '\0' \
159
+ | $SED -e 's/\x0$//' -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \
160
+ | grep -ae "^\(fmt\\|size\\|backing\\|preallocation\\|encrypt$grep_data_file\\)" \
161
+ | $SED "${filename_filters[@]}" \
162
+ -e 's/^\(fmt\)/0-\1/' \
163
+ -e 's/^\(size\)/1-\1/' \
164
+ -e 's/^\(backing\)/2-\1/' \
165
+ -e 's/^\(data_file\)/3-\1/' \
166
+ -e 's/^\(encryption\)/4-\1/' \
167
+ -e 's/^\(encrypt\.format\)/5-\1/' \
168
+ -e 's/^\(encrypt\.key-secret\)/6-\1/' \
169
+ -e 's/^\(encrypt\.iter-time\)/7-\1/' \
170
+ -e 's/^\(preallocation\)/8-\1/' \
171
+ | sort \
172
+ | $SED -e 's/^[0-9]-//' \
173
+ | tr '\n\0' ' \n' \
174
+ | $SED -e 's/^ *$//' -e 's/ *$//'
175
+ )
176
+
177
+ if [ -n "$options" ]; then
178
+ echo "$filename_part, $options"
179
+ elif [ -n "$filename_part" ]; then
180
+ echo "$filename_part"
181
+ fi
182
+}
97
+}
183
+
98
+
184
+# Filter the "Formatting..." line in QMP output (leaving the QMP output
99
+bool block_copy_call_cancelled(BlockCopyCallState *call_state)
185
+# untouched)
186
+# (In contrast to _filter_img_create(), this function does not support
187
+# multi-line Formatting output)
188
+_filter_img_create_in_qmp()
189
+{
100
+{
190
+ while read -r line; do
101
+ return call_state->cancelled;
191
+ if echo "$line" | grep -q '^Formatting'; then
192
+ echo "$line" | _filter_img_create
193
+ else
194
+ echo "$line"
195
+ fi
196
+ done
197
}
102
}
198
103
199
_filter_img_create_size()
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;
200
--
118
--
201
2.26.2
119
2.29.2
202
120
203
121
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This implements the encryption key management using the generic code in
3
Add new parameters to configure future backup features. The patch
4
qcrypto layer and exposes it to the user via qemu-img
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.
5
8
6
This code adds another 'write_func' because the initialization
9
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
write_func works directly on the underlying file, and amend
8
works on instance of luks device.
9
10
This commit also adds a 'hack/workaround' I and Kevin Wolf (thanks)
11
made to make the driver both support write sharing (to avoid breaking the users),
12
and be safe against concurrent metadata update (the keyslots)
13
14
Eventually the write sharing for luks driver will be deprecated
15
and removed together with this hack.
16
17
The hack is that we ask (as a format driver) for BLK_PERM_CONSISTENT_READ
18
and then when we want to update the keys, we unshare that permission.
19
So if someone else has the image open, even readonly, encryption
20
key update will fail gracefully.
21
22
Also thanks to Daniel Berrange for the idea of
23
unsharing read, rather that write permission which allows
24
to avoid cases when the other user had opened the image read-only.
25
26
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
27
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
28
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
29
Message-Id: <20200608094030.670121-8-mlevitsk@redhat.com>
11
Message-Id: <20210116214705.822267-11-vsementsov@virtuozzo.com>
30
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
31
---
13
---
32
block/crypto.h | 34 +++++++++++++
14
qapi/block-core.json | 13 ++++++++++++-
33
block/crypto.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++--
15
block/backup.c | 28 +++++++++++++++++++++++-----
34
2 files changed, 161 insertions(+), 3 deletions(-)
16
block/replication.c | 2 +-
17
blockdev.c | 8 +++++++-
18
4 files changed, 43 insertions(+), 8 deletions(-)
35
19
36
diff --git a/block/crypto.h b/block/crypto.h
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
37
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
38
--- a/block/crypto.h
22
--- a/qapi/block-core.json
39
+++ b/block/crypto.h
23
+++ b/qapi/block-core.json
40
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
41
#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
25
#
42
#define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
26
# @use-copy-range: Use copy offloading. Default true.
43
#define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
27
#
44
+#define BLOCK_CRYPTO_OPT_LUKS_KEYSLOT "keyslot"
28
+# @max-workers: Maximum number of parallel requests for the sustained background
45
+#define BLOCK_CRYPTO_OPT_LUKS_STATE "state"
29
+# copying process. Doesn't influence copy-before-write operations.
46
+#define BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET "old-secret"
30
+# Default 64.
47
+#define BLOCK_CRYPTO_OPT_LUKS_NEW_SECRET "new-secret"
31
+#
48
+
32
+# @max-chunk: Maximum request length for the sustained background copying
49
33
+# process. Doesn't influence copy-before-write operations.
50
#define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix) \
34
+# 0 means unlimited. If max-chunk is non-zero then it should not be
51
BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \
35
+# less than job cluster size which is calculated as maximum of
52
@@ -XXX,XX +XXX,XX @@
36
+# target image cluster size and 64k. Default 0.
53
.help = "Time to spend in PBKDF in milliseconds", \
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;
54
}
53
}
55
54
56
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_STATE(prefix) \
55
+ cluster_size = backup_calculate_cluster_size(target, errp);
57
+ { \
56
+ if (cluster_size < 0) {
58
+ .name = prefix BLOCK_CRYPTO_OPT_LUKS_STATE, \
57
+ goto error;
59
+ .type = QEMU_OPT_STRING, \
60
+ .help = "Select new state of affected keyslots (active/inactive)",\
61
+ }
58
+ }
62
+
59
+
63
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(prefix) \
60
+ if (perf->max_workers < 1) {
64
+ { \
61
+ error_setg(errp, "max-workers must be greater than zero");
65
+ .name = prefix BLOCK_CRYPTO_OPT_LUKS_KEYSLOT, \
62
+ return NULL;
66
+ .type = QEMU_OPT_NUMBER, \
67
+ .help = "Select a single keyslot to modify explicitly",\
68
+ }
63
+ }
69
+
64
+
70
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET(prefix) \
65
+ if (perf->max_chunk < 0) {
71
+ { \
66
+ error_setg(errp, "max-chunk must be zero (which means no limit) or "
72
+ .name = prefix BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET, \
67
+ "positive");
73
+ .type = QEMU_OPT_STRING, \
68
+ return NULL;
74
+ .help = "Select all keyslots that match this password", \
75
+ }
69
+ }
76
+
70
+
77
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET(prefix) \
71
+ if (perf->max_chunk && perf->max_chunk < cluster_size) {
78
+ { \
72
+ error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
79
+ .name = prefix BLOCK_CRYPTO_OPT_LUKS_NEW_SECRET, \
73
+ "cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
80
+ .type = QEMU_OPT_STRING, \
74
+ return NULL;
81
+ .help = "New secret to set in the matching keyslots. " \
82
+ "Empty string to erase", \
83
+ }
75
+ }
84
+
76
+
85
QCryptoBlockCreateOptions *
77
+
86
block_crypto_create_opts_init(QDict *opts, Error **errp);
78
if (sync_bitmap) {
87
79
/* If we need to write to this bitmap, check that we can: */
88
diff --git a/block/crypto.c b/block/crypto.c
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
89
index XXXXXXX..XXXXXXX 100644
94
index XXXXXXX..XXXXXXX 100644
90
--- a/block/crypto.c
95
--- a/block/replication.c
91
+++ b/block/crypto.c
96
+++ b/block/replication.c
92
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCrypto BlockCrypto;
97
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
93
98
int64_t active_length, hidden_length, disk_length;
94
struct BlockCrypto {
99
AioContext *aio_context;
95
QCryptoBlock *block;
100
Error *local_err = NULL;
96
+ bool updating_keys;
101
- BackupPerf perf = { .use_copy_range = true };
97
};
102
+ BackupPerf perf = { .use_copy_range = true, .max_workers = 1 };
98
103
99
104
aio_context = bdrv_get_aio_context(bs);
100
@@ -XXX,XX +XXX,XX @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
105
aio_context_acquire(aio_context);
101
return ret;
106
diff --git a/blockdev.c b/blockdev.c
102
}
107
index XXXXXXX..XXXXXXX 100644
103
108
--- a/blockdev.c
104
+static ssize_t block_crypto_write_func(QCryptoBlock *block,
109
+++ b/blockdev.c
105
+ size_t offset,
110
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
106
+ const uint8_t *buf,
107
+ size_t buflen,
108
+ void *opaque,
109
+ Error **errp)
110
+{
111
+ BlockDriverState *bs = opaque;
112
+ ssize_t ret;
113
+
114
+ ret = bdrv_pwrite(bs->file, offset, buf, buflen);
115
+ if (ret < 0) {
116
+ error_setg_errno(errp, -ret, "Could not write encryption header");
117
+ return ret;
118
+ }
119
+ return ret;
120
+}
121
+
122
123
struct BlockCryptoCreateData {
124
BlockBackend *blk;
125
@@ -XXX,XX +XXX,XX @@ static QemuOptsList block_crypto_create_opts_luks = {
126
};
127
128
129
+static QemuOptsList block_crypto_amend_opts_luks = {
130
+ .name = "crypto",
131
+ .head = QTAILQ_HEAD_INITIALIZER(block_crypto_create_opts_luks.head),
132
+ .desc = {
133
+ BLOCK_CRYPTO_OPT_DEF_LUKS_STATE(""),
134
+ BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(""),
135
+ BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET(""),
136
+ BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET(""),
137
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
138
+ { /* end of list */ }
139
+ },
140
+};
141
+
142
QCryptoBlockOpenOptions *
143
block_crypto_open_opts_init(QDict *opts, Error **errp)
144
{
111
{
145
@@ -XXX,XX +XXX,XX @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
112
BlockJob *job = NULL;
146
return spec_info;
113
BdrvDirtyBitmap *bmap = NULL;
147
}
114
- BackupPerf perf = { .use_copy_range = true };
148
115
+ BackupPerf perf = { .use_copy_range = true, .max_workers = 64 };
149
+static int
116
int job_flags = JOB_DEFAULT;
150
+block_crypto_amend_options_luks(BlockDriverState *bs,
117
151
+ QemuOpts *opts,
118
if (!backup->has_speed) {
152
+ BlockDriverAmendStatusCB *status_cb,
119
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
153
+ void *cb_opaque,
120
if (backup->x_perf->has_use_copy_range) {
154
+ bool force,
121
perf.use_copy_range = backup->x_perf->use_copy_range;
155
+ Error **errp)
122
}
156
+{
123
+ if (backup->x_perf->has_max_workers) {
157
+ BlockCrypto *crypto = bs->opaque;
124
+ perf.max_workers = backup->x_perf->max_workers;
158
+ QDict *cryptoopts = NULL;
125
+ }
159
+ QCryptoBlockAmendOptions *amend_options = NULL;
126
+ if (backup->x_perf->has_max_chunk) {
160
+ int ret;
127
+ perf.max_chunk = backup->x_perf->max_chunk;
161
+
128
+ }
162
+ assert(crypto);
129
}
163
+ assert(crypto->block);
130
164
+ crypto->updating_keys = true;
131
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
165
+
166
+ ret = bdrv_child_refresh_perms(bs, bs->file, errp);
167
+ if (ret < 0) {
168
+ goto cleanup;
169
+ }
170
+
171
+ cryptoopts = qemu_opts_to_qdict(opts, NULL);
172
+ qdict_put_str(cryptoopts, "format", "luks");
173
+ amend_options = block_crypto_amend_opts_init(cryptoopts, errp);
174
+ if (!amend_options) {
175
+ ret = -EINVAL;
176
+ goto cleanup;
177
+ }
178
+
179
+ ret = qcrypto_block_amend_options(crypto->block,
180
+ block_crypto_read_func,
181
+ block_crypto_write_func,
182
+ bs,
183
+ amend_options,
184
+ force,
185
+ errp);
186
+cleanup:
187
+ crypto->updating_keys = false;
188
+ bdrv_child_refresh_perms(bs, bs->file, errp);
189
+ qapi_free_QCryptoBlockAmendOptions(amend_options);
190
+ qobject_unref(cryptoopts);
191
+ return ret;
192
+}
193
+
194
+
195
+static void
196
+block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
197
+ const BdrvChildRole role,
198
+ BlockReopenQueue *reopen_queue,
199
+ uint64_t perm, uint64_t shared,
200
+ uint64_t *nperm, uint64_t *nshared)
201
+{
202
+
203
+ BlockCrypto *crypto = bs->opaque;
204
+
205
+ bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
206
+
207
+ /*
208
+ * For backward compatibility, manually share the write
209
+ * and resize permission
210
+ */
211
+ *nshared |= (BLK_PERM_WRITE | BLK_PERM_RESIZE);
212
+ /*
213
+ * Since we are not fully a format driver, don't always request
214
+ * the read/resize permission but only when explicitly
215
+ * requested
216
+ */
217
+ *nperm &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
218
+ *nperm |= perm & (BLK_PERM_WRITE | BLK_PERM_RESIZE);
219
+
220
+ /*
221
+ * This driver doesn't modify LUKS metadata except
222
+ * when updating the encryption slots.
223
+ * Thus unlike a proper format driver we don't ask for
224
+ * shared write/read permission. However we need it
225
+ * when we are updating the keys, to ensure that only we
226
+ * have access to the device.
227
+ *
228
+ * Encryption update will set the crypto->updating_keys
229
+ * during that period and refresh permissions
230
+ *
231
+ */
232
+ if (crypto->updating_keys) {
233
+ /* need exclusive write access for header update */
234
+ *nperm |= BLK_PERM_WRITE;
235
+ /* unshare read and write permission */
236
+ *nshared &= ~(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE);
237
+ }
238
+}
239
+
240
+
241
static const char *const block_crypto_strong_runtime_opts[] = {
242
BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
243
244
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_crypto_luks = {
245
.bdrv_probe = block_crypto_probe_luks,
246
.bdrv_open = block_crypto_open_luks,
247
.bdrv_close = block_crypto_close,
248
- /* This driver doesn't modify LUKS metadata except when creating image.
249
- * Allow share-rw=on as a special case. */
250
- .bdrv_child_perm = bdrv_default_perms,
251
+ .bdrv_child_perm = block_crypto_child_perms,
252
.bdrv_co_create = block_crypto_co_create_luks,
253
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
254
.bdrv_co_truncate = block_crypto_co_truncate,
255
.create_opts = &block_crypto_create_opts_luks,
256
+ .amend_opts = &block_crypto_amend_opts_luks,
257
258
.bdrv_reopen_prepare = block_crypto_reopen_prepare,
259
.bdrv_refresh_limits = block_crypto_refresh_limits,
260
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_crypto_luks = {
261
.bdrv_measure = block_crypto_measure,
262
.bdrv_get_info = block_crypto_get_info_luks,
263
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
264
+ .bdrv_amend_options = block_crypto_amend_options_luks,
265
266
.is_format = true,
267
268
--
132
--
269
2.26.2
133
2.29.2
270
134
271
135
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New 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.
1
5
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Some qcow2 create options can't be used for amend.
3
The further change of moving backup to be a one block-copy call will
4
Remove them from the qcow2 create options and add generic logic to detect
4
make copying chunk-size and cluster-size two separate things. So, even
5
such options in qemu-img
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.
6
9
7
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
10
So, let's explicitly specify, what test wants: set max-chunk to 64K, so
8
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
11
that one iteration is 64K. Note, that we don't need to limit
9
Message-Id: <20200608094030.670121-6-mlevitsk@redhat.com>
12
max-workers, as block-copy rate limiter will handle the situation and
10
[mreitz: Dropped some iotests reference output hunks that became
13
wouldn't start new workers when speed limit is obviously reached.
11
unnecessary thanks to
14
12
"iotests: Make _filter_img_create more active"]
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>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
19
---
15
block/qcow2.c | 138 +++++++++-----------------------
20
tests/qemu-iotests/185 | 3 ++-
16
qemu-img.c | 18 ++++-
21
tests/qemu-iotests/185.out | 3 ++-
17
tests/qemu-iotests/049.out | 102 ++++++++++++------------
22
2 files changed, 4 insertions(+), 2 deletions(-)
18
tests/qemu-iotests/061.out | 12 ++-
19
tests/qemu-iotests/082.out | 158 ++++---------------------------------
20
tests/qemu-iotests/085.out | 38 ++++-----
21
tests/qemu-iotests/144.out | 4 +-
22
tests/qemu-iotests/182.out | 2 +-
23
tests/qemu-iotests/185.out | 8 +-
24
tests/qemu-iotests/255.out | 8 +-
25
tests/qemu-iotests/274.out | 46 +++++------
26
tests/qemu-iotests/280.out | 2 +-
27
12 files changed, 183 insertions(+), 353 deletions(-)
28
23
29
diff --git a/block/qcow2.c b/block/qcow2.c
24
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
30
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100755
31
--- a/block/qcow2.c
26
--- a/tests/qemu-iotests/185
32
+++ b/block/qcow2.c
27
+++ b/tests/qemu-iotests/185
33
@@ -XXX,XX +XXX,XX @@ static int qcow2_change_backing_file(BlockDriverState *bs,
28
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
34
return qcow2_update_header(bs);
29
'target': '$TEST_IMG.copy',
35
}
30
'format': '$IMGFMT',
36
31
'sync': 'full',
37
-static int qcow2_crypt_method_from_format(const char *encryptfmt)
32
- 'speed': 65536 } }" \
38
-{
33
+ 'speed': 65536,
39
- if (g_str_equal(encryptfmt, "luks")) {
34
+ 'x-perf': {'max-chunk': 65536} } }" \
40
- return QCOW_CRYPT_LUKS;
35
"return"
41
- } else if (g_str_equal(encryptfmt, "aes")) {
36
42
- return QCOW_CRYPT_AES;
37
# If we don't sleep here 'quit' command races with disk I/O
43
- } else {
44
- return -EINVAL;
45
- }
46
-}
47
-
48
static int qcow2_set_up_encryption(BlockDriverState *bs,
49
QCryptoBlockCreateOptions *cryptoopts,
50
Error **errp)
51
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
52
bool lazy_refcounts = s->use_lazy_refcounts;
53
bool data_file_raw = data_file_is_raw(bs);
54
const char *compat = NULL;
55
- uint64_t cluster_size = s->cluster_size;
56
- bool encrypt;
57
- int encformat;
58
int refcount_bits = s->refcount_bits;
59
int ret;
60
QemuOptDesc *desc = opts->list->desc;
61
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
62
error_setg(errp, "Unknown compatibility level %s", compat);
63
return -EINVAL;
64
}
65
- } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
66
- error_setg(errp, "Cannot change preallocation mode");
67
- return -ENOTSUP;
68
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
69
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
70
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) {
71
backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE);
72
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) {
73
backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
74
- } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
75
- encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
76
- !!s->crypto);
77
-
78
- if (encrypt != !!s->crypto) {
79
- error_setg(errp,
80
- "Changing the encryption flag is not supported");
81
- return -ENOTSUP;
82
- }
83
- } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) {
84
- encformat = qcow2_crypt_method_from_format(
85
- qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT));
86
-
87
- if (encformat != s->crypt_method_header) {
88
- error_setg(errp,
89
- "Changing the encryption format is not supported");
90
- return -ENOTSUP;
91
- }
92
- } else if (g_str_has_prefix(desc->name, "encrypt.")) {
93
- error_setg(errp,
94
- "Changing the encryption parameters is not supported");
95
- return -ENOTSUP;
96
- } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
97
- cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
98
- cluster_size);
99
- if (cluster_size != s->cluster_size) {
100
- error_setg(errp, "Changing the cluster size is not supported");
101
- return -ENOTSUP;
102
- }
103
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
104
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
105
lazy_refcounts);
106
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
107
"images");
108
return -EINVAL;
109
}
110
- } else if (!strcmp(desc->name, BLOCK_OPT_COMPRESSION_TYPE)) {
111
- const char *ct_name =
112
- qemu_opt_get(opts, BLOCK_OPT_COMPRESSION_TYPE);
113
- int compression_type =
114
- qapi_enum_parse(&Qcow2CompressionType_lookup, ct_name, -1,
115
- NULL);
116
- if (compression_type == -1) {
117
- error_setg(errp, "Unknown compression type: %s", ct_name);
118
- return -ENOTSUP;
119
- }
120
-
121
- if (compression_type != s->compression_type) {
122
- error_setg(errp, "Changing the compression type "
123
- "is not supported");
124
- return -ENOTSUP;
125
- }
126
} else {
127
/* if this point is reached, this probably means a new option was
128
* added without having it covered here */
129
@@ -XXX,XX +XXX,XX @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
130
.help = "The external data file must stay valid " \
131
"as a raw image" \
132
}, \
133
- { \
134
- .name = BLOCK_OPT_ENCRYPT, \
135
- .type = QEMU_OPT_BOOL, \
136
- .help = "Encrypt the image with format 'aes'. (Deprecated " \
137
- "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \
138
- }, \
139
- { \
140
- .name = BLOCK_OPT_ENCRYPT_FORMAT, \
141
- .type = QEMU_OPT_STRING, \
142
- .help = "Encrypt the image, format choices: 'aes', 'luks'", \
143
- }, \
144
- BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \
145
- "ID of secret providing qcow AES key or LUKS passphrase"), \
146
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \
147
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \
148
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \
149
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \
150
- BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \
151
- BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \
152
- { \
153
- .name = BLOCK_OPT_CLUSTER_SIZE, \
154
- .type = QEMU_OPT_SIZE, \
155
- .help = "qcow2 cluster size", \
156
- .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \
157
- }, \
158
- { \
159
- .name = BLOCK_OPT_PREALLOC, \
160
- .type = QEMU_OPT_STRING, \
161
- .help = "Preallocation mode (allowed values: off, " \
162
- "metadata, falloc, full)" \
163
- }, \
164
{ \
165
.name = BLOCK_OPT_LAZY_REFCOUNTS, \
166
.type = QEMU_OPT_BOOL, \
167
@@ -XXX,XX +XXX,XX @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
168
.type = QEMU_OPT_NUMBER, \
169
.help = "Width of a reference count entry in bits", \
170
.def_value_str = "16" \
171
- }, \
172
- { \
173
- .name = BLOCK_OPT_COMPRESSION_TYPE, \
174
- .type = QEMU_OPT_STRING, \
175
- .help = "Compression method used for image cluster " \
176
- "compression", \
177
- .def_value_str = "zlib" \
178
}
179
180
static QemuOptsList qcow2_create_opts = {
181
.name = "qcow2-create-opts",
182
.head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
183
.desc = {
184
+ { \
185
+ .name = BLOCK_OPT_ENCRYPT, \
186
+ .type = QEMU_OPT_BOOL, \
187
+ .help = "Encrypt the image with format 'aes'. (Deprecated " \
188
+ "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \
189
+ }, \
190
+ { \
191
+ .name = BLOCK_OPT_ENCRYPT_FORMAT, \
192
+ .type = QEMU_OPT_STRING, \
193
+ .help = "Encrypt the image, format choices: 'aes', 'luks'", \
194
+ }, \
195
+ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \
196
+ "ID of secret providing qcow AES key or LUKS passphrase"), \
197
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \
198
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \
199
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \
200
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \
201
+ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \
202
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \
203
+ { \
204
+ .name = BLOCK_OPT_CLUSTER_SIZE, \
205
+ .type = QEMU_OPT_SIZE, \
206
+ .help = "qcow2 cluster size", \
207
+ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \
208
+ }, \
209
+ { \
210
+ .name = BLOCK_OPT_PREALLOC, \
211
+ .type = QEMU_OPT_STRING, \
212
+ .help = "Preallocation mode (allowed values: off, " \
213
+ "metadata, falloc, full)" \
214
+ }, \
215
+ { \
216
+ .name = BLOCK_OPT_COMPRESSION_TYPE, \
217
+ .type = QEMU_OPT_STRING, \
218
+ .help = "Compression method used for image cluster " \
219
+ "compression", \
220
+ .def_value_str = "zlib" \
221
+ },
222
QCOW_COMMON_OPTIONS,
223
{ /* end of list */ }
224
}
225
diff --git a/qemu-img.c b/qemu-img.c
226
index XXXXXXX..XXXXXXX 100644
227
--- a/qemu-img.c
228
+++ b/qemu-img.c
229
@@ -XXX,XX +XXX,XX @@ static int print_amend_option_help(const char *format)
230
/* Every driver supporting amendment must have amend_opts */
231
assert(drv->amend_opts);
232
233
- printf("Creation options for '%s':\n", format);
234
+ printf("Amend options for '%s':\n", format);
235
qemu_opts_print_help(drv->amend_opts, false);
236
- printf("\nNote that not all of these options may be amendable.\n");
237
return 0;
238
}
239
240
@@ -XXX,XX +XXX,XX @@ static int img_amend(int argc, char **argv)
241
amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts);
242
opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort);
243
qemu_opts_do_parse(opts, options, NULL, &err);
244
+
245
if (err) {
246
+ /* Try to parse options using the create options */
247
+ Error *err1 = NULL;
248
+ amend_opts = qemu_opts_append(amend_opts, bs->drv->create_opts);
249
+ qemu_opts_del(opts);
250
+ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort);
251
+ qemu_opts_do_parse(opts, options, NULL, &err1);
252
+
253
+ if (!err1) {
254
+ error_append_hint(&err,
255
+ "This option is only supported for image creation\n");
256
+ } else {
257
+ error_free(err1);
258
+ }
259
+
260
error_report_err(err);
261
ret = -1;
262
goto out;
263
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
264
index XXXXXXX..XXXXXXX 100644
265
--- a/tests/qemu-iotests/049.out
266
+++ b/tests/qemu-iotests/049.out
267
@@ -XXX,XX +XXX,XX @@ QA output created by 049
268
== 1. Traditional size parameter ==
269
270
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024
271
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
272
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
273
274
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b
275
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
276
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
277
278
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k
279
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
280
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
281
282
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K
283
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
284
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
285
286
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M
287
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
288
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16
289
290
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G
291
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
292
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16
293
294
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T
295
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
296
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16
297
298
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0
299
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
300
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
301
302
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b
303
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
304
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
305
306
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k
307
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
308
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
309
310
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K
311
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
312
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
313
314
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M
315
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
316
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16
317
318
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G
319
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
320
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16
321
322
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T
323
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
324
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16
325
326
== 2. Specifying size via -o ==
327
328
qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2
329
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
330
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
331
332
qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2
333
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
334
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
335
336
qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2
337
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
338
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
339
340
qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2
341
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
342
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
343
344
qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2
345
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
346
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16
347
348
qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2
349
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
350
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16
351
352
qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2
353
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
354
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16
355
356
qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2
357
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
358
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
359
360
qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2
361
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
362
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
363
364
qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2
365
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
366
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
367
368
qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2
369
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
370
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
371
372
qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2
373
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
374
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16
375
376
qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2
377
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
378
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16
379
380
qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2
381
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
382
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16
383
384
== 3. Invalid sizes ==
385
386
@@ -XXX,XX +XXX,XX @@ qemu-img: TEST_DIR/t.qcow2: The image size must be specified only once
387
== Check correct interpretation of suffixes for cluster size ==
388
389
qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M
390
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
391
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
392
393
qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M
394
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
395
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
396
397
qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M
398
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
399
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
400
401
qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M
402
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
403
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
404
405
qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M
406
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1048576 lazy_refcounts=off refcount_bits=16 compression_type=zlib
407
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1048576 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
408
409
qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M
410
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
411
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
412
413
qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M
414
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
415
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
416
417
qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M
418
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=512 lazy_refcounts=off refcount_bits=16 compression_type=zlib
419
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
420
421
qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M
422
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=512 lazy_refcounts=off refcount_bits=16 compression_type=zlib
423
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=512 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
424
425
qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M
426
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=524288 lazy_refcounts=off refcount_bits=16 compression_type=zlib
427
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=524288 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
428
429
== Check compat level option ==
430
431
qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M
432
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
433
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16
434
435
qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
436
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
437
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16
438
439
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
440
qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42'
441
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
442
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16
443
444
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
445
qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar'
446
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
447
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16
448
449
== Check preallocation option ==
450
451
qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M
452
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=off lazy_refcounts=off refcount_bits=16 compression_type=zlib
453
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
454
455
qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
456
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16 compression_type=zlib
457
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=metadata compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
458
459
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
460
qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234'
461
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16 compression_type=zlib
462
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
463
464
== Check encryption option ==
465
466
qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M
467
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
468
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=off cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
469
470
qemu-img create -f qcow2 --object secret,id=sec0,data=123456 -o encryption=on,encrypt.key-secret=sec0 TEST_DIR/t.qcow2 64M
471
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on encrypt.key-secret=sec0 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
472
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 encryption=on encrypt.key-secret=sec0 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
473
474
== Check lazy_refcounts option (only with v3) ==
475
476
qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M
477
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
478
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16
479
480
qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M
481
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=on refcount_bits=16 compression_type=zlib
482
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=on refcount_bits=16
483
484
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
485
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
486
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16
487
488
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
489
qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
490
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16 compression_type=zlib
491
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=on refcount_bits=16
492
493
*** done
494
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
495
index XXXXXXX..XXXXXXX 100644
496
--- a/tests/qemu-iotests/061.out
497
+++ b/tests/qemu-iotests/061.out
498
@@ -XXX,XX +XXX,XX @@ qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (
499
qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
500
qemu-img: Unknown compatibility level 0.42
501
qemu-img: Invalid parameter 'foo'
502
-qemu-img: Changing the cluster size is not supported
503
-qemu-img: Changing the encryption flag is not supported
504
-qemu-img: Cannot change preallocation mode
505
+qemu-img: Invalid parameter 'cluster_size'
506
+This option is only supported for image creation
507
+qemu-img: Invalid parameter 'encryption'
508
+This option is only supported for image creation
509
+qemu-img: Invalid parameter 'preallocation'
510
+This option is only supported for image creation
511
512
=== Testing correct handling of unset value ===
513
514
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
515
Should work:
516
Should not work:
517
-qemu-img: Changing the cluster size is not supported
518
+qemu-img: Invalid parameter 'cluster_size'
519
+This option is only supported for image creation
520
521
=== Testing zero expansion on inactive clusters ===
522
523
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
524
index XXXXXXX..XXXXXXX 100644
525
--- a/tests/qemu-iotests/082.out
526
+++ b/tests/qemu-iotests/082.out
527
@@ -XXX,XX +XXX,XX @@ QA output created by 082
528
=== create: Options specified more than once ===
529
530
Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M
531
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
532
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
533
image: TEST_DIR/t.IMGFMT
534
file format: IMGFMT
535
virtual size: 128 MiB (134217728 bytes)
536
cluster_size: 65536
537
538
Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M
539
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=4096 lazy_refcounts=on refcount_bits=16 compression_type=zlib
540
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=4096 compression_type=zlib size=134217728 lazy_refcounts=on refcount_bits=16
541
image: TEST_DIR/t.IMGFMT
542
file format: IMGFMT
543
virtual size: 128 MiB (134217728 bytes)
544
@@ -XXX,XX +XXX,XX @@ Format specific information:
545
corrupt: false
546
547
Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M
548
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=8192 lazy_refcounts=on refcount_bits=16 compression_type=zlib
549
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=8192 compression_type=zlib size=134217728 lazy_refcounts=on refcount_bits=16
550
image: TEST_DIR/t.IMGFMT
551
file format: IMGFMT
552
virtual size: 128 MiB (134217728 bytes)
553
@@ -XXX,XX +XXX,XX @@ Format specific information:
554
corrupt: false
555
556
Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M
557
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=8192 lazy_refcounts=off refcount_bits=16 compression_type=zlib
558
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=8192 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
559
image: TEST_DIR/t.IMGFMT
560
file format: IMGFMT
561
virtual size: 128 MiB (134217728 bytes)
562
@@ -XXX,XX +XXX,XX @@ Supported options:
563
size=<size> - Virtual disk size
564
565
Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
566
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,help cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
567
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,help lazy_refcounts=off refcount_bits=16
568
569
Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M
570
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,? cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
571
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,? lazy_refcounts=off refcount_bits=16
572
573
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M
574
qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2,
575
@@ -XXX,XX +XXX,XX @@ qemu-img: Format driver 'bochs' does not support image creation
576
=== convert: Options specified more than once ===
577
578
Testing: create -f qcow2 TEST_DIR/t.qcow2 128M
579
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
580
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
581
582
Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
583
image: TEST_DIR/t.IMGFMT.base
584
@@ -XXX,XX +XXX,XX @@ cluster_size: 65536
585
=== amend: help for -o ===
586
587
Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2
588
-Creation options for 'qcow2':
589
+Amend options for 'qcow2':
590
backing_file=<str> - File name of a base image
591
backing_fmt=<str> - Image format of the base image
592
- cluster_size=<size> - qcow2 cluster size
593
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
594
- compression_type=<str> - Compression method used for image cluster compression
595
data_file=<str> - File name of an external data file
596
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
597
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
598
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
599
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
600
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
601
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
602
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
603
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
604
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
605
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
606
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
607
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
608
refcount_bits=<num> - Width of a reference count entry in bits
609
size=<size> - Virtual disk size
610
611
-Note that not all of these options may be amendable.
612
-
613
Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
614
-Creation options for 'qcow2':
615
+Amend options for 'qcow2':
616
backing_file=<str> - File name of a base image
617
backing_fmt=<str> - Image format of the base image
618
- cluster_size=<size> - qcow2 cluster size
619
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
620
- compression_type=<str> - Compression method used for image cluster compression
621
data_file=<str> - File name of an external data file
622
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
623
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
624
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
625
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
626
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
627
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
628
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
629
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
630
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
631
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
632
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
633
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
634
refcount_bits=<num> - Width of a reference count entry in bits
635
size=<size> - Virtual disk size
636
637
-Note that not all of these options may be amendable.
638
-
639
Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
640
-Creation options for 'qcow2':
641
+Amend options for 'qcow2':
642
backing_file=<str> - File name of a base image
643
backing_fmt=<str> - Image format of the base image
644
- cluster_size=<size> - qcow2 cluster size
645
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
646
- compression_type=<str> - Compression method used for image cluster compression
647
data_file=<str> - File name of an external data file
648
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
649
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
650
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
651
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
652
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
653
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
654
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
655
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
656
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
657
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
658
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
659
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
660
refcount_bits=<num> - Width of a reference count entry in bits
661
size=<size> - Virtual disk size
662
663
-Note that not all of these options may be amendable.
664
-
665
Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
666
-Creation options for 'qcow2':
667
+Amend options for 'qcow2':
668
backing_file=<str> - File name of a base image
669
backing_fmt=<str> - Image format of the base image
670
- cluster_size=<size> - qcow2 cluster size
671
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
672
- compression_type=<str> - Compression method used for image cluster compression
673
data_file=<str> - File name of an external data file
674
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
675
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
676
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
677
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
678
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
679
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
680
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
681
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
682
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
683
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
684
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
685
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
686
refcount_bits=<num> - Width of a reference count entry in bits
687
size=<size> - Virtual disk size
688
689
-Note that not all of these options may be amendable.
690
-
691
Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
692
-Creation options for 'qcow2':
693
+Amend options for 'qcow2':
694
backing_file=<str> - File name of a base image
695
backing_fmt=<str> - Image format of the base image
696
- cluster_size=<size> - qcow2 cluster size
697
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
698
- compression_type=<str> - Compression method used for image cluster compression
699
data_file=<str> - File name of an external data file
700
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
701
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
702
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
703
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
704
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
705
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
706
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
707
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
708
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
709
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
710
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
711
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
712
refcount_bits=<num> - Width of a reference count entry in bits
713
size=<size> - Virtual disk size
714
715
-Note that not all of these options may be amendable.
716
-
717
Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
718
-Creation options for 'qcow2':
719
+Amend options for 'qcow2':
720
backing_file=<str> - File name of a base image
721
backing_fmt=<str> - Image format of the base image
722
- cluster_size=<size> - qcow2 cluster size
723
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
724
- compression_type=<str> - Compression method used for image cluster compression
725
data_file=<str> - File name of an external data file
726
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
727
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
728
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
729
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
730
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
731
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
732
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
733
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
734
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
735
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
736
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
737
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
738
refcount_bits=<num> - Width of a reference count entry in bits
739
size=<size> - Virtual disk size
740
741
-Note that not all of these options may be amendable.
742
-
743
Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
744
-Creation options for 'qcow2':
745
+Amend options for 'qcow2':
746
backing_file=<str> - File name of a base image
747
backing_fmt=<str> - Image format of the base image
748
- cluster_size=<size> - qcow2 cluster size
749
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
750
- compression_type=<str> - Compression method used for image cluster compression
751
data_file=<str> - File name of an external data file
752
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
753
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
754
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
755
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
756
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
757
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
758
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
759
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
760
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
761
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
762
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
763
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
764
refcount_bits=<num> - Width of a reference count entry in bits
765
size=<size> - Virtual disk size
766
767
-Note that not all of these options may be amendable.
768
-
769
Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
770
-Creation options for 'qcow2':
771
+Amend options for 'qcow2':
772
backing_file=<str> - File name of a base image
773
backing_fmt=<str> - Image format of the base image
774
- cluster_size=<size> - qcow2 cluster size
775
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
776
- compression_type=<str> - Compression method used for image cluster compression
777
data_file=<str> - File name of an external data file
778
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
779
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
780
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
781
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
782
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
783
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
784
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
785
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
786
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
787
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
788
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
789
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
790
refcount_bits=<num> - Width of a reference count entry in bits
791
size=<size> - Virtual disk size
792
793
-Note that not all of these options may be amendable.
794
-
795
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
796
797
Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2
798
@@ -XXX,XX +XXX,XX @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/
799
qemu-img: Invalid option list: ,,
800
801
Testing: amend -f qcow2 -o help
802
-Creation options for 'qcow2':
803
+Amend options for 'qcow2':
804
backing_file=<str> - File name of a base image
805
backing_fmt=<str> - Image format of the base image
806
- cluster_size=<size> - qcow2 cluster size
807
compat=<str> - Compatibility level (v2 [0.10] or v3 [1.1])
808
- compression_type=<str> - Compression method used for image cluster compression
809
data_file=<str> - File name of an external data file
810
data_file_raw=<bool (on/off)> - The external data file must stay valid as a raw image
811
- encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
812
- encrypt.cipher-mode=<str> - Name of encryption cipher mode
813
- encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
814
- encrypt.hash-alg=<str> - Name of encryption hash algorithm
815
- encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
816
- encrypt.ivgen-alg=<str> - Name of IV generator algorithm
817
- encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
818
- encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
819
- encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
820
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
821
- preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
822
refcount_bits=<num> - Width of a reference count entry in bits
823
size=<size> - Virtual disk size
824
825
-Note that not all of these options may be amendable.
826
-
827
Testing: amend -o help
828
qemu-img: Expecting one image file name
829
830
diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out
831
index XXXXXXX..XXXXXXX 100644
832
--- a/tests/qemu-iotests/085.out
833
+++ b/tests/qemu-iotests/085.out
834
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728
835
=== Create a single snapshot on virtio0 ===
836
837
{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', 'snapshot-file':'TEST_DIR/1-snapshot-v0.IMGFMT', 'format': 'IMGFMT' } }
838
-Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.1 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
839
+Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2.1 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
840
{"return": {}}
841
842
=== Invalid command - missing device and nodename ===
843
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file
844
=== Create several transactional group snapshots ===
845
846
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/2-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/2-snapshot-v1.IMGFMT' } } ] } }
847
-Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/1-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
848
-Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
849
+Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/1-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
850
+Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2.2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
851
{"return": {}}
852
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/3-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/3-snapshot-v1.IMGFMT' } } ] } }
853
-Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
854
-Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
855
+Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/2-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
856
+Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/2-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
857
{"return": {}}
858
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/4-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/4-snapshot-v1.IMGFMT' } } ] } }
859
-Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/3-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
860
-Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/3-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
861
+Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/3-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
862
+Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/3-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
863
{"return": {}}
864
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/5-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/5-snapshot-v1.IMGFMT' } } ] } }
865
-Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/4-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
866
-Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/4-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
867
+Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/4-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
868
+Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/4-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
869
{"return": {}}
870
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/6-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/6-snapshot-v1.IMGFMT' } } ] } }
871
-Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/5-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
872
-Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/5-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
873
+Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/5-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
874
+Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/5-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
875
{"return": {}}
876
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/7-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/7-snapshot-v1.IMGFMT' } } ] } }
877
-Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/6-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
878
-Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/6-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
879
+Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/6-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
880
+Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/6-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
881
{"return": {}}
882
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/8-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/8-snapshot-v1.IMGFMT' } } ] } }
883
-Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/7-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
884
-Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/7-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
885
+Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/7-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
886
+Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/7-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
887
{"return": {}}
888
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/9-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/9-snapshot-v1.IMGFMT' } } ] } }
889
-Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/8-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
890
-Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/8-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
891
+Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/8-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
892
+Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/8-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
893
{"return": {}}
894
{ 'execute': 'transaction', 'arguments': {'actions': [ { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio0', 'snapshot-file': 'TEST_DIR/10-snapshot-v0.IMGFMT' } }, { 'type': 'blockdev-snapshot-sync', 'data' : { 'device': 'virtio1', 'snapshot-file': 'TEST_DIR/10-snapshot-v1.IMGFMT' } } ] } }
895
-Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v0.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
896
-Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v1.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
897
+Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/9-snapshot-v0.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
898
+Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/9-snapshot-v1.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
899
{"return": {}}
900
901
=== Create a couple of snapshots using blockdev-snapshot ===
902
diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out
903
index XXXXXXX..XXXXXXX 100644
904
--- a/tests/qemu-iotests/144.out
905
+++ b/tests/qemu-iotests/144.out
906
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=536870912
907
{ 'execute': 'qmp_capabilities' }
908
{"return": {}}
909
{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', 'snapshot-file':'TEST_DIR/tmp.IMGFMT', 'format': 'IMGFMT' } }
910
-Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
911
+Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
912
{"return": {}}
913
914
=== Performing block-commit on active layer ===
915
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/
916
=== Performing Live Snapshot 2 ===
917
918
{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', 'snapshot-file':'TEST_DIR/tmp2.IMGFMT', 'format': 'IMGFMT' } }
919
-Formatting 'TEST_DIR/tmp2.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
920
+Formatting 'TEST_DIR/tmp2.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=536870912 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
921
{"return": {}}
922
*** done
923
diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out
924
index XXXXXXX..XXXXXXX 100644
925
--- a/tests/qemu-iotests/182.out
926
+++ b/tests/qemu-iotests/182.out
927
@@ -XXX,XX +XXX,XX @@ Is another process using the image [TEST_DIR/t.qcow2]?
928
{'execute': 'blockdev-add', 'arguments': { 'node-name': 'node0', 'driver': 'file', 'filename': 'TEST_DIR/t.IMGFMT', 'locking': 'on' } }
929
{"return": {}}
930
{'execute': 'blockdev-snapshot-sync', 'arguments': { 'node-name': 'node0', 'snapshot-file': 'TEST_DIR/t.IMGFMT.overlay', 'snapshot-node-name': 'node1' } }
931
-Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
932
+Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 cluster_size=65536 compression_type=zlib size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file lazy_refcounts=off refcount_bits=16
933
{"return": {}}
934
{'execute': 'blockdev-add', 'arguments': { 'node-name': 'node1', 'driver': 'file', 'filename': 'TEST_DIR/t.IMGFMT', 'locking': 'on' } }
935
{"return": {}}
936
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
38
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
937
index XXXXXXX..XXXXXXX 100644
39
index XXXXXXX..XXXXXXX 100644
938
--- a/tests/qemu-iotests/185.out
40
--- a/tests/qemu-iotests/185.out
939
+++ b/tests/qemu-iotests/185.out
41
+++ b/tests/qemu-iotests/185.out
940
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
42
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
941
=== Creating backing chain ===
43
'target': 'TEST_DIR/t.IMGFMT.copy',
942
44
'format': 'IMGFMT',
943
{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'disk', 'snapshot-file': 'TEST_DIR/t.IMGFMT.mid', 'format': 'IMGFMT', 'mode': 'absolute-paths' } }
45
'sync': 'full',
944
-Formatting 'TEST_DIR/t.qcow2.mid', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.qcow2.base backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
46
- 'speed': 65536 } }
945
+Formatting 'TEST_DIR/t.qcow2.mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 backing_file=TEST_DIR/t.qcow2.base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
47
+ 'speed': 65536,
946
{"return": {}}
48
+ 'x-perf': { 'max-chunk': 65536 } } }
947
{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'qemu-io disk "write 0 4M"' } }
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
948
wrote 4194304/4194304 bytes at offset 0
949
4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
950
{"return": ""}
951
{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'disk', 'snapshot-file': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'absolute-paths' } }
952
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.qcow2.mid backing_fmt=qcow2 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
953
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 backing_file=TEST_DIR/t.qcow2.mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
954
{"return": {}}
955
956
=== Start commit job and exit qemu ===
957
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
958
{ 'execute': 'qmp_capabilities' }
959
{"return": {}}
960
{ 'execute': 'drive-mirror', 'arguments': { 'device': 'disk', 'target': 'TEST_DIR/t.IMGFMT.copy', 'format': 'IMGFMT', 'sync': 'full', 'speed': 65536 } }
961
-Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
962
+Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
963
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
50
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
964
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
51
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
965
{"return": {}}
966
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l
967
{ 'execute': 'qmp_capabilities' }
968
{"return": {}}
969
{ 'execute': 'drive-backup', 'arguments': { 'device': 'disk', 'target': 'TEST_DIR/t.IMGFMT.copy', 'format': 'IMGFMT', 'sync': 'full', 'speed': 65536 } }
970
-Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
971
+Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
972
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
973
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
974
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
975
diff --git a/tests/qemu-iotests/255.out b/tests/qemu-iotests/255.out
976
index XXXXXXX..XXXXXXX 100644
977
--- a/tests/qemu-iotests/255.out
978
+++ b/tests/qemu-iotests/255.out
979
@@ -XXX,XX +XXX,XX @@ Finishing a commit job with background reads
980
981
=== Create backing chain and start VM ===
982
983
-Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
984
+Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
985
986
-Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
987
+Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
988
989
=== Start background read requests ===
990
991
@@ -XXX,XX +XXX,XX @@ Closing the VM while a job is being cancelled
992
993
=== Create images and start VM ===
994
995
-Formatting 'TEST_DIR/PID-src.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
996
+Formatting 'TEST_DIR/PID-src.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
997
998
-Formatting 'TEST_DIR/PID-dst.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
999
+Formatting 'TEST_DIR/PID-dst.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
1000
1001
wrote 1048576/1048576 bytes at offset 0
1002
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1003
diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out
1004
index XXXXXXX..XXXXXXX 100644
1005
--- a/tests/qemu-iotests/274.out
1006
+++ b/tests/qemu-iotests/274.out
1007
@@ -XXX,XX +XXX,XX @@
1008
== Commit tests ==
1009
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1010
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
1011
1012
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1013
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1014
1015
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1016
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16
1017
1018
wrote 2097152/2097152 bytes at offset 0
1019
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1020
@@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576
1021
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1022
1023
=== Testing HMP commit (top -> mid) ===
1024
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1025
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
1026
1027
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1028
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1029
1030
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1031
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16
1032
1033
wrote 2097152/2097152 bytes at offset 0
1034
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1035
@@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576
1036
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1037
1038
=== Testing QMP active commit (top -> mid) ===
1039
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1040
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
1041
1042
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1043
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1044
1045
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1046
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16
1047
1048
wrote 2097152/2097152 bytes at offset 0
1049
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1050
@@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576
1051
1052
== Resize tests ==
1053
=== preallocation=off ===
1054
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=6442450944 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1055
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16
1056
1057
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=1073741824 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1058
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1059
1060
wrote 65536/65536 bytes at offset 5368709120
1061
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1062
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 5368709120
1063
{ "start": 1073741824, "length": 7516192768, "depth": 0, "zero": true, "data": false}]
1064
1065
=== preallocation=metadata ===
1066
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=34359738368 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1067
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=34359738368 lazy_refcounts=off refcount_bits=16
1068
1069
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=32212254720 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1070
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=32212254720 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1071
1072
wrote 65536/65536 bytes at offset 33285996544
1073
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1074
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 33285996544
1075
{ "start": 34896609280, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 2685075456}]
1076
1077
=== preallocation=falloc ===
1078
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=10485760 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1079
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=10485760 lazy_refcounts=off refcount_bits=16
1080
1081
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=5242880 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1082
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=5242880 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1083
1084
wrote 65536/65536 bytes at offset 9437184
1085
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1086
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 9437184
1087
{ "start": 5242880, "length": 10485760, "depth": 0, "zero": false, "data": true, "offset": 327680}]
1088
1089
=== preallocation=full ===
1090
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=16777216 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1091
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=16777216 lazy_refcounts=off refcount_bits=16
1092
1093
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=8388608 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1094
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=8388608 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1095
1096
wrote 65536/65536 bytes at offset 11534336
1097
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1098
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 11534336
1099
{ "start": 8388608, "length": 4194304, "depth": 0, "zero": false, "data": true, "offset": 327680}]
1100
1101
=== preallocation=off ===
1102
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=393216 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1103
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=393216 lazy_refcounts=off refcount_bits=16
1104
1105
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=259072 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1106
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=259072 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1107
1108
wrote 65536/65536 bytes at offset 259072
1109
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1110
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 259072
1111
{ "start": 262144, "length": 262144, "depth": 0, "zero": true, "data": false}]
1112
1113
=== preallocation=off ===
1114
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=409600 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1115
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=409600 lazy_refcounts=off refcount_bits=16
1116
1117
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=262144 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1118
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1119
1120
wrote 65536/65536 bytes at offset 344064
1121
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1122
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 344064
1123
{ "start": 262144, "length": 262144, "depth": 0, "zero": true, "data": false}]
1124
1125
=== preallocation=off ===
1126
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=524288 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1127
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=524288 lazy_refcounts=off refcount_bits=16
1128
1129
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=262144 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1130
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
1131
1132
wrote 65536/65536 bytes at offset 446464
1133
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1134
diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out
1135
index XXXXXXX..XXXXXXX 100644
1136
--- a/tests/qemu-iotests/280.out
1137
+++ b/tests/qemu-iotests/280.out
1138
@@ -XXX,XX +XXX,XX @@
1139
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
1140
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
1141
1142
=== Launch VM ===
1143
Enabling migration QMP events on VM...
1144
--
52
--
1145
2.26.2
53
2.29.2
1146
54
1147
55
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
820c6bee534ec3b added testing of qcow2.py into 291, and it breaks 291
3
Iotest 257 dumps a lot of in-progress information of backup job, such
4
with external data file. Actually, 291 is bad place for qcow2.py
4
as offset and bitmap dirtiness. Further commit will move backup to be
5
testing, better add a separate test.
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.
6
9
7
For now, drop qcow2.py testing from 291 to fix the regression.
8
9
Fixes: 820c6bee534ec3b
10
Reported-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20200618154052.8629-1-vsementsov@virtuozzo.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Message-Id: <20210116214705.822267-15-vsementsov@virtuozzo.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
14
---
16
tests/qemu-iotests/291 | 4 ----
15
tests/qemu-iotests/257 | 1 +
17
tests/qemu-iotests/291.out | 33 ---------------------------------
16
tests/qemu-iotests/257.out | 306 ++++++++++++++++++-------------------
18
2 files changed, 37 deletions(-)
17
2 files changed, 154 insertions(+), 153 deletions(-)
19
18
20
diff --git a/tests/qemu-iotests/291 b/tests/qemu-iotests/291
19
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
21
index XXXXXXX..XXXXXXX 100755
20
index XXXXXXX..XXXXXXX 100755
22
--- a/tests/qemu-iotests/291
21
--- a/tests/qemu-iotests/257
23
+++ b/tests/qemu-iotests/291
22
+++ b/tests/qemu-iotests/257
24
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'w 1M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
23
@@ -XXX,XX +XXX,XX @@ def blockdev_backup(vm, device, target, sync, **kwargs):
25
$QEMU_IMG bitmap --disable -f $IMGFMT "$TEST_IMG" b1
24
target=target,
26
$QEMU_IMG bitmap --enable -f $IMGFMT "$TEST_IMG" b2
25
sync=sync,
27
$QEMU_IO -c 'w 2M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
26
filter_node_name='backup-top',
28
-echo "Check resulting qcow2 header extensions:"
27
+ x_perf={'max-workers': 1},
29
-$PYTHON qcow2.py "$TEST_IMG" dump-header-exts
28
**kwargs)
30
29
return result
31
echo
30
32
echo "=== Bitmap preservation not possible to non-qcow2 ==="
31
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
33
@@ -XXX,XX +XXX,XX @@ $QEMU_IMG bitmap --merge tmp -f $IMGFMT "$TEST_IMG" b0
34
$QEMU_IMG bitmap --remove --image-opts \
35
driver=$IMGFMT,file.driver=file,file.filename="$TEST_IMG" tmp
36
_img_info --format-specific
37
-echo "Check resulting qcow2 header extensions:"
38
-$PYTHON qcow2.py "$TEST_IMG" dump-header-exts
39
40
echo
41
echo "=== Check bitmap contents ==="
42
diff --git a/tests/qemu-iotests/291.out b/tests/qemu-iotests/291.out
43
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
44
--- a/tests/qemu-iotests/291.out
33
--- a/tests/qemu-iotests/257.out
45
+++ b/tests/qemu-iotests/291.out
34
+++ b/tests/qemu-iotests/257.out
46
@@ -XXX,XX +XXX,XX @@ wrote 1048576/1048576 bytes at offset 1048576
35
@@ -XXX,XX +XXX,XX @@ write -P0x76 0x3ff0000 0x10000
47
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
36
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
48
wrote 1048576/1048576 bytes at offset 2097152
37
{"return": {}}
49
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
38
{}
50
-Check resulting qcow2 header extensions:
39
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
51
-Header extension:
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}}}
52
-magic 0xe2792aca (Backing format)
41
{"return": {}}
53
-length 5
42
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
54
-data 'qcow2'
43
55
-
44
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
56
-Header extension:
45
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
57
-magic 0x6803f857 (Feature table)
46
{"return": {}}
58
-length 336
47
{}
59
-data <binary>
48
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
60
-
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}}}
61
-Header extension:
50
{"return": {}}
62
-magic 0x23852875 (Bitmaps)
51
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
63
-length 24
52
64
-nb_bitmaps 2
53
@@ -XXX,XX +XXX,XX @@ expecting 6 dirty sectors; have 6. OK!
65
-reserved32 0
54
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
66
-bitmap_directory_size 0x40
55
{"return": {}}
67
-bitmap_directory_offset 0x510000
56
{}
68
-
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"}}
69
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}}}
70
=== Bitmap preservation not possible to non-qcow2 ===
59
{"return": {}}
71
60
72
@@ -XXX,XX +XXX,XX @@ Format specific information:
61
--- Write #2 ---
73
granularity: 65536
62
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
74
refcount bits: 16
63
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
75
corrupt: false
64
{"return": {}}
76
-Check resulting qcow2 header extensions:
65
{}
77
-Header extension:
66
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
78
-magic 0x6803f857 (Feature table)
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}}}
79
-length 336
68
{"return": {}}
80
-data <binary>
69
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
81
-
70
82
-Header extension:
71
@@ -XXX,XX +XXX,XX @@ expecting 15 dirty sectors; have 15. OK!
83
-magic 0x23852875 (Bitmaps)
72
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
84
-length 24
73
{"return": {}}
85
-nb_bitmaps 3
74
{}
86
-reserved32 0
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"}}
87
-bitmap_directory_size 0x60
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}}}
88
-bitmap_directory_offset 0x520000
77
{"return": {}}
89
-
78
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
90
79
{"return": {}}
91
=== Check bitmap contents ===
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"}}
92
1183
93
--
1184
--
94
2.26.2
1185
2.29.2
95
1186
96
1187
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
ret may be > 0 on success path at this point. Fix assertion, which may
3
We are going to stop use of this callback in the following commit.
4
crash currently.
4
Still the callback handling code will be dropped in a separate commit.
5
So, for now let's make it optional.
5
6
6
Fixes: 4ce5dd3e9b5ee0fac18625860eb3727399ee965e
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20200526181347.489557-1-vsementsov@virtuozzo.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20210116214705.822267-16-vsementsov@virtuozzo.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
---
11
block/block-copy.c | 4 +++-
12
block/block-copy.c | 4 +++-
12
1 file changed, 3 insertions(+), 1 deletion(-)
13
1 file changed, 3 insertions(+), 1 deletion(-)
13
14
14
diff --git a/block/block-copy.c b/block/block-copy.c
15
diff --git a/block/block-copy.c b/block/block-copy.c
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-copy.c
17
--- a/block/block-copy.c
17
+++ b/block/block-copy.c
18
+++ b/block/block-copy.c
18
@@ -XXX,XX +XXX,XX @@ out:
19
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
19
* block_copy_task_run. If it fails, it means some task already failed
20
t->call_state->error_is_read = error_is_read;
20
* for real reason, let's return first failure.
21
} else {
21
* Still, assert that we don't rewrite failure by success.
22
progress_work_done(t->s->progress, t->bytes);
22
+ *
23
- t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
23
+ * Note: ret may be positive here because of block-status result.
24
+ if (t->s->progress_bytes_callback) {
24
*/
25
+ t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
25
- assert(ret == 0 || aio_task_pool_status(aio) < 0);
26
+ }
26
+ assert(ret >= 0 || aio_task_pool_status(aio) < 0);
27
}
27
ret = aio_task_pool_status(aio);
28
co_put_to_shres(t->s->mem, t->bytes);
28
29
block_copy_task_end(t, ret);
29
aio_task_pool_free(aio);
30
--
30
--
31
2.26.2
31
2.29.2
32
32
33
33
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Message-Id: <20210116214705.822267-21-vsementsov@virtuozzo.com>
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
---
8
include/block/block-copy.h | 2 +-
9
block/backup-top.c | 2 +-
10
block/block-copy.c | 10 ++--------
11
3 files changed, 4 insertions(+), 10 deletions(-)
12
13
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/block-copy.h
16
+++ b/include/block/block-copy.h
17
@@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
18
int64_t offset, int64_t *count);
19
20
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
21
- bool ignore_ratelimit, bool *error_is_read);
22
+ bool ignore_ratelimit);
23
24
/*
25
* Run block-copy in a coroutine, create corresponding BlockCopyCallState
26
diff --git a/block/backup-top.c b/block/backup-top.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/block/backup-top.c
29
+++ b/block/backup-top.c
30
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
31
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
32
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
33
34
- return block_copy(s->bcs, off, end - off, true, NULL);
35
+ return block_copy(s->bcs, off, end - off, true);
36
}
37
38
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
39
diff --git a/block/block-copy.c b/block/block-copy.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/block/block-copy.c
42
+++ b/block/block-copy.c
43
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
44
}
45
46
int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
47
- bool ignore_ratelimit, bool *error_is_read)
48
+ bool ignore_ratelimit)
49
{
50
BlockCopyCallState call_state = {
51
.s = s,
52
@@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes,
53
.max_workers = BLOCK_COPY_MAX_WORKERS,
54
};
55
56
- int ret = block_copy_common(&call_state);
57
-
58
- if (error_is_read && ret < 0) {
59
- *error_is_read = call_state.error_is_read;
60
- }
61
-
62
- return ret;
63
+ return block_copy_common(&call_state);
64
}
65
66
static void coroutine_fn block_copy_async_co_entry(void *opaque)
67
--
68
2.29.2
69
70
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Message-Id: <20210116214705.822267-22-vsementsov@virtuozzo.com>
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
---
8
scripts/simplebench/bench_block_job.py | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
10
11
diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py
12
index XXXXXXX..XXXXXXX 100755
13
--- a/scripts/simplebench/bench_block_job.py
14
+++ b/scripts/simplebench/bench_block_job.py
15
@@ -XXX,XX +XXX,XX @@
16
-#!/usr/bin/env python
17
+#!/usr/bin/env python3
18
#
19
# Benchmark block jobs
20
#
21
--
22
2.29.2
23
24
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
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
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
1
From: David Edmondson <david.edmondson@oracle.com>
2
2
3
Commit 96927c744 replaced qdev_init_nofail() call by
3
When a call to fcntl(2) for the purpose of adding file locks fails
4
isa_realize_and_unref() which has a different error
4
with an error other than EAGAIN or EACCES, report the error returned
5
message. Update the test output accordingly.
5
by fcntl.
6
6
7
Gitlab CI error after merging b77b5b3dc7:
7
EAGAIN or EACCES are elided as they are considered to be common
8
https://gitlab.com/qemu-project/qemu/-/jobs/597414772#L4375
8
failures, indicating that a conflicting lock is held by another
9
process.
9
10
10
Reported-by: Thomas Huth <thuth@redhat.com>
11
No errors are elided when removing file locks.
11
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
12
12
Message-Id: <20200616154949.6586-1-philmd@redhat.com>
13
Signed-off-by: David Edmondson <david.edmondson@oracle.com>
13
Reviewed-by: Thomas Huth <thuth@redhat.com>
14
Message-Id: <20210113164447.2545785-1-david.edmondson@oracle.com>
15
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
17
---
16
tests/qemu-iotests/051.pc.out | 4 ++--
18
block/file-posix.c | 38 ++++++++++++++++++++++++++++----------
17
1 file changed, 2 insertions(+), 2 deletions(-)
19
1 file changed, 28 insertions(+), 10 deletions(-)
18
20
19
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
21
diff --git a/block/file-posix.c b/block/file-posix.c
20
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qemu-iotests/051.pc.out
23
--- a/block/file-posix.c
22
+++ b/tests/qemu-iotests/051.pc.out
24
+++ b/block/file-posix.c
23
@@ -XXX,XX +XXX,XX @@ QEMU X.Y.Z monitor - type 'help' for more information
25
@@ -XXX,XX +XXX,XX @@ typedef struct RawPosixAIOData {
24
26
static int cdrom_reopen(BlockDriverState *bs);
25
Testing: -drive if=ide
27
#endif
26
QEMU X.Y.Z monitor - type 'help' for more information
28
27
-(qemu) QEMU_PROG: Initialization of device ide-hd failed: Device needs media, but drive is empty
29
+/*
28
+(qemu) QEMU_PROG: Device needs media, but drive is empty
30
+ * Elide EAGAIN and EACCES details when failing to lock, as this
29
31
+ * indicates that the specified file region is already locked by
30
Testing: -drive if=virtio
32
+ * another process, which is considered a common scenario.
31
QEMU X.Y.Z monitor - type 'help' for more information
33
+ */
32
@@ -XXX,XX +XXX,XX @@ QEMU X.Y.Z monitor - type 'help' for more information
34
+#define raw_lock_error_setg_errno(errp, err, fmt, ...) \
33
35
+ do { \
34
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
36
+ if ((err) == EAGAIN || (err) == EACCES) { \
35
QEMU X.Y.Z monitor - type 'help' for more information
37
+ error_setg((errp), (fmt), ## __VA_ARGS__); \
36
-(qemu) QEMU_PROG: Initialization of device ide-hd failed: Block node is read-only
38
+ } else { \
37
+(qemu) QEMU_PROG: Block node is read-only
39
+ error_setg_errno((errp), (err), (fmt), ## __VA_ARGS__); \
38
40
+ } \
39
Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on
41
+ } while (0)
40
QEMU X.Y.Z monitor - type 'help' for more information
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
}
41
--
112
--
42
2.26.2
113
2.29.2
43
114
44
115
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
This commit adds two tests, which test the new amend interface
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
4
of both luks raw images and qcow2 luks encrypted images.
4
Suggested-by: Maxim Levitsky <mlevitsk@redhat.com>
5
5
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
6
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
6
Message-Id: <20210112170540.2912-1-berto@igalia.com>
7
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
7
[mreitz: Add "# group:" line]
8
Message-Id: <20200608094030.670121-11-mlevitsk@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
9
---
11
tests/qemu-iotests/293 | 207 +++++++++++++++++++++++++++++++++++++
10
tests/qemu-iotests/313 | 104 +++++++++++++++++++++++++++++++++++++
12
tests/qemu-iotests/293.out | 99 ++++++++++++++++++
11
tests/qemu-iotests/313.out | 29 +++++++++++
13
tests/qemu-iotests/294 | 90 ++++++++++++++++
12
tests/qemu-iotests/group | 1 +
14
tests/qemu-iotests/294.out | 30 ++++++
13
3 files changed, 134 insertions(+)
15
tests/qemu-iotests/group | 2 +
14
create mode 100755 tests/qemu-iotests/313
16
5 files changed, 428 insertions(+)
15
create mode 100644 tests/qemu-iotests/313.out
17
create mode 100755 tests/qemu-iotests/293
18
create mode 100644 tests/qemu-iotests/293.out
19
create mode 100755 tests/qemu-iotests/294
20
create mode 100644 tests/qemu-iotests/294.out
21
16
22
diff --git a/tests/qemu-iotests/293 b/tests/qemu-iotests/293
17
diff --git a/tests/qemu-iotests/313 b/tests/qemu-iotests/313
23
new file mode 100755
18
new file mode 100755
24
index XXXXXXX..XXXXXXX
19
index XXXXXXX..XXXXXXX
25
--- /dev/null
20
--- /dev/null
26
+++ b/tests/qemu-iotests/293
21
+++ b/tests/qemu-iotests/313
27
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@
28
+#!/usr/bin/env bash
23
+#!/usr/bin/env bash
24
+# group: rw auto quick
29
+#
25
+#
30
+# Test encryption key management with luks
26
+# Test for the regression fixed in commit c8bf9a9169
31
+# Based on 134
32
+#
27
+#
33
+# Copyright (C) 2019 Red Hat, Inc.
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>
34
+#
31
+#
35
+# This program is free software; you can redistribute it and/or modify
32
+# This program is free software; you can redistribute it and/or modify
36
+# it under the terms of the GNU General Public License as published by
33
+# it under the terms of the GNU General Public License as published by
37
+# the Free Software Foundation; either version 2 of the License, or
34
+# the Free Software Foundation; either version 2 of the License, or
38
+# (at your option) any later version.
35
+# (at your option) any later version.
...
...
45
+# You should have received a copy of the GNU General Public License
42
+# You should have received a copy of the GNU General Public License
46
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
43
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
47
+#
44
+#
48
+
45
+
49
+# creator
46
+# creator
50
+owner=mlevitsk@redhat.com
47
+owner=berto@igalia.com
51
+
48
+
52
+seq=`basename $0`
49
+seq=`basename $0`
53
+echo "QA output created by $seq"
50
+echo "QA output created by $seq"
54
+
51
+
55
+status=1    # failure is the default!
52
+status=1 # failure is the default!
56
+
53
+
57
+_cleanup()
54
+_cleanup()
58
+{
55
+{
59
+    _cleanup_test_img
56
+ _cleanup_test_img
60
+}
57
+}
61
+trap "_cleanup; exit \$status" 0 1 2 3 15
58
+trap "_cleanup; exit \$status" 0 1 2 3 15
62
+
59
+
63
+# get standard environment, filters and checks
60
+# get standard environment, filters and checks
64
+. ./common.rc
61
+. ./common.rc
65
+. ./common.filter
62
+. ./common.filter
66
+
63
+
67
+_supported_fmt qcow2 luks
64
+_supported_fmt qcow2
68
+_supported_proto file #TODO
65
+_supported_proto file
66
+_supported_os Linux
67
+_unsupported_imgopts cluster_size refcount_bits extended_l2 compat=0.10 data_file
69
+
68
+
70
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
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}"
71
+
74
+
72
+if [ "$IMGFMT" = "qcow2" ] ; then
75
+# Number of refcount entries per refcount blocks
73
+    PR="encrypt."
76
+ref_entries=$(( ${cluster_size} * 8 / ${refcount_bits} ))
74
+    EXTRA_IMG_ARGS="-o encrypt.format=luks"
75
+fi
76
+
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 ))
77
+
81
+
78
+# secrets: you are supposed to see the password as *******, see :-)
82
+# Number of entries in the refcount cache
79
+S0="--object secret,id=sec0,data=hunter0"
83
+ref_blocks=4
80
+S1="--object secret,id=sec1,data=hunter1"
81
+S2="--object secret,id=sec2,data=hunter2"
82
+S3="--object secret,id=sec3,data=hunter3"
83
+S4="--object secret,id=sec4,data=hunter4"
84
+SECRETS="$S0 $S1 $S2 $S3 $S4"
85
+
84
+
86
+# image with given secret
85
+# Write enough data clusters to fill the refcount cache and allocate
87
+IMGS0="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec0"
86
+# one more refcount block.
88
+IMGS1="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec1"
87
+# Subtract 3 clusters from the total: qcow2 header, refcount table, L1 table
89
+IMGS2="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec2"
88
+total_data_clusters=$(( ${data_clusters_per_refblock} * ${ref_blocks} + 1 - 3 ))
90
+IMGS3="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec3"
91
+IMGS4="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec4"
92
+
89
+
93
+
90
+# Total size to write in bytes
94
+echo "== creating a test image =="
91
+total_size=$(( ${total_data_clusters} * ${cluster_size} ))
95
+_make_test_img $S0 $EXTRA_IMG_ARGS -o ${PR}key-secret=sec0,${PR}iter-time=10 32M
96
+
92
+
97
+echo
93
+echo
98
+echo "== test that key 0 opens the image =="
94
+echo '### Create the image'
99
+$QEMU_IO $S0 -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir
95
+echo
96
+TEST_IMG_FILE=$TEST_IMG.base _make_test_img -o $options $total_size | _filter_img_create_size
100
+
97
+
101
+echo
98
+echo
102
+echo "== adding a password to slot 4 =="
99
+echo '### Write data to allocate more refcount blocks than the cache can hold'
103
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec4,${PR}iter-time=10,${PR}keyslot=4
100
+echo
104
+echo "== adding a password to slot 1 =="
101
+$QEMU_IO -c "write -P 1 0 $total_size" $TEST_IMG.base | _filter_qemu_io
105
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec1,${PR}iter-time=10
106
+echo "== adding a password to slot 3 =="
107
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=active,${PR}new-secret=sec3,${PR}iter-time=10,${PR}keyslot=3
108
+
109
+echo "== adding a password to slot 2 =="
110
+$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec2,${PR}iter-time=10
111
+
112
+
113
+echo "== erase slot 4 =="
114
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=4 | _filter_img_create
115
+
116
+
102
+
117
+echo
103
+echo
118
+echo "== all secrets should work =="
104
+echo '### Create an overlay'
119
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
105
+echo
120
+    $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir
106
+_make_test_img -F $IMGFMT -b $TEST_IMG.base -o $options | _filter_img_create_size
121
+done
122
+
107
+
123
+echo
108
+echo
124
+echo "== erase slot 0 and try it =="
109
+echo '### Fill the overlay with zeroes'
125
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec0 | _filter_img_create
110
+echo
126
+$QEMU_IO $SECRETS -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir
111
+$QEMU_IO -c "write -z 0 $total_size" $TEST_IMG | _filter_qemu_io
127
+
112
+
128
+echo
113
+echo
129
+echo "== erase slot 2 and try it =="
114
+echo '### Commit changes to the base image'
130
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=2 | _filter_img_create
115
+echo
131
+$QEMU_IO $SECRETS -c "read 0 4096" $IMGS2 | _filter_qemu_io | _filter_testdir
116
+$QEMU_IMG commit $TEST_IMG
132
+
133
+
134
+# at this point slots 1 and 3 should be active
135
+
117
+
136
+echo
118
+echo
137
+echo "== filling 4 slots with secret 2 =="
119
+echo '### Check the base image'
138
+for i in $(seq 0 3) ; do
139
+    $QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec2,${PR}iter-time=10
140
+done
141
+
142
+echo
120
+echo
143
+echo "== adding secret 0 =="
121
+$QEMU_IMG check $TEST_IMG.base
144
+    $QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec0,${PR}iter-time=10
145
+
146
+echo
147
+echo "== adding secret 3 (last slot) =="
148
+    $QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec3,${PR}iter-time=10
149
+
150
+echo
151
+echo "== trying to add another slot (should fail) =="
152
+$QEMU_IMG amend $SECRETS $IMGS2 -o ${PR}state=active,${PR}new-secret=sec3,${PR}iter-time=10
153
+
154
+echo
155
+echo "== all secrets should work again =="
156
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
157
+    $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir
158
+done
159
+
160
+
161
+echo
162
+
163
+echo "== erase all keys of secret 2=="
164
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec2
165
+
166
+echo "== erase all keys of secret 1=="
167
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec1
168
+
169
+echo "== erase all keys of secret 0=="
170
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=inactive,${PR}old-secret=sec0
171
+
172
+echo "== erasing secret3 will fail now since it is the only secret (in 3 slots) =="
173
+$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=inactive,${PR}old-secret=sec3
174
+
175
+echo
176
+echo "== only secret3 should work now =="
177
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
178
+    $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir
179
+done
180
+
181
+echo
182
+echo "== add secret0 =="
183
+$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}state=active,${PR}new-secret=sec0,${PR}iter-time=10
184
+
185
+echo "== erase secret3 =="
186
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=inactive,${PR}old-secret=sec3
187
+
188
+echo
189
+echo "== only secret0 should work now =="
190
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
191
+    $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir
192
+done
193
+
194
+echo
195
+echo "== replace secret0 with secret1 (should fail) =="
196
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec1,${PR}keyslot=0
197
+
198
+echo
199
+echo "== replace secret0 with secret1 with force (should work) =="
200
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}state=active,${PR}new-secret=sec1,${PR}iter-time=10,${PR}keyslot=0 --force
201
+
202
+echo
203
+echo "== only secret1 should work now =="
204
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
205
+    $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir
206
+done
207
+
208
+
209
+echo
210
+echo "== erase last secret (should fail) =="
211
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=0
212
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec1
213
+
214
+
215
+echo "== erase non existing secrets (should fail) =="
216
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec5 --force
217
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}old-secret=sec0 --force
218
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=1 --force
219
+
220
+echo
221
+echo "== erase last secret with force by slot (should work) =="
222
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}state=inactive,${PR}keyslot=0 --force
223
+
224
+echo
225
+echo "== we have no secrets now, data is lost forever =="
226
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
227
+    $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | _filter_testdir
228
+done
229
+
122
+
230
+# success, all done
123
+# success, all done
231
+echo "*** done"
124
+echo "*** done"
232
+rm -f $seq.full
125
+rm -f $seq.full
233
+status=0
126
+status=0
234
+
127
diff --git a/tests/qemu-iotests/313.out b/tests/qemu-iotests/313.out
235
diff --git a/tests/qemu-iotests/293.out b/tests/qemu-iotests/293.out
236
new file mode 100644
128
new file mode 100644
237
index XXXXXXX..XXXXXXX
129
index XXXXXXX..XXXXXXX
238
--- /dev/null
130
--- /dev/null
239
+++ b/tests/qemu-iotests/293.out
131
+++ b/tests/qemu-iotests/313.out
240
@@ -XXX,XX +XXX,XX @@
132
@@ -XXX,XX +XXX,XX @@
241
+QA output created by 293
133
+QA output created by 313
242
+== creating a test image ==
243
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
244
+
134
+
245
+== test that key 0 opens the image ==
135
+### Create the image
246
+read 4096/4096 bytes at offset 0
247
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
248
+
136
+
249
+== adding a password to slot 4 ==
137
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=SIZE
250
+== adding a password to slot 1 ==
251
+== adding a password to slot 3 ==
252
+== adding a password to slot 2 ==
253
+== erase slot 4 ==
254
+
138
+
255
+== all secrets should work ==
139
+### Write data to allocate more refcount blocks than the cache can hold
256
+read 4096/4096 bytes at offset 0
257
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
258
+read 4096/4096 bytes at offset 0
259
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
260
+read 4096/4096 bytes at offset 0
261
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
262
+read 4096/4096 bytes at offset 0
263
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
264
+
140
+
265
+== erase slot 0 and try it ==
141
+wrote 8347648/8347648 bytes at offset 0
266
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
142
+7.961 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
267
+
143
+
268
+== erase slot 2 and try it ==
144
+### Create an overlay
269
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
270
+
145
+
271
+== filling 4 slots with secret 2 ==
146
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
272
+
147
+
273
+== adding secret 0 ==
148
+### Fill the overlay with zeroes
274
+
149
+
275
+== adding secret 3 (last slot) ==
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)
276
+
152
+
277
+== trying to add another slot (should fail) ==
153
+### Commit changes to the base image
278
+qemu-img: Can't add a keyslot - all keyslots are in use
279
+
154
+
280
+== all secrets should work again ==
155
+Image committed.
281
+read 4096/4096 bytes at offset 0
282
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
283
+read 4096/4096 bytes at offset 0
284
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
285
+read 4096/4096 bytes at offset 0
286
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
287
+read 4096/4096 bytes at offset 0
288
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
289
+
156
+
290
+== erase all keys of secret 2==
157
+### Check the base image
291
+== erase all keys of secret 1==
292
+== erase all keys of secret 0==
293
+== erasing secret3 will fail now since it is the only secret (in 3 slots) ==
294
+qemu-img: All the active keyslots match the (old) password that was given and erasing them will erase all the data in the image irreversibly - refusing operation
295
+
158
+
296
+== only secret3 should work now ==
159
+No errors were found on the image.
297
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
160
+Image end offset: 8396800
298
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
299
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
300
+read 4096/4096 bytes at offset 0
301
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
302
+
303
+== add secret0 ==
304
+== erase secret3 ==
305
+
306
+== only secret0 should work now ==
307
+read 4096/4096 bytes at offset 0
308
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
309
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
310
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
311
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
312
+
313
+== replace secret0 with secret1 (should fail) ==
314
+qemu-img: Refusing to overwrite active keyslot 0 - please erase it first
315
+
316
+== replace secret0 with secret1 with force (should work) ==
317
+
318
+== only secret1 should work now ==
319
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
320
+read 4096/4096 bytes at offset 0
321
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
322
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
323
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
324
+
325
+== erase last secret (should fail) ==
326
+qemu-img: Attempt to erase the only active keyslot 0 which will erase all the data in the image irreversibly - refusing operation
327
+qemu-img: All the active keyslots match the (old) password that was given and erasing them will erase all the data in the image irreversibly - refusing operation
328
+== erase non existing secrets (should fail) ==
329
+qemu-img: No secret with id 'sec5'
330
+qemu-img: No keyslots match given (old) password for erase operation
331
+
332
+== erase last secret with force by slot (should work) ==
333
+
334
+== we have no secrets now, data is lost forever ==
335
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
336
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
337
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
338
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
339
+*** done
340
diff --git a/tests/qemu-iotests/294 b/tests/qemu-iotests/294
341
new file mode 100755
342
index XXXXXXX..XXXXXXX
343
--- /dev/null
344
+++ b/tests/qemu-iotests/294
345
@@ -XXX,XX +XXX,XX @@
346
+#
347
+# Copyright (C) 2019 Red Hat, Inc.
348
+#
349
+# This program is free software; you can redistribute it and/or modify
350
+# it under the terms of the GNU General Public License as published by
351
+# the Free Software Foundation; either version 2 of the License, or
352
+# (at your option) any later version.
353
+#
354
+# This program is distributed in the hope that it will be useful,
355
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
356
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
357
+# GNU General Public License for more details.
358
+#
359
+# You should have received a copy of the GNU General Public License
360
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
361
+#
362
+
363
+# creator
364
+owner=mlevitsk@redhat.com
365
+
366
+seq=`basename $0`
367
+echo "QA output created by $seq"
368
+
369
+status=1    # failure is the default!
370
+
371
+_cleanup()
372
+{
373
+    _cleanup_test_img
374
+}
375
+trap "_cleanup; exit \$status" 0 1 2 3 15
376
+
377
+# get standard environment, filters and checks
378
+. ./common.rc
379
+. ./common.filter
380
+
381
+_supported_fmt luks
382
+_supported_proto file #TODO
383
+
384
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
385
+
386
+# you are supposed to see the password as *******, see :-)
387
+S0="--object secret,id=sec0,data=hunter0"
388
+S1="--object secret,id=sec1,data=hunter1"
389
+SECRETS="$S0 $S1"
390
+
391
+
392
+IMGS0="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec0"
393
+IMGS1="--image-opts driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec1"
394
+
395
+echo "== creating a test image =="
396
+_make_test_img $S0 -o "key-secret=sec0,iter-time=10" 32M
397
+
398
+echo
399
+echo "== test that key 0 opens the image =="
400
+$QEMU_IO $S0 -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir
401
+
402
+echo
403
+echo "== adding a password to slot 1 =="
404
+$QEMU_IMG amend $SECRETS $IMGS0 -o state=active,new-secret=sec1,keyslot=1,iter-time=10
405
+
406
+echo
407
+echo "== 'backup' the image header =="
408
+dd if=$TEST_IMG_FILE of=${TEST_IMG_FILE}.bk bs=4K skip=0 count=1
409
+
410
+echo
411
+echo "== erase slot 0 =="
412
+$QEMU_IMG amend $SECRETS $IMGS1 -o state=inactive,keyslot=0 | _filter_img_create
413
+
414
+echo
415
+echo "== test that key 0 doesn't open the image =="
416
+$QEMU_IO $S0 -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir
417
+
418
+echo
419
+echo "== 'restore' the image header =="
420
+dd if=${TEST_IMG_FILE}.bk of=${TEST_IMG_FILE} bs=4K skip=0 count=1 conv=notrunc
421
+
422
+echo
423
+echo "== test that key 0 still doesn't open the image (key material is erased) =="
424
+$QEMU_IO $SECRETS -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir
425
+
426
+echo
427
+echo "== test that key 1 still works =="
428
+$QEMU_IO $SECRETS -c "read 0 4096" $IMGS1 | _filter_qemu_io | _filter_testdir
429
+
430
+echo "*** done"
431
+rm -f $seq.full
432
+status=0
433
+
434
+
435
+exit 0
436
diff --git a/tests/qemu-iotests/294.out b/tests/qemu-iotests/294.out
437
new file mode 100644
438
index XXXXXXX..XXXXXXX
439
--- /dev/null
440
+++ b/tests/qemu-iotests/294.out
441
@@ -XXX,XX +XXX,XX @@
442
+QA output created by 294
443
+== creating a test image ==
444
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
445
+
446
+== test that key 0 opens the image ==
447
+read 4096/4096 bytes at offset 0
448
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
449
+
450
+== adding a password to slot 1 ==
451
+
452
+== 'backup' the image header ==
453
+1+0 records in
454
+1+0 records out
455
+
456
+== erase slot 0 ==
457
+
458
+== test that key 0 doesn't open the image ==
459
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
460
+
461
+== 'restore' the image header ==
462
+1+0 records in
463
+1+0 records out
464
+
465
+== test that key 0 still doesn't open the image (key material is erased) ==
466
+qemu-io: can't open: Invalid password, cannot unlock any keyslot
467
+
468
+== test that key 1 still works ==
469
+read 4096/4096 bytes at offset 0
470
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
471
+*** done
161
+*** done
472
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
162
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
473
index XXXXXXX..XXXXXXX 100644
163
index XXXXXXX..XXXXXXX 100644
474
--- a/tests/qemu-iotests/group
164
--- a/tests/qemu-iotests/group
475
+++ b/tests/qemu-iotests/group
165
+++ b/tests/qemu-iotests/group
476
@@ -XXX,XX +XXX,XX @@
166
@@ -XXX,XX +XXX,XX @@
477
290 rw auto quick
167
309 rw auto quick
478
291 rw quick
168
310 rw quick
479
292 rw auto quick
169
312 rw quick
480
+293 rw auto
170
+313 rw auto quick
481
+294 rw auto quick
482
297 meta
483
--
171
--
484
2.26.2
172
2.29.2
485
173
486
174
diff view generated by jsdifflib
New patch
1
Commit 0afec75734331 removed the 'change' QMP command, so we can no
2
longer test it in 118.
1
3
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
1
From: Maxim Levitsky <mlevitsk@redhat.com>
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.
2
5
3
This allows more tests to be able to have same output on both qcow2 luks encrypted images
6
So after ccd3b3b8112, 178 fails with:
4
and raw luks images
5
7
6
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
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")
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
17
Message-Id: <20210126123834.115915-1-mreitz@redhat.com>
9
Message-Id: <20200618150628.2169239-3-mreitz@redhat.com>
10
---
18
---
11
tests/qemu-iotests/087.out | 6 +++---
19
tests/qemu-iotests/178 | 2 +-
12
tests/qemu-iotests/134.out | 2 +-
20
tests/qemu-iotests/178.out.qcow2 | 2 +-
13
tests/qemu-iotests/158.out | 4 ++--
21
tests/qemu-iotests/178.out.raw | 2 +-
14
tests/qemu-iotests/188.out | 2 +-
22
3 files changed, 3 insertions(+), 3 deletions(-)
15
tests/qemu-iotests/189.out | 4 ++--
16
tests/qemu-iotests/198.out | 4 ++--
17
tests/qemu-iotests/263.out | 4 ++--
18
tests/qemu-iotests/284.out | 6 +++---
19
tests/qemu-iotests/common.filter | 5 +----
20
9 files changed, 17 insertions(+), 20 deletions(-)
21
23
22
diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
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
23
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
24
--- a/tests/qemu-iotests/087.out
39
--- a/tests/qemu-iotests/178.out.qcow2
25
+++ b/tests/qemu-iotests/087.out
40
+++ b/tests/qemu-iotests/178.out.qcow2
26
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
41
@@ -XXX,XX +XXX,XX @@ qemu-img: --image-opts, -f, and -l require a filename argument.
27
42
qemu-img: --image-opts, -f, and -l require a filename argument.
28
=== Encrypted image QCow ===
43
qemu-img: Invalid option list: ,
29
44
qemu-img: Invalid parameter 'snapshot.foo'
30
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0
45
-qemu-img: Failed in parsing snapshot param 'snapshot.foo'
31
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
46
+qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
32
Testing:
47
qemu-img: --output must be used with human or json as argument.
33
QMP_VERSION
48
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
34
{"return": {}}
49
qemu-img: Unknown file format 'foo'
35
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
50
diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw
36
37
=== Encrypted image LUKS ===
38
39
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encrypt.format=luks encrypt.key-secret=sec0
40
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
41
Testing:
42
QMP_VERSION
43
{"return": {}}
44
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
45
46
=== Missing driver ===
47
48
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0
49
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
50
Testing: -S
51
QMP_VERSION
52
{"return": {}}
53
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
54
index XXXXXXX..XXXXXXX 100644
51
index XXXXXXX..XXXXXXX 100644
55
--- a/tests/qemu-iotests/134.out
52
--- a/tests/qemu-iotests/178.out.raw
56
+++ b/tests/qemu-iotests/134.out
53
+++ b/tests/qemu-iotests/178.out.raw
57
@@ -XXX,XX +XXX,XX @@
54
@@ -XXX,XX +XXX,XX @@ qemu-img: --image-opts, -f, and -l require a filename argument.
58
QA output created by 134
55
qemu-img: --image-opts, -f, and -l require a filename argument.
59
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0
56
qemu-img: Invalid option list: ,
60
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
57
qemu-img: Invalid parameter 'snapshot.foo'
61
58
-qemu-img: Failed in parsing snapshot param 'snapshot.foo'
62
== reading whole image ==
59
+qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
63
read 134217728/134217728 bytes at offset 0
60
qemu-img: --output must be used with human or json as argument.
64
diff --git a/tests/qemu-iotests/158.out b/tests/qemu-iotests/158.out
61
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
65
index XXXXXXX..XXXXXXX 100644
62
qemu-img: Unknown file format 'foo'
66
--- a/tests/qemu-iotests/158.out
67
+++ b/tests/qemu-iotests/158.out
68
@@ -XXX,XX +XXX,XX @@
69
QA output created by 158
70
== create base ==
71
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on encrypt.key-secret=sec0
72
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on
73
74
== writing whole image ==
75
wrote 134217728/134217728 bytes at offset 0
76
@@ -XXX,XX +XXX,XX @@ wrote 134217728/134217728 bytes at offset 0
77
read 134217728/134217728 bytes at offset 0
78
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
79
== create overlay ==
80
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base encryption=on encrypt.key-secret=sec0
81
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base encryption=on
82
83
== writing part of a cluster ==
84
wrote 1024/1024 bytes at offset 0
85
diff --git a/tests/qemu-iotests/188.out b/tests/qemu-iotests/188.out
86
index XXXXXXX..XXXXXXX 100644
87
--- a/tests/qemu-iotests/188.out
88
+++ b/tests/qemu-iotests/188.out
89
@@ -XXX,XX +XXX,XX @@
90
QA output created by 188
91
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
92
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216
93
94
== reading whole image ==
95
read 16777216/16777216 bytes at offset 0
96
diff --git a/tests/qemu-iotests/189.out b/tests/qemu-iotests/189.out
97
index XXXXXXX..XXXXXXX 100644
98
--- a/tests/qemu-iotests/189.out
99
+++ b/tests/qemu-iotests/189.out
100
@@ -XXX,XX +XXX,XX @@
101
QA output created by 189
102
== create base ==
103
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
104
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216
105
106
== writing whole image ==
107
wrote 16777216/16777216 bytes at offset 0
108
@@ -XXX,XX +XXX,XX @@ wrote 16777216/16777216 bytes at offset 0
109
read 16777216/16777216 bytes at offset 0
110
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
111
== create overlay ==
112
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 encrypt.iter-time=10
113
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base
114
115
== writing part of a cluster ==
116
wrote 1024/1024 bytes at offset 0
117
diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out
118
index XXXXXXX..XXXXXXX 100644
119
--- a/tests/qemu-iotests/198.out
120
+++ b/tests/qemu-iotests/198.out
121
@@ -XXX,XX +XXX,XX @@
122
QA output created by 198
123
== create base ==
124
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
125
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216
126
127
== writing whole image base ==
128
wrote 16777216/16777216 bytes at offset 0
129
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
130
== create overlay ==
131
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 encrypt.iter-time=10
132
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base
133
134
== writing whole image layer ==
135
wrote 16777216/16777216 bytes at offset 0
136
diff --git a/tests/qemu-iotests/263.out b/tests/qemu-iotests/263.out
137
index XXXXXXX..XXXXXXX 100644
138
--- a/tests/qemu-iotests/263.out
139
+++ b/tests/qemu-iotests/263.out
140
@@ -XXX,XX +XXX,XX @@ QA output created by 263
141
142
testing LUKS qcow2 encryption
143
144
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
145
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
146
== reading the whole image ==
147
read 1048576/1048576 bytes at offset 0
148
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
149
@@ -XXX,XX +XXX,XX @@ read 982528/982528 bytes at offset 66048
150
151
testing legacy AES qcow2 encryption
152
153
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=aes encrypt.key-secret=sec0
154
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
155
== reading the whole image ==
156
read 1048576/1048576 bytes at offset 0
157
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
158
diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out
159
index XXXXXXX..XXXXXXX 100644
160
--- a/tests/qemu-iotests/284.out
161
+++ b/tests/qemu-iotests/284.out
162
@@ -XXX,XX +XXX,XX @@ QA output created by 284
163
164
testing LUKS qcow2 encryption
165
166
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
167
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
168
169
== cluster size 512
170
== checking image refcounts ==
171
@@ -XXX,XX +XXX,XX @@ wrote 1/1 bytes at offset 512
172
173
== rechecking image refcounts ==
174
No errors were found on the image.
175
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
176
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
177
178
== cluster size 2048
179
== checking image refcounts ==
180
@@ -XXX,XX +XXX,XX @@ wrote 1/1 bytes at offset 2048
181
182
== rechecking image refcounts ==
183
No errors were found on the image.
184
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
185
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
186
187
== cluster size 32768
188
== checking image refcounts ==
189
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
190
index XXXXXXX..XXXXXXX 100644
191
--- a/tests/qemu-iotests/common.filter
192
+++ b/tests/qemu-iotests/common.filter
193
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
194
echo "$options" \
195
| tr '\n' '\0' \
196
| $SED -e 's/\x0$//' -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \
197
- | grep -ae "^\(fmt\\|size\\|backing\\|preallocation\\|encrypt$grep_data_file\\)" \
198
+ | grep -ae "^\(fmt\\|size\\|backing\\|preallocation\\|encryption$grep_data_file\\)" \
199
| $SED "${filename_filters[@]}" \
200
-e 's/^\(fmt\)/0-\1/' \
201
-e 's/^\(size\)/1-\1/' \
202
-e 's/^\(backing\)/2-\1/' \
203
-e 's/^\(data_file\)/3-\1/' \
204
-e 's/^\(encryption\)/4-\1/' \
205
- -e 's/^\(encrypt\.format\)/5-\1/' \
206
- -e 's/^\(encrypt\.key-secret\)/6-\1/' \
207
- -e 's/^\(encrypt\.iter-time\)/7-\1/' \
208
-e 's/^\(preallocation\)/8-\1/' \
209
| sort \
210
| $SED -e 's/^[0-9]-//' \
211
--
63
--
212
2.26.2
64
2.29.2
213
65
214
66
diff view generated by jsdifflib