1
The following changes since commit 6c599282f8ab382fe59f03a6cae755b89561a7b3:
1
The following changes since commit 16aaacb307ed607b9780c12702c44f0fe52edc7e:
2
2
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2020-02-15-v2' into staging (2020-02-17 13:32:25 +0000)
3
Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20200430' into staging (2020-04-30 14:00:36 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to c45a88f4429d7a8f384b75f3fd3fed5138a6edca:
9
for you to fetch changes up to eaae29ef89d498d0eac553c77b554f310a47f809:
10
10
11
iotests: Check that @replaces can replace filters (2020-02-18 14:52:16 +0100)
11
qemu-storage-daemon: Fix non-string --object properties (2020-04-30 17:51:07 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches:
15
15
16
- Fix check_to_replace_node()
16
- Fix resize (extending) of short overlays
17
- commit: Expose on-error option in QMP
17
- nvme: introduce PMR support from NVMe 1.4 spec
18
- qcow2: Fix qcow2_alloc_cluster_abort() for external data file
18
- qemu-storage-daemon: Fix non-string --object properties
19
- mirror: Fix deadlock
20
- vvfat: Fix segfault while closing read-write node
21
- Code cleanups
22
19
23
----------------------------------------------------------------
20
----------------------------------------------------------------
24
Alberto Garcia (1):
21
Alberto Garcia (1):
25
qcow2: Fix alignment checks in encrypted images
22
qcow2: Add incompatibility note between backing files and raw external data files
26
23
27
Hikaru Nishida (1):
24
Andrzej Jakowski (1):
28
block/vvfat: Do not unref qcow on closing backing bdrv
25
nvme: introduce PMR support from NVMe 1.4 spec
29
26
30
Kevin Wolf (12):
27
Kevin Wolf (12):
31
mirror: Store MirrorOp.co for debuggability
28
block: Add flags to BlockDriver.bdrv_co_truncate()
32
mirror: Don't let an operation wait for itself
29
block: Add flags to bdrv(_co)_truncate()
33
qcow2: update_refcount(): Reset old_table_index after qcow2_cache_put()
30
block-backend: Add flags to blk_truncate()
34
qcow2: Fix qcow2_alloc_cluster_abort() for external data file
31
qcow2: Support BDRV_REQ_ZERO_WRITE for truncate
35
iotests: Test copy offloading with external data file
32
raw-format: Support BDRV_REQ_ZERO_WRITE for truncate
36
qapi: Document meaning of 'ignore' BlockdevOnError for jobs
33
file-posix: Support BDRV_REQ_ZERO_WRITE for truncate
37
commit: Remove unused bytes_written
34
block: truncate: Don't make backing file data visible
38
commit: Fix argument order for block_job_error_action()
35
iotests: Filter testfiles out in filter_img_info()
39
commit: Inline commit_populate()
36
iotests: Test committing to short backing file
40
commit: Fix is_read for block_job_error_action()
37
qcow2: Forward ZERO_WRITE flag for full preallocation
41
commit: Expose on-error option in QMP
38
qom: Factor out user_creatable_add_dict()
42
iotests: Test error handling policies with block-commit
39
qemu-storage-daemon: Fix non-string --object properties
43
40
44
Max Reitz (19):
41
Paolo Bonzini (1):
45
blockdev: Allow external snapshots everywhere
42
qemu-iotests: allow qcow2 external discarded clusters to contain stale data
46
blockdev: Allow resizing everywhere
47
block: Drop bdrv_is_first_non_filter()
48
iotests: Let 041 use -blockdev for quorum children
49
quorum: Fix child permissions
50
block: Add bdrv_recurse_can_replace()
51
blkverify: Implement .bdrv_recurse_can_replace()
52
quorum: Implement .bdrv_recurse_can_replace()
53
block: Use bdrv_recurse_can_replace()
54
block: Remove bdrv_recurse_is_first_non_filter()
55
mirror: Double-check immediately before replacing
56
quorum: Stop marking it as a filter
57
iotests: Use complete_and_wait() in 155
58
iotests: Add VM.assert_block_path()
59
iotests/041: Drop superfluous shutdowns
60
iotests: Resolve TODOs in 041
61
iotests: Use self.image_len in TestRepairQuorum
62
iotests: Add tests for invalid Quorum @replaces
63
iotests: Check that @replaces can replace filters
64
43
65
Philippe Mathieu-Daudé (3):
44
docs/interop/qcow2.txt | 3 +
66
block/qcow2-bitmap: Remove unneeded variable assignment
45
hw/block/nvme.h | 2 +
67
block: Remove superfluous semicolons
46
include/block/block.h | 5 +-
68
block/io_uring: Remove superfluous semicolon
47
include/block/block_int.h | 10 +-
69
48
include/block/nvme.h | 172 ++++++++++++++++++++++++++
70
qapi/block-core.json | 9 +-
49
include/qom/object_interfaces.h | 16 +++
71
include/block/block.h | 5 -
50
include/sysemu/block-backend.h | 2 +-
72
include/block/block_int.h | 16 +--
51
block.c | 3 +-
73
block.c | 89 ++++++-------
52
block/block-backend.c | 4 +-
74
block/blkverify.c | 20 +--
53
block/commit.c | 4 +-
75
block/commit.c | 37 ++----
54
block/crypto.c | 7 +-
76
block/copy-on-read.c | 9 --
55
block/file-posix.c | 6 +-
77
block/filter-compress.c | 9 --
56
block/file-win32.c | 2 +-
78
block/io_uring.c | 2 +-
57
block/gluster.c | 1 +
79
block/mirror.c | 37 ++++--
58
block/io.c | 43 ++++++-
80
block/qcow2-bitmap.c | 1 -
59
block/iscsi.c | 2 +-
81
block/qcow2-cluster.c | 7 +-
60
block/mirror.c | 2 +-
82
block/qcow2-refcount.c | 1 +
61
block/nfs.c | 3 +-
83
block/qcow2-threads.c | 12 +-
62
block/parallels.c | 6 +-
84
block/qcow2.c | 2 -
63
block/qcow.c | 4 +-
85
block/quorum.c | 70 +++++++++--
64
block/qcow2-cluster.c | 2 +-
86
block/replication.c | 7 --
65
block/qcow2-refcount.c | 2 +-
87
block/throttle.c | 8 --
66
block/qcow2.c | 73 +++++++++--
88
block/vvfat.c | 7 --
67
block/qed.c | 3 +-
89
blockdev.c | 18 +--
68
block/raw-format.c | 6 +-
90
tests/qemu-iotests/iotests.py | 59 +++++++++
69
block/rbd.c | 1 +
91
tests/qemu-iotests/040 | 283 ++++++++++++++++++++++++++++++++++++++++++
70
block/sheepdog.c | 4 +-
92
tests/qemu-iotests/040.out | 4 +-
71
block/ssh.c | 2 +-
93
tests/qemu-iotests/041 | 138 +++++++++++++++++---
72
block/vdi.c | 2 +-
94
tests/qemu-iotests/041.out | 4 +-
73
block/vhdx-log.c | 2 +-
95
tests/qemu-iotests/155 | 7 +-
74
block/vhdx.c | 6 +-
96
tests/qemu-iotests/244 | 14 +++
75
block/vmdk.c | 8 +-
97
tests/qemu-iotests/244.out | 6 +
76
block/vpc.c | 2 +-
98
28 files changed, 675 insertions(+), 206 deletions(-)
77
blockdev.c | 2 +-
78
hw/block/nvme.c | 109 ++++++++++++++++
79
qemu-img.c | 2 +-
80
qemu-io-cmds.c | 2 +-
81
qemu-storage-daemon.c | 4 +-
82
qom/object_interfaces.c | 31 +++++
83
qom/qom-qmp-cmds.c | 24 +---
84
tests/test-block-iothread.c | 9 +-
85
tests/qemu-iotests/iotests.py | 5 +-
86
hw/block/Makefile.objs | 2 +-
87
hw/block/trace-events | 4 +
88
tests/qemu-iotests/244 | 10 +-
89
tests/qemu-iotests/244.out | 9 +-
90
tests/qemu-iotests/274 | 155 +++++++++++++++++++++++
91
tests/qemu-iotests/274.out | 268 ++++++++++++++++++++++++++++++++++++++++
92
tests/qemu-iotests/group | 1 +
93
49 files changed, 951 insertions(+), 96 deletions(-)
94
create mode 100755 tests/qemu-iotests/274
95
create mode 100644 tests/qemu-iotests/274.out
99
96
100
97
diff view generated by jsdifflib
Deleted patch
1
If a coroutine is launched, but the coroutine pointer isn't stored
2
anywhere, debugging any problems inside the coroutine is quite hard.
3
Let's store the coroutine pointer of a mirror operation in MirrorOp to
4
have it available in the debugger.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
9
block/mirror.c | 2 ++
10
1 file changed, 2 insertions(+)
11
12
diff --git a/block/mirror.c b/block/mirror.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/block/mirror.c
15
+++ b/block/mirror.c
16
@@ -XXX,XX +XXX,XX @@ struct MirrorOp {
17
bool is_pseudo_op;
18
bool is_active_write;
19
CoQueue waiting_requests;
20
+ Coroutine *co;
21
22
QTAILQ_ENTRY(MirrorOp) next;
23
};
24
@@ -XXX,XX +XXX,XX @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
25
default:
26
abort();
27
}
28
+ op->co = co;
29
30
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
31
qemu_coroutine_enter(co);
32
--
33
2.20.1
34
35
diff view generated by jsdifflib
Deleted patch
1
mirror_wait_for_free_in_flight_slot() just picks a random operation to
2
wait for. However, when mirror_co_read() waits for free slots, its
3
MirrorOp is already in s->ops_in_flight, so if not enough slots are
4
immediately available, an operation can end up waiting for itself to
5
complete, which results in a hang.
6
1
7
Fix this by passing the current MirrorOp and skipping this operation
8
when picking an operation to wait for.
9
10
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1794692
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
---
14
block/mirror.c | 21 ++++++++++++---------
15
1 file changed, 12 insertions(+), 9 deletions(-)
16
17
diff --git a/block/mirror.c b/block/mirror.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/mirror.c
20
+++ b/block/mirror.c
21
@@ -XXX,XX +XXX,XX @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
22
}
23
24
static inline void coroutine_fn
25
-mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
26
+mirror_wait_for_any_operation(MirrorBlockJob *s, MirrorOp *self, bool active)
27
{
28
MirrorOp *op;
29
30
QTAILQ_FOREACH(op, &s->ops_in_flight, next) {
31
+ if (self == op) {
32
+ continue;
33
+ }
34
/* Do not wait on pseudo ops, because it may in turn wait on
35
* some other operation to start, which may in fact be the
36
* caller of this function. Since there is only one pseudo op
37
@@ -XXX,XX +XXX,XX @@ mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
38
}
39
40
static inline void coroutine_fn
41
-mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
42
+mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s, MirrorOp *self)
43
{
44
/* Only non-active operations use up in-flight slots */
45
- mirror_wait_for_any_operation(s, false);
46
+ mirror_wait_for_any_operation(s, self, false);
47
}
48
49
/* Perform a mirror copy operation.
50
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_co_read(void *opaque)
51
52
while (s->buf_free_count < nb_chunks) {
53
trace_mirror_yield_in_flight(s, op->offset, s->in_flight);
54
- mirror_wait_for_free_in_flight_slot(s);
55
+ mirror_wait_for_free_in_flight_slot(s, op);
56
}
57
58
/* Now make a QEMUIOVector taking enough granularity-sized chunks
59
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
60
61
while (s->in_flight >= MAX_IN_FLIGHT) {
62
trace_mirror_yield_in_flight(s, offset, s->in_flight);
63
- mirror_wait_for_free_in_flight_slot(s);
64
+ mirror_wait_for_free_in_flight_slot(s, pseudo_op);
65
}
66
67
if (s->ret < 0) {
68
@@ -XXX,XX +XXX,XX @@ static void mirror_free_init(MirrorBlockJob *s)
69
static void coroutine_fn mirror_wait_for_all_io(MirrorBlockJob *s)
70
{
71
while (s->in_flight > 0) {
72
- mirror_wait_for_free_in_flight_slot(s);
73
+ mirror_wait_for_free_in_flight_slot(s, NULL);
74
}
75
}
76
77
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
78
if (s->in_flight >= MAX_IN_FLIGHT) {
79
trace_mirror_yield(s, UINT64_MAX, s->buf_free_count,
80
s->in_flight);
81
- mirror_wait_for_free_in_flight_slot(s);
82
+ mirror_wait_for_free_in_flight_slot(s, NULL);
83
continue;
84
}
85
86
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
87
/* Do not start passive operations while there are active
88
* writes in progress */
89
while (s->in_active_write_counter) {
90
- mirror_wait_for_any_operation(s, true);
91
+ mirror_wait_for_any_operation(s, NULL, true);
92
}
93
94
if (s->ret < 0) {
95
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
96
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
97
(cnt == 0 && s->in_flight > 0)) {
98
trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight);
99
- mirror_wait_for_free_in_flight_slot(s);
100
+ mirror_wait_for_free_in_flight_slot(s, NULL);
101
continue;
102
} else if (cnt != 0) {
103
delay_ns = mirror_iteration(s);
104
--
105
2.20.1
106
107
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Backing files and raw external data files are mutually exclusive.
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
The documentation of the raw external data bit (in autoclear_features)
5
Message-Id: <20200218103454.296704-20-mreitz@redhat.com>
5
already indicates that, but we should also mention it on the other
6
side.
7
8
Suggested-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Alberto Garcia <berto@igalia.com>
10
Message-Id: <20200410121816.8334-1-berto@igalia.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
13
---
8
tests/qemu-iotests/041 | 46 ++++++++++++++++++++++++++++++++++++++
14
docs/interop/qcow2.txt | 3 +++
9
tests/qemu-iotests/041.out | 4 ++--
15
1 file changed, 3 insertions(+)
10
2 files changed, 48 insertions(+), 2 deletions(-)
11
16
12
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
17
diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
13
index XXXXXXX..XXXXXXX 100755
18
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/qemu-iotests/041
19
--- a/docs/interop/qcow2.txt
15
+++ b/tests/qemu-iotests/041
20
+++ b/docs/interop/qcow2.txt
16
@@ -XXX,XX +XXX,XX @@ class TestOrphanedSource(iotests.QMPTestCase):
21
@@ -XXX,XX +XXX,XX @@ The first cluster of a qcow2 image contains the file header:
17
self.assertFalse('mirror-filter' in nodes,
22
is stored (NB: The string is not null terminated). 0 if the
18
'Mirror filter node did not disappear')
23
image doesn't have a backing file.
19
24
20
+# Test cases for @replaces that do not necessarily involve Quorum
25
+ Note: backing files are incompatible with raw external data
21
+class TestReplaces(iotests.QMPTestCase):
26
+ files (auto-clear feature bit 1).
22
+ # Each of these test cases needs their own block graph, so do not
23
+ # create any nodes here
24
+ def setUp(self):
25
+ self.vm = iotests.VM()
26
+ self.vm.launch()
27
+
27
+
28
+ def tearDown(self):
28
16 - 19: backing_file_size
29
+ self.vm.shutdown()
29
Length of the backing file name in bytes. Must not be
30
+ for img in (test_img, target_img):
30
longer than 1023 bytes. Undefined if the image doesn't have
31
+ try:
32
+ os.remove(img)
33
+ except OSError:
34
+ pass
35
+
36
+ @iotests.skip_if_unsupported(['copy-on-read'])
37
+ def test_replace_filter(self):
38
+ """
39
+ Check that we can replace filter nodes.
40
+ """
41
+ result = self.vm.qmp('blockdev-add', **{
42
+ 'driver': 'copy-on-read',
43
+ 'node-name': 'filter0',
44
+ 'file': {
45
+ 'driver': 'copy-on-read',
46
+ 'node-name': 'filter1',
47
+ 'file': {
48
+ 'driver': 'null-co'
49
+ }
50
+ }
51
+ })
52
+ self.assert_qmp(result, 'return', {})
53
+
54
+ result = self.vm.qmp('blockdev-add',
55
+ node_name='target', driver='null-co')
56
+ self.assert_qmp(result, 'return', {})
57
+
58
+ result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0',
59
+ target='target', sync='full', replaces='filter1')
60
+ self.assert_qmp(result, 'return', {})
61
+
62
+ self.complete_and_wait('mirror')
63
+
64
+ self.vm.assert_block_path('filter0', '/file', 'target')
65
+
66
if __name__ == '__main__':
67
iotests.main(supported_fmts=['qcow2', 'qed'],
68
supported_protocols=['file'],
69
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
70
index XXXXXXX..XXXXXXX 100644
71
--- a/tests/qemu-iotests/041.out
72
+++ b/tests/qemu-iotests/041.out
73
@@ -XXX,XX +XXX,XX @@
74
-.............................................................................................
75
+..............................................................................................
76
----------------------------------------------------------------------
77
-Ran 93 tests
78
+Ran 94 tests
79
80
OK
81
--
31
--
82
2.20.1
32
2.25.3
83
33
84
34
diff view generated by jsdifflib
1
This adds a test for 'qemu-img convert' with copy offloading where the
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
target image has an external data file. If the test hosts supports it,
3
it tests both the case where copy offloading is supported and the case
4
where it isn't (otherwise we just test unsupported twice).
5
2
6
More specifically, the case with unsupported copy offloading tests
3
Test 244 checks the expected behavior of qcow2 external data files
7
qcow2_alloc_cluster_abort() with external data files.
4
with respect to zero and discarded clusters. Filesystems however
5
are free to ignore discard requests, and this seems to be the
6
case for overlayfs. Relax the tests to skip checks on the
7
external data file for discarded areas, which implies not using
8
qemu-img compare in the data_file_raw=on case.
8
9
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
This fixes docker tests on RHEL8.
10
Message-Id: <20200211094900.17315-4-kwolf@redhat.com>
11
12
Cc: Kevin Wolf <kwolf@redhat.com>
13
Cc: qemu-block@nongnu.org
14
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
15
Message-Id: <20200409191006.24429-1-pbonzini@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
17
---
13
tests/qemu-iotests/244 | 14 ++++++++++++++
18
tests/qemu-iotests/244 | 10 ++++++++--
14
tests/qemu-iotests/244.out | 6 ++++++
19
tests/qemu-iotests/244.out | 9 ++++++---
15
2 files changed, 20 insertions(+)
20
2 files changed, 14 insertions(+), 5 deletions(-)
16
21
17
diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244
22
diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244
18
index XXXXXXX..XXXXXXX 100755
23
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/244
24
--- a/tests/qemu-iotests/244
20
+++ b/tests/qemu-iotests/244
25
+++ b/tests/qemu-iotests/244
21
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'read -P 0x11 0 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
26
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'read -P 0 0 1M' \
22
$QEMU_IMG map --output=human "$TEST_IMG" | _filter_testdir
27
echo
23
$QEMU_IMG map --output=json "$TEST_IMG"
28
$QEMU_IO -c 'read -P 0 0 1M' \
24
29
-c 'read -P 0x11 1M 1M' \
25
+echo
30
- -c 'read -P 0 2M 2M' \
26
+echo "=== Copy offloading ==="
31
-c 'read -P 0x11 4M 1M' \
27
+echo
32
-c 'read -P 0 5M 1M' \
28
+
33
-f raw "$TEST_IMG.data" |
29
+# Make use of copy offloading if the test host can provide it
34
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'read -P 0 0 1M' \
30
+_make_test_img -o "data_file=$TEST_IMG.data" 64M
35
-f $IMGFMT "$TEST_IMG" |
31
+$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG"
36
_filter_qemu_io
32
+$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG"
37
33
+
38
+# Discarded clusters are only marked as such in the qcow2 metadata, but
34
+# blkdebug doesn't support copy offloading, so this tests the error path
39
+# they can contain stale data in the external data file. Instead, zero
35
+$QEMU_IMG amend -f $IMGFMT -o "data_file=blkdebug::$TEST_IMG.data" "$TEST_IMG"
40
+# clusters must be zeroed in the external data file too.
36
+$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG"
41
echo
37
+$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG"
42
-$QEMU_IMG compare "$TEST_IMG" "$TEST_IMG.data"
38
+
43
+$QEMU_IO -c 'read -P 0 0 1M' \
39
# success, all done
44
+ -c 'read -P 0x11 1M 1M' \
40
echo "*** done"
45
+ -c 'read -P 0 3M 3M' \
41
rm -f $seq.full
46
+ -f raw "$TEST_IMG".data |
47
+ _filter_qemu_io
48
49
echo -n "qcow2 file size after I/O: "
50
du -b $TEST_IMG | cut -f1
42
diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out
51
diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out
43
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
44
--- a/tests/qemu-iotests/244.out
53
--- a/tests/qemu-iotests/244.out
45
+++ b/tests/qemu-iotests/244.out
54
+++ b/tests/qemu-iotests/244.out
46
@@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File
55
@@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 0
47
0 0x100000 0 TEST_DIR/t.qcow2.data
56
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
48
[{ "start": 0, "length": 1048576, "depth": 0, "zero": false, "data": true, "offset": 0},
57
read 1048576/1048576 bytes at offset 1048576
49
{ "start": 1048576, "length": 66060288, "depth": 0, "zero": true, "data": false}]
58
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
50
+
59
-read 2097152/2097152 bytes at offset 2097152
51
+=== Copy offloading ===
60
-2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
52
+
61
read 1048576/1048576 bytes at offset 4194304
53
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data
62
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
54
+Images are identical.
63
read 1048576/1048576 bytes at offset 5242880
55
+Images are identical.
64
@@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576
56
*** done
65
read 4194304/4194304 bytes at offset 2097152
66
4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
67
68
-Images are identical.
69
+read 1048576/1048576 bytes at offset 0
70
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
71
+read 1048576/1048576 bytes at offset 1048576
72
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
73
+read 3145728/3145728 bytes at offset 3145728
74
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
75
qcow2 file size after I/O: 327680
76
77
=== bdrv_co_block_status test for file and offset=0 ===
57
--
78
--
58
2.20.1
79
2.25.3
59
80
60
81
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
This adds a new BdrvRequestFlags parameter to the .bdrv_co_truncate()
2
2
driver callbacks, and a supported_truncate_flags field in
3
After a couple of follow-up patches, this function will replace
3
BlockDriverState that allows drivers to advertise support for request
4
bdrv_recurse_is_first_non_filter() in check_to_replace_node().
4
flags in the context of truncate.
5
5
6
bdrv_recurse_is_first_non_filter() is both not sufficiently specific for
6
For now, we always pass 0 and no drivers declare support for any flag.
7
check_to_replace_node() (it allows cases that should not be allowed,
7
8
like replacing child nodes of quorum with dissenting data that have more
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
parents than just quorum), and it is too restrictive (it is perfectly
10
fine to replace filters).
11
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Message-Id: <20200218103454.296704-7-mreitz@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Message-Id: <20200424125448.63318-2-kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
14
---
17
include/block/block_int.h | 10 ++++++++++
15
include/block/block_int.h | 10 +++++++++-
18
block.c | 38 ++++++++++++++++++++++++++++++++++++++
16
block/crypto.c | 3 ++-
19
2 files changed, 48 insertions(+)
17
block/file-posix.c | 2 +-
18
block/file-win32.c | 2 +-
19
block/gluster.c | 1 +
20
block/io.c | 8 +++++++-
21
block/iscsi.c | 2 +-
22
block/nfs.c | 3 ++-
23
block/qcow2.c | 2 +-
24
block/qed.c | 1 +
25
block/raw-format.c | 2 +-
26
block/rbd.c | 1 +
27
block/sheepdog.c | 4 ++--
28
block/ssh.c | 2 +-
29
tests/test-block-iothread.c | 3 ++-
30
15 files changed, 33 insertions(+), 13 deletions(-)
20
31
21
diff --git a/include/block/block_int.h b/include/block/block_int.h
32
diff --git a/include/block/block_int.h b/include/block/block_int.h
22
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block_int.h
34
--- a/include/block/block_int.h
24
+++ b/include/block/block_int.h
35
+++ b/include/block/block_int.h
25
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
36
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
26
*/
37
*/
27
bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
38
int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset,
28
BlockDriverState *candidate);
39
bool exact, PreallocMode prealloc,
40
- Error **errp);
41
+ BdrvRequestFlags flags, Error **errp);
42
43
int64_t (*bdrv_getlength)(BlockDriverState *bs);
44
bool has_variable_length;
45
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
46
/* Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA,
47
* BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) */
48
unsigned int supported_zero_flags;
29
+ /*
49
+ /*
30
+ * Return true if @to_replace can be replaced by a BDS with the
50
+ * Flags honoured during truncate (so far: BDRV_REQ_ZERO_WRITE).
31
+ * same data as @bs without it affecting @bs's behavior (that is,
51
+ *
32
+ * without it being visible to @bs's parents).
52
+ * If BDRV_REQ_ZERO_WRITE is given, the truncate operation must make sure
53
+ * that any added space reads as all zeros. If this can't be guaranteed,
54
+ * the operation must fail.
33
+ */
55
+ */
34
+ bool (*bdrv_recurse_can_replace)(BlockDriverState *bs,
56
+ unsigned int supported_truncate_flags;
35
+ BlockDriverState *to_replace);
57
36
58
/* the following member gives a name to every node on the bs graph. */
37
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
59
char node_name[32];
38
int (*bdrv_probe_device)(const char *filename);
60
diff --git a/block/crypto.c b/block/crypto.c
39
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
61
index XXXXXXX..XXXXXXX 100644
40
uint64_t perm, uint64_t shared,
62
--- a/block/crypto.c
41
uint64_t *nperm, uint64_t *nshared);
63
+++ b/block/crypto.c
42
64
@@ -XXX,XX +XXX,XX @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
43
+bool bdrv_recurse_can_replace(BlockDriverState *bs,
65
44
+ BlockDriverState *to_replace);
66
static int coroutine_fn
45
+
67
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
46
/*
68
- PreallocMode prealloc, Error **errp)
47
* Default implementation for drivers to pass bdrv_co_block_status() to
69
+ PreallocMode prealloc, BdrvRequestFlags flags,
48
* their file.
70
+ Error **errp)
49
diff --git a/block.c b/block.c
71
{
50
index XXXXXXX..XXXXXXX 100644
72
BlockCrypto *crypto = bs->opaque;
51
--- a/block.c
73
uint64_t payload_offset =
52
+++ b/block.c
74
diff --git a/block/file-posix.c b/block/file-posix.c
53
@@ -XXX,XX +XXX,XX @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
75
index XXXXXXX..XXXXXXX 100644
54
return false;
76
--- a/block/file-posix.c
77
+++ b/block/file-posix.c
78
@@ -XXX,XX +XXX,XX @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset,
79
80
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
81
bool exact, PreallocMode prealloc,
82
- Error **errp)
83
+ BdrvRequestFlags flags, Error **errp)
84
{
85
BDRVRawState *s = bs->opaque;
86
struct stat st;
87
diff --git a/block/file-win32.c b/block/file-win32.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/block/file-win32.c
90
+++ b/block/file-win32.c
91
@@ -XXX,XX +XXX,XX @@ static void raw_close(BlockDriverState *bs)
92
93
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
94
bool exact, PreallocMode prealloc,
95
- Error **errp)
96
+ BdrvRequestFlags flags, Error **errp)
97
{
98
BDRVRawState *s = bs->opaque;
99
LONG low, high;
100
diff --git a/block/gluster.c b/block/gluster.c
101
index XXXXXXX..XXXXXXX 100644
102
--- a/block/gluster.c
103
+++ b/block/gluster.c
104
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
105
int64_t offset,
106
bool exact,
107
PreallocMode prealloc,
108
+ BdrvRequestFlags flags,
109
Error **errp)
110
{
111
BDRVGlusterState *s = bs->opaque;
112
diff --git a/block/io.c b/block/io.c
113
index XXXXXXX..XXXXXXX 100644
114
--- a/block/io.c
115
+++ b/block/io.c
116
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
117
BlockDriverState *bs = child->bs;
118
BlockDriver *drv = bs->drv;
119
BdrvTrackedRequest req;
120
+ BdrvRequestFlags flags = 0;
121
int64_t old_size, new_bytes;
122
int ret;
123
124
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
125
}
126
127
if (drv->bdrv_co_truncate) {
128
- ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, errp);
129
+ if (flags & ~bs->supported_truncate_flags) {
130
+ error_setg(errp, "Block driver does not support requested flags");
131
+ ret = -ENOTSUP;
132
+ goto out;
133
+ }
134
+ ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, flags, errp);
135
} else if (bs->file && drv->is_filter) {
136
ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
137
} else {
138
diff --git a/block/iscsi.c b/block/iscsi.c
139
index XXXXXXX..XXXXXXX 100644
140
--- a/block/iscsi.c
141
+++ b/block/iscsi.c
142
@@ -XXX,XX +XXX,XX @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
143
144
static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
145
bool exact, PreallocMode prealloc,
146
- Error **errp)
147
+ BdrvRequestFlags flags, Error **errp)
148
{
149
IscsiLun *iscsilun = bs->opaque;
150
int64_t cur_length;
151
diff --git a/block/nfs.c b/block/nfs.c
152
index XXXXXXX..XXXXXXX 100644
153
--- a/block/nfs.c
154
+++ b/block/nfs.c
155
@@ -XXX,XX +XXX,XX @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
156
157
static int coroutine_fn
158
nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
159
- PreallocMode prealloc, Error **errp)
160
+ PreallocMode prealloc, BdrvRequestFlags flags,
161
+ Error **errp)
162
{
163
NFSClient *client = bs->opaque;
164
int ret;
165
diff --git a/block/qcow2.c b/block/qcow2.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/qcow2.c
168
+++ b/block/qcow2.c
169
@@ -XXX,XX +XXX,XX @@ fail:
170
171
static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
172
bool exact, PreallocMode prealloc,
173
- Error **errp)
174
+ BdrvRequestFlags flags, Error **errp)
175
{
176
BDRVQcow2State *s = bs->opaque;
177
uint64_t old_length;
178
diff --git a/block/qed.c b/block/qed.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/block/qed.c
181
+++ b/block/qed.c
182
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
183
int64_t offset,
184
bool exact,
185
PreallocMode prealloc,
186
+ BdrvRequestFlags flags,
187
Error **errp)
188
{
189
BDRVQEDState *s = bs->opaque;
190
diff --git a/block/raw-format.c b/block/raw-format.c
191
index XXXXXXX..XXXXXXX 100644
192
--- a/block/raw-format.c
193
+++ b/block/raw-format.c
194
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
195
196
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
197
bool exact, PreallocMode prealloc,
198
- Error **errp)
199
+ BdrvRequestFlags flags, Error **errp)
200
{
201
BDRVRawState *s = bs->opaque;
202
203
diff --git a/block/rbd.c b/block/rbd.c
204
index XXXXXXX..XXXXXXX 100644
205
--- a/block/rbd.c
206
+++ b/block/rbd.c
207
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
208
int64_t offset,
209
bool exact,
210
PreallocMode prealloc,
211
+ BdrvRequestFlags flags,
212
Error **errp)
213
{
214
int r;
215
diff --git a/block/sheepdog.c b/block/sheepdog.c
216
index XXXXXXX..XXXXXXX 100644
217
--- a/block/sheepdog.c
218
+++ b/block/sheepdog.c
219
@@ -XXX,XX +XXX,XX @@ static int64_t sd_getlength(BlockDriverState *bs)
220
221
static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
222
bool exact, PreallocMode prealloc,
223
- Error **errp)
224
+ BdrvRequestFlags flags, Error **errp)
225
{
226
BDRVSheepdogState *s = bs->opaque;
227
int ret, fd;
228
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
229
230
assert(!flags);
231
if (offset > s->inode.vdi_size) {
232
- ret = sd_co_truncate(bs, offset, false, PREALLOC_MODE_OFF, NULL);
233
+ ret = sd_co_truncate(bs, offset, false, PREALLOC_MODE_OFF, 0, NULL);
234
if (ret < 0) {
235
return ret;
236
}
237
diff --git a/block/ssh.c b/block/ssh.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/block/ssh.c
240
+++ b/block/ssh.c
241
@@ -XXX,XX +XXX,XX @@ static int64_t ssh_getlength(BlockDriverState *bs)
242
243
static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
244
bool exact, PreallocMode prealloc,
245
- Error **errp)
246
+ BdrvRequestFlags flags, Error **errp)
247
{
248
BDRVSSHState *s = bs->opaque;
249
250
diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c
251
index XXXXXXX..XXXXXXX 100644
252
--- a/tests/test-block-iothread.c
253
+++ b/tests/test-block-iothread.c
254
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs,
255
256
static int coroutine_fn
257
bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
258
- PreallocMode prealloc, Error **errp)
259
+ PreallocMode prealloc, BdrvRequestFlags flags,
260
+ Error **errp)
261
{
262
return 0;
55
}
263
}
56
57
+/*
58
+ * This function checks whether the given @to_replace is allowed to be
59
+ * replaced by a node that always shows the same data as @bs. This is
60
+ * used for example to verify whether the mirror job can replace
61
+ * @to_replace by the target mirrored from @bs.
62
+ * To be replaceable, @bs and @to_replace may either be guaranteed to
63
+ * always show the same data (because they are only connected through
64
+ * filters), or some driver may allow replacing one of its children
65
+ * because it can guarantee that this child's data is not visible at
66
+ * all (for example, for dissenting quorum children that have no other
67
+ * parents).
68
+ */
69
+bool bdrv_recurse_can_replace(BlockDriverState *bs,
70
+ BlockDriverState *to_replace)
71
+{
72
+ if (!bs || !bs->drv) {
73
+ return false;
74
+ }
75
+
76
+ if (bs == to_replace) {
77
+ return true;
78
+ }
79
+
80
+ /* See what the driver can do */
81
+ if (bs->drv->bdrv_recurse_can_replace) {
82
+ return bs->drv->bdrv_recurse_can_replace(bs, to_replace);
83
+ }
84
+
85
+ /* For filters without an own implementation, we can recurse on our own */
86
+ if (bs->drv->is_filter) {
87
+ BdrvChild *child = bs->file ?: bs->backing;
88
+ return bdrv_recurse_can_replace(child->bs, to_replace);
89
+ }
90
+
91
+ /* Safe default */
92
+ return false;
93
+}
94
+
95
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
96
const char *node_name, Error **errp)
97
{
98
--
264
--
99
2.20.1
265
2.25.3
100
266
101
267
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Now that block drivers can support flags for .bdrv_co_truncate, expose
2
the parameter in the node level interfaces bdrv_co_truncate() and
3
bdrv_truncate().
2
4
3
It is unused now. (And it was ugly because it needed to explore all BDS
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
chains from the top.)
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20200218103454.296704-4-mreitz@redhat.com>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20200424125448.63318-3-kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
---
11
include/block/block.h | 1 -
12
include/block/block.h | 5 +++--
12
block.c | 26 --------------------------
13
block/block-backend.c | 2 +-
13
2 files changed, 27 deletions(-)
14
block/crypto.c | 2 +-
15
block/io.c | 12 +++++++-----
16
block/parallels.c | 6 +++---
17
block/qcow.c | 4 ++--
18
block/qcow2-refcount.c | 2 +-
19
block/qcow2.c | 15 +++++++++------
20
block/raw-format.c | 2 +-
21
block/vhdx-log.c | 2 +-
22
block/vhdx.c | 2 +-
23
block/vmdk.c | 2 +-
24
tests/test-block-iothread.c | 6 +++---
25
13 files changed, 34 insertions(+), 28 deletions(-)
14
26
15
diff --git a/include/block/block.h b/include/block/block.h
27
diff --git a/include/block/block.h b/include/block/block.h
16
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/block.h
29
--- a/include/block/block.h
18
+++ b/include/block/block.h
30
+++ b/include/block/block.h
19
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
31
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
20
/* external snapshots */
32
void bdrv_refresh_filename(BlockDriverState *bs);
21
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
33
22
BlockDriverState *candidate);
34
int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
23
-bool bdrv_is_first_non_filter(BlockDriverState *candidate);
35
- PreallocMode prealloc, Error **errp);
24
36
+ PreallocMode prealloc, BdrvRequestFlags flags,
25
/* check if a named node can be replaced when doing drive-mirror */
37
+ Error **errp);
26
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
38
int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
27
diff --git a/block.c b/block.c
39
- PreallocMode prealloc, Error **errp);
28
index XXXXXXX..XXXXXXX 100644
40
+ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp);
29
--- a/block.c
41
30
+++ b/block.c
42
int64_t bdrv_nb_sectors(BlockDriverState *bs);
31
@@ -XXX,XX +XXX,XX @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
43
int64_t bdrv_getlength(BlockDriverState *bs);
32
return false;
44
diff --git a/block/block-backend.c b/block/block-backend.c
33
}
45
index XXXXXXX..XXXXXXX 100644
34
46
--- a/block/block-backend.c
35
-/* This function checks if the candidate is the first non filter bs down it's
47
+++ b/block/block-backend.c
36
- * bs chain. Since we don't have pointers to parents it explore all bs chains
48
@@ -XXX,XX +XXX,XX @@ int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
37
- * from the top. Some filters can choose not to pass down the recursion.
49
return -ENOMEDIUM;
38
- */
50
}
39
-bool bdrv_is_first_non_filter(BlockDriverState *candidate)
51
40
-{
52
- return bdrv_truncate(blk->root, offset, exact, prealloc, errp);
41
- BlockDriverState *bs;
53
+ return bdrv_truncate(blk->root, offset, exact, prealloc, 0, errp);
42
- BdrvNextIterator it;
54
}
43
-
55
44
- /* walk down the bs forest recursively */
56
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
45
- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
57
diff --git a/block/crypto.c b/block/crypto.c
46
- bool perm;
58
index XXXXXXX..XXXXXXX 100644
47
-
59
--- a/block/crypto.c
48
- /* try to recurse in this top level bs */
60
+++ b/block/crypto.c
49
- perm = bdrv_recurse_is_first_non_filter(bs, candidate);
61
@@ -XXX,XX +XXX,XX @@ block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
50
-
62
51
- /* candidate is the first non filter */
63
offset += payload_offset;
52
- if (perm) {
64
53
- bdrv_next_cleanup(&it);
65
- return bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
54
- return true;
66
+ return bdrv_co_truncate(bs->file, offset, exact, prealloc, 0, errp);
55
- }
67
}
56
- }
68
57
-
69
static void block_crypto_close(BlockDriverState *bs)
58
- return false;
70
diff --git a/block/io.c b/block/io.c
59
-}
71
index XXXXXXX..XXXXXXX 100644
60
-
72
--- a/block/io.c
61
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
73
+++ b/block/io.c
62
const char *node_name, Error **errp)
74
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs)
75
* 'offset' bytes in length.
76
*/
77
int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
78
- PreallocMode prealloc, Error **errp)
79
+ PreallocMode prealloc, BdrvRequestFlags flags,
80
+ Error **errp)
63
{
81
{
82
BlockDriverState *bs = child->bs;
83
BlockDriver *drv = bs->drv;
84
BdrvTrackedRequest req;
85
- BdrvRequestFlags flags = 0;
86
int64_t old_size, new_bytes;
87
int ret;
88
89
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
90
}
91
ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, flags, errp);
92
} else if (bs->file && drv->is_filter) {
93
- ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
94
+ ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
95
} else {
96
error_setg(errp, "Image format driver does not support resize");
97
ret = -ENOTSUP;
98
@@ -XXX,XX +XXX,XX @@ typedef struct TruncateCo {
99
int64_t offset;
100
bool exact;
101
PreallocMode prealloc;
102
+ BdrvRequestFlags flags;
103
Error **errp;
104
int ret;
105
} TruncateCo;
106
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_truncate_co_entry(void *opaque)
107
{
108
TruncateCo *tco = opaque;
109
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->exact,
110
- tco->prealloc, tco->errp);
111
+ tco->prealloc, tco->flags, tco->errp);
112
aio_wait_kick();
113
}
114
115
int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
116
- PreallocMode prealloc, Error **errp)
117
+ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp)
118
{
119
Coroutine *co;
120
TruncateCo tco = {
121
@@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
122
.offset = offset,
123
.exact = exact,
124
.prealloc = prealloc,
125
+ .flags = flags,
126
.errp = errp,
127
.ret = NOT_DONE,
128
};
129
diff --git a/block/parallels.c b/block/parallels.c
130
index XXXXXXX..XXXXXXX 100644
131
--- a/block/parallels.c
132
+++ b/block/parallels.c
133
@@ -XXX,XX +XXX,XX @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
134
} else {
135
ret = bdrv_truncate(bs->file,
136
(s->data_end + space) << BDRV_SECTOR_BITS,
137
- false, PREALLOC_MODE_OFF, NULL);
138
+ false, PREALLOC_MODE_OFF, 0, NULL);
139
}
140
if (ret < 0) {
141
return ret;
142
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
143
* That means we have to pass exact=true.
144
*/
145
ret = bdrv_truncate(bs->file, res->image_end_offset, true,
146
- PREALLOC_MODE_OFF, &local_err);
147
+ PREALLOC_MODE_OFF, 0, &local_err);
148
if (ret < 0) {
149
error_report_err(local_err);
150
res->check_errors++;
151
@@ -XXX,XX +XXX,XX @@ static void parallels_close(BlockDriverState *bs)
152
153
/* errors are ignored, so we might as well pass exact=true */
154
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS, true,
155
- PREALLOC_MODE_OFF, NULL);
156
+ PREALLOC_MODE_OFF, 0, NULL);
157
}
158
159
g_free(s->bat_dirty_bmap);
160
diff --git a/block/qcow.c b/block/qcow.c
161
index XXXXXXX..XXXXXXX 100644
162
--- a/block/qcow.c
163
+++ b/block/qcow.c
164
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
165
return -E2BIG;
166
}
167
ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
168
- false, PREALLOC_MODE_OFF, NULL);
169
+ false, PREALLOC_MODE_OFF, 0, NULL);
170
if (ret < 0) {
171
return ret;
172
}
173
@@ -XXX,XX +XXX,XX @@ static int qcow_make_empty(BlockDriverState *bs)
174
l1_length) < 0)
175
return -1;
176
ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length, false,
177
- PREALLOC_MODE_OFF, NULL);
178
+ PREALLOC_MODE_OFF, 0, NULL);
179
if (ret < 0)
180
return ret;
181
182
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
183
index XXXXXXX..XXXXXXX 100644
184
--- a/block/qcow2-refcount.c
185
+++ b/block/qcow2-refcount.c
186
@@ -XXX,XX +XXX,XX @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
187
}
188
189
ret = bdrv_truncate(bs->file, offset + s->cluster_size, false,
190
- PREALLOC_MODE_OFF, &local_err);
191
+ PREALLOC_MODE_OFF, 0, &local_err);
192
if (ret < 0) {
193
error_report_err(local_err);
194
goto resize_fail;
195
diff --git a/block/qcow2.c b/block/qcow2.c
196
index XXXXXXX..XXXXXXX 100644
197
--- a/block/qcow2.c
198
+++ b/block/qcow2.c
199
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
200
mode = PREALLOC_MODE_OFF;
201
}
202
ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, false,
203
- mode, errp);
204
+ mode, 0, errp);
205
if (ret < 0) {
206
return ret;
207
}
208
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
209
* always fulfilled, so there is no need to pass it on.)
210
*/
211
bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
212
- false, PREALLOC_MODE_OFF, &local_err);
213
+ false, PREALLOC_MODE_OFF, 0, &local_err);
214
if (local_err) {
215
warn_reportf_err(local_err,
216
"Failed to truncate the tail of the image: ");
217
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
218
* file should be resized to the exact target size, too,
219
* so we pass @exact here.
220
*/
221
- ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, errp);
222
+ ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, 0,
223
+ errp);
224
if (ret < 0) {
225
goto fail;
226
}
227
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
228
new_file_size = allocation_start +
229
nb_new_data_clusters * s->cluster_size;
230
/* Image file grows, so @exact does not matter */
231
- ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, errp);
232
+ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, 0,
233
+ errp);
234
if (ret < 0) {
235
error_prepend(errp, "Failed to resize underlying file: ");
236
qcow2_free_clusters(bs, allocation_start,
237
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
238
if (len < 0) {
239
return len;
240
}
241
- return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL);
242
+ return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, 0,
243
+ NULL);
244
}
245
246
if (offset_into_cluster(s, offset)) {
247
@@ -XXX,XX +XXX,XX @@ static int make_completely_empty(BlockDriverState *bs)
248
}
249
250
ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size, false,
251
- PREALLOC_MODE_OFF, &local_err);
252
+ PREALLOC_MODE_OFF, 0, &local_err);
253
if (ret < 0) {
254
error_report_err(local_err);
255
goto fail;
256
diff --git a/block/raw-format.c b/block/raw-format.c
257
index XXXXXXX..XXXXXXX 100644
258
--- a/block/raw-format.c
259
+++ b/block/raw-format.c
260
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
261
262
s->size = offset;
263
offset += s->offset;
264
- return bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
265
+ return bdrv_co_truncate(bs->file, offset, exact, prealloc, 0, errp);
266
}
267
268
static void raw_eject(BlockDriverState *bs, bool eject_flag)
269
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
270
index XXXXXXX..XXXXXXX 100644
271
--- a/block/vhdx-log.c
272
+++ b/block/vhdx-log.c
273
@@ -XXX,XX +XXX,XX @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
274
goto exit;
275
}
276
ret = bdrv_truncate(bs->file, new_file_size, false,
277
- PREALLOC_MODE_OFF, NULL);
278
+ PREALLOC_MODE_OFF, 0, NULL);
279
if (ret < 0) {
280
goto exit;
281
}
282
diff --git a/block/vhdx.c b/block/vhdx.c
283
index XXXXXXX..XXXXXXX 100644
284
--- a/block/vhdx.c
285
+++ b/block/vhdx.c
286
@@ -XXX,XX +XXX,XX @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
287
}
288
289
return bdrv_truncate(bs->file, *new_offset + s->block_size, false,
290
- PREALLOC_MODE_OFF, NULL);
291
+ PREALLOC_MODE_OFF, 0, NULL);
292
}
293
294
/*
295
diff --git a/block/vmdk.c b/block/vmdk.c
296
index XXXXXXX..XXXXXXX 100644
297
--- a/block/vmdk.c
298
+++ b/block/vmdk.c
299
@@ -XXX,XX +XXX,XX @@ vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
300
}
301
length = QEMU_ALIGN_UP(length, BDRV_SECTOR_SIZE);
302
ret = bdrv_truncate(s->extents[i].file, length, false,
303
- PREALLOC_MODE_OFF, NULL);
304
+ PREALLOC_MODE_OFF, 0, NULL);
305
if (ret < 0) {
306
return ret;
307
}
308
diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c
309
index XXXXXXX..XXXXXXX 100644
310
--- a/tests/test-block-iothread.c
311
+++ b/tests/test-block-iothread.c
312
@@ -XXX,XX +XXX,XX @@ static void test_sync_op_truncate(BdrvChild *c)
313
int ret;
314
315
/* Normal success path */
316
- ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, NULL);
317
+ ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL);
318
g_assert_cmpint(ret, ==, 0);
319
320
/* Early error: Negative offset */
321
- ret = bdrv_truncate(c, -2, false, PREALLOC_MODE_OFF, NULL);
322
+ ret = bdrv_truncate(c, -2, false, PREALLOC_MODE_OFF, 0, NULL);
323
g_assert_cmpint(ret, ==, -EINVAL);
324
325
/* Error: Read-only image */
326
c->bs->read_only = true;
327
c->bs->open_flags &= ~BDRV_O_RDWR;
328
329
- ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, NULL);
330
+ ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL);
331
g_assert_cmpint(ret, ==, -EACCES);
332
333
c->bs->read_only = false;
64
--
334
--
65
2.20.1
335
2.25.3
66
336
67
337
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Now that node level interface bdrv_truncate() supports passing request
2
2
flags to the block driver, expose this on the BlockBackend level, too.
3
It no longer has any users.
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Message-Id: <20200218103454.296704-11-mreitz@redhat.com>
6
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200424125448.63318-4-kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
---
10
include/block/block.h | 4 ----
11
include/sysemu/block-backend.h | 2 +-
11
include/block/block_int.h | 8 --------
12
block.c | 3 ++-
12
block.c | 33 ---------------------------------
13
block/block-backend.c | 4 ++--
13
block/blkverify.c | 15 ---------------
14
block/commit.c | 4 ++--
14
block/copy-on-read.c | 9 ---------
15
block/crypto.c | 2 +-
15
block/filter-compress.c | 9 ---------
16
block/mirror.c | 2 +-
16
block/quorum.c | 18 ------------------
17
block/qcow2.c | 4 ++--
17
block/replication.c | 7 -------
18
block/qed.c | 2 +-
18
block/throttle.c | 8 --------
19
block/vdi.c | 2 +-
19
9 files changed, 111 deletions(-)
20
block/vhdx.c | 4 ++--
20
21
block/vmdk.c | 6 +++---
21
diff --git a/include/block/block.h b/include/block/block.h
22
block/vpc.c | 2 +-
22
index XXXXXXX..XXXXXXX 100644
23
blockdev.c | 2 +-
23
--- a/include/block/block.h
24
qemu-img.c | 2 +-
24
+++ b/include/block/block.h
25
qemu-io-cmds.c | 2 +-
25
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
26
15 files changed, 22 insertions(+), 21 deletions(-)
26
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
27
27
Error **errp);
28
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
28
29
index XXXXXXX..XXXXXXX 100644
29
-/* external snapshots */
30
--- a/include/sysemu/block-backend.h
30
-bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
31
+++ b/include/sysemu/block-backend.h
31
- BlockDriverState *candidate);
32
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
32
-
33
int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
33
/* check if a named node can be replaced when doing drive-mirror */
34
int bytes);
34
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
35
int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
35
const char *node_name, Error **errp);
36
- PreallocMode prealloc, Error **errp);
36
diff --git a/include/block/block_int.h b/include/block/block_int.h
37
+ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp);
37
index XXXXXXX..XXXXXXX 100644
38
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes);
38
--- a/include/block/block_int.h
39
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
39
+++ b/include/block/block_int.h
40
int64_t pos, int size);
40
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
41
* must implement them and return -ENOTSUP.
42
*/
43
bool is_filter;
44
- /* for snapshots block filter like Quorum can implement the
45
- * following recursive callback.
46
- * It's purpose is to recurse on the filter children while calling
47
- * bdrv_recurse_is_first_non_filter on them.
48
- * For a sample implementation look in the future Quorum block filter.
49
- */
50
- bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
51
- BlockDriverState *candidate);
52
/*
53
* Return true if @to_replace can be replaced by a BDS with the
54
* same data as @bs without it affecting @bs's behavior (that is,
55
diff --git a/block.c b/block.c
41
diff --git a/block.c b/block.c
56
index XXXXXXX..XXXXXXX 100644
42
index XXXXXXX..XXXXXXX 100644
57
--- a/block.c
43
--- a/block.c
58
+++ b/block.c
44
+++ b/block.c
59
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
45
@@ -XXX,XX +XXX,XX @@ static int64_t create_file_fallback_truncate(BlockBackend *blk,
60
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
46
int64_t size;
47
int ret;
48
49
- ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, &local_err);
50
+ ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0,
51
+ &local_err);
52
if (ret < 0 && ret != -ENOTSUP) {
53
error_propagate(errp, local_err);
54
return ret;
55
diff --git a/block/block-backend.c b/block/block-backend.c
56
index XXXXXXX..XXXXXXX 100644
57
--- a/block/block-backend.c
58
+++ b/block/block-backend.c
59
@@ -XXX,XX +XXX,XX @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
61
}
60
}
62
61
63
-/* This function will be called by the bdrv_recurse_is_first_non_filter method
62
int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
64
- * of block filter and by bdrv_is_first_non_filter.
63
- PreallocMode prealloc, Error **errp)
65
- * It is used to test if the given bs is the candidate or recurse more in the
64
+ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp)
66
- * node graph.
65
{
67
- */
66
if (!blk_is_available(blk)) {
68
-bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
67
error_setg(errp, "No medium inserted");
69
- BlockDriverState *candidate)
68
return -ENOMEDIUM;
70
-{
69
}
71
- /* return false if basic checks fails */
70
72
- if (!bs || !bs->drv) {
71
- return bdrv_truncate(blk->root, offset, exact, prealloc, 0, errp);
73
- return false;
72
+ return bdrv_truncate(blk->root, offset, exact, prealloc, flags, errp);
74
- }
75
-
76
- /* the code reached a non block filter driver -> check if the bs is
77
- * the same as the candidate. It's the recursion termination condition.
78
- */
79
- if (!bs->drv->is_filter) {
80
- return bs == candidate;
81
- }
82
- /* Down this path the driver is a block filter driver */
83
-
84
- /* If the block filter recursion method is defined use it to recurse down
85
- * the node graph.
86
- */
87
- if (bs->drv->bdrv_recurse_is_first_non_filter) {
88
- return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate);
89
- }
90
-
91
- /* the driver is a block filter but don't allow to recurse -> return false
92
- */
93
- return false;
94
-}
95
-
96
/*
97
* This function checks whether the given @to_replace is allowed to be
98
* replaced by a node that always shows the same data as @bs. This is
99
diff --git a/block/blkverify.c b/block/blkverify.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/block/blkverify.c
102
+++ b/block/blkverify.c
103
@@ -XXX,XX +XXX,XX @@ static int blkverify_co_flush(BlockDriverState *bs)
104
return bdrv_co_flush(s->test_file->bs);
105
}
73
}
106
74
107
-static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
75
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
108
- BlockDriverState *candidate)
76
diff --git a/block/commit.c b/block/commit.c
109
-{
77
index XXXXXXX..XXXXXXX 100644
110
- BDRVBlkverifyState *s = bs->opaque;
78
--- a/block/commit.c
111
-
79
+++ b/block/commit.c
112
- bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
80
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
113
-
81
}
114
- if (perm) {
82
115
- return true;
83
if (base_len < len) {
116
- }
84
- ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, NULL);
117
-
85
+ ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL);
118
- return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
86
if (ret) {
119
-}
87
goto out;
120
-
88
}
121
static bool blkverify_recurse_can_replace(BlockDriverState *bs,
89
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
122
BlockDriverState *to_replace)
90
* grow the backing file image if possible. If not possible,
123
{
91
* we must return an error */
124
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = {
92
if (length > backing_length) {
125
.bdrv_co_flush = blkverify_co_flush,
93
- ret = blk_truncate(backing, length, false, PREALLOC_MODE_OFF,
126
94
+ ret = blk_truncate(backing, length, false, PREALLOC_MODE_OFF, 0,
127
.is_filter = true,
95
&local_err);
128
- .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
96
if (ret < 0) {
129
.bdrv_recurse_can_replace = blkverify_recurse_can_replace,
97
error_report_err(local_err);
130
};
98
diff --git a/block/crypto.c b/block/crypto.c
131
99
index XXXXXXX..XXXXXXX 100644
132
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
100
--- a/block/crypto.c
133
index XXXXXXX..XXXXXXX 100644
101
+++ b/block/crypto.c
134
--- a/block/copy-on-read.c
102
@@ -XXX,XX +XXX,XX @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
135
+++ b/block/copy-on-read.c
103
* which will be used by the crypto header
136
@@ -XXX,XX +XXX,XX @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
104
*/
105
return blk_truncate(data->blk, data->size + headerlen, false,
106
- data->prealloc, errp);
107
+ data->prealloc, 0, errp);
137
}
108
}
138
109
139
110
140
-static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
111
diff --git a/block/mirror.c b/block/mirror.c
141
- BlockDriverState *candidate)
112
index XXXXXXX..XXXXXXX 100644
142
-{
113
--- a/block/mirror.c
143
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
114
+++ b/block/mirror.c
144
-}
115
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
145
-
116
146
-
117
if (s->bdev_length > base_length) {
147
static BlockDriver bdrv_copy_on_read = {
118
ret = blk_truncate(s->target, s->bdev_length, false,
148
.format_name = "copy-on-read",
119
- PREALLOC_MODE_OFF, NULL);
149
120
+ PREALLOC_MODE_OFF, 0, NULL);
150
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
121
if (ret < 0) {
151
122
goto immediate_exit;
152
.bdrv_co_block_status = bdrv_co_block_status_from_file,
123
}
153
124
diff --git a/block/qcow2.c b/block/qcow2.c
154
- .bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter,
125
index XXXXXXX..XXXXXXX 100644
155
-
126
--- a/block/qcow2.c
156
.has_variable_length = true,
127
+++ b/block/qcow2.c
157
.is_filter = true,
128
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
158
};
129
159
diff --git a/block/filter-compress.c b/block/filter-compress.c
130
/* Okay, now that we have a valid image, let's give it the right size */
160
index XXXXXXX..XXXXXXX 100644
131
ret = blk_truncate(blk, qcow2_opts->size, false, qcow2_opts->preallocation,
161
--- a/block/filter-compress.c
132
- errp);
162
+++ b/block/filter-compress.c
133
+ 0, errp);
163
@@ -XXX,XX +XXX,XX @@ static void compress_lock_medium(BlockDriverState *bs, bool locked)
134
if (ret < 0) {
164
}
135
error_prepend(errp, "Could not resize image: ");
165
136
goto out;
166
137
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
167
-static bool compress_recurse_is_first_non_filter(BlockDriverState *bs,
138
* Amending image options should ensure that the image has
168
- BlockDriverState *candidate)
139
* exactly the given new values, so pass exact=true here.
169
-{
140
*/
170
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
141
- ret = blk_truncate(blk, new_size, true, PREALLOC_MODE_OFF, errp);
171
-}
142
+ ret = blk_truncate(blk, new_size, true, PREALLOC_MODE_OFF, 0, errp);
172
-
143
blk_unref(blk);
173
-
144
if (ret < 0) {
174
static BlockDriver bdrv_compress = {
145
return ret;
175
.format_name = "compress",
146
diff --git a/block/qed.c b/block/qed.c
176
147
index XXXXXXX..XXXXXXX 100644
177
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_compress = {
148
--- a/block/qed.c
178
149
+++ b/block/qed.c
179
.bdrv_co_block_status = bdrv_co_block_status_from_file,
150
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
180
151
* The QED format associates file length with allocation status,
181
- .bdrv_recurse_is_first_non_filter = compress_recurse_is_first_non_filter,
152
* so a new file (which is empty) must have a length of 0.
182
-
153
*/
183
.has_variable_length = true,
154
- ret = blk_truncate(blk, 0, true, PREALLOC_MODE_OFF, errp);
184
.is_filter = true,
155
+ ret = blk_truncate(blk, 0, true, PREALLOC_MODE_OFF, 0, errp);
185
};
156
if (ret < 0) {
186
diff --git a/block/quorum.c b/block/quorum.c
157
goto out;
187
index XXXXXXX..XXXXXXX 100644
158
}
188
--- a/block/quorum.c
159
diff --git a/block/vdi.c b/block/vdi.c
189
+++ b/block/quorum.c
160
index XXXXXXX..XXXXXXX 100644
190
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
161
--- a/block/vdi.c
191
return result;
162
+++ b/block/vdi.c
192
}
163
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
193
164
194
-static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
165
if (image_type == VDI_TYPE_STATIC) {
195
- BlockDriverState *candidate)
166
ret = blk_truncate(blk, offset + blocks * block_size, false,
196
-{
167
- PREALLOC_MODE_OFF, errp);
197
- BDRVQuorumState *s = bs->opaque;
168
+ PREALLOC_MODE_OFF, 0, errp);
198
- int i;
169
if (ret < 0) {
199
-
170
error_prepend(errp, "Failed to statically allocate file");
200
- for (i = 0; i < s->num_children; i++) {
171
goto exit;
201
- bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs,
172
diff --git a/block/vhdx.c b/block/vhdx.c
202
- candidate);
173
index XXXXXXX..XXXXXXX 100644
203
- if (perm) {
174
--- a/block/vhdx.c
204
- return true;
175
+++ b/block/vhdx.c
205
- }
176
@@ -XXX,XX +XXX,XX @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
206
- }
177
/* All zeroes, so we can just extend the file - the end of the BAT
207
-
178
* is the furthest thing we have written yet */
208
- return false;
179
ret = blk_truncate(blk, data_file_offset, false, PREALLOC_MODE_OFF,
209
-}
180
- errp);
210
-
181
+ 0, errp);
211
static bool quorum_recurse_can_replace(BlockDriverState *bs,
182
if (ret < 0) {
212
BlockDriverState *to_replace)
183
goto exit;
213
{
184
}
214
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
185
} else if (type == VHDX_TYPE_FIXED) {
215
.bdrv_child_perm = quorum_child_perm,
186
ret = blk_truncate(blk, data_file_offset + image_size, false,
216
187
- PREALLOC_MODE_OFF, errp);
217
.is_filter = true,
188
+ PREALLOC_MODE_OFF, 0, errp);
218
- .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
189
if (ret < 0) {
219
.bdrv_recurse_can_replace = quorum_recurse_can_replace,
190
goto exit;
220
191
}
221
.strong_runtime_opts = quorum_strong_runtime_opts,
192
diff --git a/block/vmdk.c b/block/vmdk.c
222
diff --git a/block/replication.c b/block/replication.c
193
index XXXXXXX..XXXXXXX 100644
223
index XXXXXXX..XXXXXXX 100644
194
--- a/block/vmdk.c
224
--- a/block/replication.c
195
+++ b/block/vmdk.c
225
+++ b/block/replication.c
196
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_extent(BlockBackend *blk,
226
@@ -XXX,XX +XXX,XX @@ out:
197
int gd_buf_size;
227
return ret;
198
228
}
199
if (flat) {
229
200
- ret = blk_truncate(blk, filesize, false, PREALLOC_MODE_OFF, errp);
230
-static bool replication_recurse_is_first_non_filter(BlockDriverState *bs,
201
+ ret = blk_truncate(blk, filesize, false, PREALLOC_MODE_OFF, 0, errp);
231
- BlockDriverState *candidate)
202
goto exit;
232
-{
203
}
233
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
204
magic = cpu_to_be32(VMDK4_MAGIC);
234
-}
205
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_extent(BlockBackend *blk,
235
-
206
}
236
static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
207
237
{
208
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9, false,
238
Error *local_err = NULL;
209
- PREALLOC_MODE_OFF, errp);
239
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_replication = {
210
+ PREALLOC_MODE_OFF, 0, errp);
240
.bdrv_co_writev = replication_co_writev,
211
if (ret < 0) {
241
212
goto exit;
242
.is_filter = true,
213
}
243
- .bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter,
214
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vmdk_co_do_create(int64_t size,
244
215
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
245
.has_variable_length = true,
216
* for description file */
246
.strong_runtime_opts = replication_strong_runtime_opts,
217
if (desc_offset == 0) {
247
diff --git a/block/throttle.c b/block/throttle.c
218
- ret = blk_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, errp);
248
index XXXXXXX..XXXXXXX 100644
219
+ ret = blk_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, 0, errp);
249
--- a/block/throttle.c
220
if (ret < 0) {
250
+++ b/block/throttle.c
221
goto exit;
251
@@ -XXX,XX +XXX,XX @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state)
222
}
252
reopen_state->opaque = NULL;
223
diff --git a/block/vpc.c b/block/vpc.c
253
}
224
index XXXXXXX..XXXXXXX 100644
254
225
--- a/block/vpc.c
255
-static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
226
+++ b/block/vpc.c
256
- BlockDriverState *candidate)
227
@@ -XXX,XX +XXX,XX @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
257
-{
228
/* Add footer to total size */
258
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
229
total_size += HEADER_SIZE;
259
-}
230
260
-
231
- ret = blk_truncate(blk, total_size, false, PREALLOC_MODE_OFF, errp);
261
static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
232
+ ret = blk_truncate(blk, total_size, false, PREALLOC_MODE_OFF, 0, errp);
262
{
233
if (ret < 0) {
263
ThrottleGroupMember *tgm = bs->opaque;
234
return ret;
264
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_throttle = {
235
}
265
.bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes,
236
diff --git a/blockdev.c b/blockdev.c
266
.bdrv_co_pdiscard = throttle_co_pdiscard,
237
index XXXXXXX..XXXXXXX 100644
267
238
--- a/blockdev.c
268
- .bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter,
239
+++ b/blockdev.c
269
-
240
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
270
.bdrv_attach_aio_context = throttle_attach_aio_context,
241
}
271
.bdrv_detach_aio_context = throttle_detach_aio_context,
242
272
243
bdrv_drained_begin(bs);
244
- ret = blk_truncate(blk, size, false, PREALLOC_MODE_OFF, errp);
245
+ ret = blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
246
bdrv_drained_end(bs);
247
248
out:
249
diff --git a/qemu-img.c b/qemu-img.c
250
index XXXXXXX..XXXXXXX 100644
251
--- a/qemu-img.c
252
+++ b/qemu-img.c
253
@@ -XXX,XX +XXX,XX @@ static int img_resize(int argc, char **argv)
254
* resizing, so pass @exact=true. It is of no use to report
255
* success when the image has not actually been resized.
256
*/
257
- ret = blk_truncate(blk, total_size, true, prealloc, &err);
258
+ ret = blk_truncate(blk, total_size, true, prealloc, 0, &err);
259
if (!ret) {
260
qprintf(quiet, "Image resized.\n");
261
} else {
262
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
263
index XXXXXXX..XXXXXXX 100644
264
--- a/qemu-io-cmds.c
265
+++ b/qemu-io-cmds.c
266
@@ -XXX,XX +XXX,XX @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
267
* exact=true. It is better to err on the "emit more errors" side
268
* than to be overly permissive.
269
*/
270
- ret = blk_truncate(blk, offset, true, PREALLOC_MODE_OFF, &local_err);
271
+ ret = blk_truncate(blk, offset, true, PREALLOC_MODE_OFF, 0, &local_err);
272
if (ret < 0) {
273
error_report_err(local_err);
274
return ret;
273
--
275
--
274
2.20.1
276
2.25.3
275
277
276
278
diff view generated by jsdifflib
1
For external data file, cluster allocations return an offset in the data
1
If BDRV_REQ_ZERO_WRITE is set and we're extending the image, calling
2
file and are not refcounted. In this case, there is nothing to do for
2
qcow2_cluster_zeroize() with flags=0 does the right thing: It doesn't
3
qcow2_alloc_cluster_abort(). Freeing the same offset in the qcow2 file
3
undo any previous preallocation, but just adds the zero flag to all
4
is wrong and causes crashes in the better case or image corruption in
4
relevant L2 entries. If an external data file is in use, a write_zeroes
5
the worse case.
5
request to the data file is made instead.
6
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20200211094900.17315-3-kwolf@redhat.com>
8
Message-Id: <20200424125448.63318-5-kwolf@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
12
---
11
block/qcow2-cluster.c | 7 +++++--
13
block/qcow2-cluster.c | 2 +-
12
1 file changed, 5 insertions(+), 2 deletions(-)
14
block/qcow2.c | 34 ++++++++++++++++++++++++++++++++++
15
2 files changed, 35 insertions(+), 1 deletion(-)
13
16
14
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
17
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
15
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
16
--- a/block/qcow2-cluster.c
19
--- a/block/qcow2-cluster.c
17
+++ b/block/qcow2-cluster.c
20
+++ b/block/qcow2-cluster.c
18
@@ -XXX,XX +XXX,XX @@ err:
21
@@ -XXX,XX +XXX,XX @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
19
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
22
/* Caller must pass aligned values, except at image end */
20
{
23
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
21
BDRVQcow2State *s = bs->opaque;
24
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
22
- qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits,
25
- end_offset == bs->total_sectors << BDRV_SECTOR_BITS);
23
- QCOW2_DISCARD_NEVER);
26
+ end_offset >= bs->total_sectors << BDRV_SECTOR_BITS);
24
+ if (!has_data_file(bs)) {
27
25
+ qcow2_free_clusters(bs, m->alloc_offset,
28
/* The zero flag is only supported by version 3 and newer */
26
+ m->nb_clusters << s->cluster_bits,
29
if (s->qcow_version < 3) {
27
+ QCOW2_DISCARD_NEVER);
30
diff --git a/block/qcow2.c b/block/qcow2.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/block/qcow2.c
33
+++ b/block/qcow2.c
34
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
35
36
bs->supported_zero_flags = header.version >= 3 ?
37
BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0;
38
+ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
39
40
/* Repair image if dirty */
41
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
43
g_assert_not_reached();
44
}
45
46
+ if ((flags & BDRV_REQ_ZERO_WRITE) && offset > old_length) {
47
+ uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->cluster_size);
48
+
49
+ /*
50
+ * Use zero clusters as much as we can. qcow2_cluster_zeroize()
51
+ * requires a cluster-aligned start. The end may be unaligned if it is
52
+ * at the end of the image (which it is here).
53
+ */
54
+ ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0);
55
+ if (ret < 0) {
56
+ error_setg_errno(errp, -ret, "Failed to zero out new clusters");
57
+ goto fail;
58
+ }
59
+
60
+ /* Write explicit zeros for the unaligned head */
61
+ if (zero_start > old_length) {
62
+ uint64_t len = zero_start - old_length;
63
+ uint8_t *buf = qemu_blockalign0(bs, len);
64
+ QEMUIOVector qiov;
65
+ qemu_iovec_init_buf(&qiov, buf, len);
66
+
67
+ qemu_co_mutex_unlock(&s->lock);
68
+ ret = qcow2_co_pwritev_part(bs, old_length, len, &qiov, 0, 0);
69
+ qemu_co_mutex_lock(&s->lock);
70
+
71
+ qemu_vfree(buf);
72
+ if (ret < 0) {
73
+ error_setg_errno(errp, -ret, "Failed to zero out the new area");
74
+ goto fail;
75
+ }
76
+ }
28
+ }
77
+ }
29
}
78
+
30
79
if (prealloc != PREALLOC_MODE_OFF) {
31
/*
80
/* Flush metadata before actually changing the image size */
81
ret = qcow2_write_caches(bs);
32
--
82
--
33
2.20.1
83
2.25.3
34
84
35
85
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
The raw format driver can simply forward the flag and let its bs->file
2
child take care of actually providing the zeros.
2
3
3
Let check_to_replace_node() use the more specialized
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
bdrv_recurse_can_replace() instead of
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
bdrv_recurse_is_first_non_filter(), which is too restrictive (or, in the
6
Reviewed-by: Eric Blake <eblake@redhat.com>
6
case of quorum, sometimes not restrictive enough).
7
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Message-Id: <20200218103454.296704-10-mreitz@redhat.com>
8
Message-Id: <20200424125448.63318-6-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
10
---
13
block.c | 18 ++++++++++++++++--
11
block/raw-format.c | 4 +++-
14
1 file changed, 16 insertions(+), 2 deletions(-)
12
1 file changed, 3 insertions(+), 1 deletion(-)
15
13
16
diff --git a/block.c b/block.c
14
diff --git a/block/raw-format.c b/block/raw-format.c
17
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
16
--- a/block/raw-format.c
19
+++ b/block.c
17
+++ b/block/raw-format.c
20
@@ -XXX,XX +XXX,XX @@ bool bdrv_recurse_can_replace(BlockDriverState *bs,
18
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
21
return false;
19
20
s->size = offset;
21
offset += s->offset;
22
- return bdrv_co_truncate(bs->file, offset, exact, prealloc, 0, errp);
23
+ return bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
22
}
24
}
23
25
24
+/*
26
static void raw_eject(BlockDriverState *bs, bool eject_flag)
25
+ * Check whether the given @node_name can be replaced by a node that
27
@@ -XXX,XX +XXX,XX @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
26
+ * has the same data as @parent_bs. If so, return @node_name's BDS;
28
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
27
+ * NULL otherwise.
29
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
28
+ *
30
bs->file->bs->supported_zero_flags);
29
+ * @node_name must be a (recursive) *child of @parent_bs (or this
31
+ bs->supported_truncate_flags = bs->file->bs->supported_truncate_flags &
30
+ * function will return NULL).
32
+ BDRV_REQ_ZERO_WRITE;
31
+ *
33
32
+ * The result (whether the node can be replaced or not) is only valid
34
if (bs->probed && !bdrv_is_read_only(bs)) {
33
+ * for as long as no graph or permission changes occur.
35
bdrv_refresh_filename(bs->file->bs);
34
+ */
35
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
36
const char *node_name, Error **errp)
37
{
38
@@ -XXX,XX +XXX,XX @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
39
* Another benefit is that this tests exclude backing files which are
40
* blocked by the backing blockers.
41
*/
42
- if (!bdrv_recurse_is_first_non_filter(parent_bs, to_replace_bs)) {
43
- error_setg(errp, "Only top most non filter can be replaced");
44
+ if (!bdrv_recurse_can_replace(parent_bs, to_replace_bs)) {
45
+ error_setg(errp, "Cannot replace '%s' by a node mirrored from '%s', "
46
+ "because it cannot be guaranteed that doing so would not "
47
+ "lead to an abrupt change of visible data",
48
+ node_name, parent_bs->node_name);
49
to_replace_bs = NULL;
50
goto out;
51
}
52
--
36
--
53
2.20.1
37
2.25.3
54
38
55
39
diff view generated by jsdifflib
1
It is not obvious what 'ignore' actually means for block jobs: It could
1
For regular files, we always get BDRV_REQ_ZERO_WRITE behaviour from the
2
be continuing the job and returning success in the end despite the error
2
OS, so we can advertise the flag and just ignore it.
3
(no block job does this). It could also mean continuing and returning
4
failure in the end (this is what stream does). And it can mean retrying
5
the failed request later (this is what backup, commit and mirror do).
6
7
This (somewhat inconsistent) behaviour was introduced and described for
8
stream and mirror in commit 32c81a4a6ec. backup and commit were
9
introduced later and use the same model as mirror.
10
3
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20200214200812.28180-2-kwolf@redhat.com>
5
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Reviewed-by: Ján Tomko <jtomko@redhat.com>
6
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200424125448.63318-7-kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
10
---
16
qapi/block-core.json | 5 ++++-
11
block/file-posix.c | 4 ++++
17
1 file changed, 4 insertions(+), 1 deletion(-)
12
1 file changed, 4 insertions(+)
18
13
19
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/block/file-posix.c b/block/file-posix.c
20
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-core.json
16
--- a/block/file-posix.c
22
+++ b/qapi/block-core.json
17
+++ b/block/file-posix.c
23
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
24
# for jobs, cancel the job
19
#endif
25
#
20
26
# @ignore: ignore the error, only report a QMP event (BLOCK_IO_ERROR
21
bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
27
-# or BLOCK_JOB_ERROR)
22
+ if (S_ISREG(st.st_mode)) {
28
+# or BLOCK_JOB_ERROR). The backup, mirror and commit block jobs retry
23
+ /* When extending regular files, we get zeros from the OS */
29
+# the failing request later and may still complete successfully. The
24
+ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
30
+# stream block job continues to stream and will complete with an
25
+ }
31
+# error.
26
ret = 0;
32
#
27
fail:
33
# @enospc: same as @stop on ENOSPC, same as @report otherwise.
28
if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) {
34
#
35
--
29
--
36
2.20.1
30
2.25.3
37
31
38
32
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
When extending the size of an image that has a backing file larger than
2
its old size, make sure that the backing file data doesn't become
3
visible in the guest, but the added area is properly zeroed out.
2
4
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
Consider the following scenario where the overlay is shorter than its
4
Message-Id: <20200218103454.296704-9-mreitz@redhat.com>
6
backing file:
7
8
base.qcow2: AAAAAAAA
9
overlay.qcow2: BBBB
10
11
When resizing (extending) overlay.qcow2, the new blocks should not stay
12
unallocated and make the additional As from base.qcow2 visible like
13
before this patch, but zeros should be read.
14
15
A similar case happens with the various variants of a commit job when an
16
intermediate file is short (- for unallocated):
17
18
base.qcow2: A-A-AAAA
19
mid.qcow2: BB-B
20
top.qcow2: C--C--C-
21
22
After commit top.qcow2 to mid.qcow2, the following happens:
23
24
mid.qcow2: CB-C00C0 (correct result)
25
mid.qcow2: CB-C--C- (before this fix)
26
27
Without the fix, blocks that previously read as zeros on top.qcow2
28
suddenly turn into A.
29
30
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
31
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
32
Message-Id: <20200424125448.63318-8-kwolf@redhat.com>
33
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
35
---
7
block/quorum.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
36
block/io.c | 25 +++++++++++++++++++++++++
8
1 file changed, 54 insertions(+)
37
1 file changed, 25 insertions(+)
9
38
10
diff --git a/block/quorum.c b/block/quorum.c
39
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
40
index XXXXXXX..XXXXXXX 100644
12
--- a/block/quorum.c
41
--- a/block/io.c
13
+++ b/block/quorum.c
42
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@ static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
15
return false;
44
goto out;
16
}
45
}
17
46
18
+static bool quorum_recurse_can_replace(BlockDriverState *bs,
47
+ /*
19
+ BlockDriverState *to_replace)
48
+ * If the image has a backing file that is large enough that it would
20
+{
49
+ * provide data for the new area, we cannot leave it unallocated because
21
+ BDRVQuorumState *s = bs->opaque;
50
+ * then the backing file content would become visible. Instead, zero-fill
22
+ int i;
51
+ * the new area.
52
+ *
53
+ * Note that if the image has a backing file, but was opened without the
54
+ * backing file, taking care of keeping things consistent with that backing
55
+ * file is the user's responsibility.
56
+ */
57
+ if (new_bytes && bs->backing) {
58
+ int64_t backing_len;
23
+
59
+
24
+ for (i = 0; i < s->num_children; i++) {
60
+ backing_len = bdrv_getlength(backing_bs(bs));
25
+ /*
61
+ if (backing_len < 0) {
26
+ * We have no idea whether our children show the same data as
62
+ ret = backing_len;
27
+ * this node (@bs). It is actually highly likely that
63
+ error_setg_errno(errp, -ret, "Could not get backing file size");
28
+ * @to_replace does not, because replacing a broken child is
64
+ goto out;
29
+ * one of the main use cases here.
65
+ }
30
+ *
66
+
31
+ * We do know that the new BDS will match @bs, so replacing
67
+ if (backing_len > old_size) {
32
+ * any of our children by it will be safe. It cannot change
68
+ flags |= BDRV_REQ_ZERO_WRITE;
33
+ * the data this quorum node presents to its parents.
34
+ *
35
+ * However, replacing @to_replace by @bs in any of our
36
+ * children's chains may change visible data somewhere in
37
+ * there. We therefore cannot recurse down those chains with
38
+ * bdrv_recurse_can_replace().
39
+ * (More formally, bdrv_recurse_can_replace() requires that
40
+ * @to_replace will be replaced by something matching the @bs
41
+ * passed to it. We cannot guarantee that.)
42
+ *
43
+ * Thus, we can only check whether any of our immediate
44
+ * children matches @to_replace.
45
+ *
46
+ * (In the future, we might add a function to recurse down a
47
+ * chain that checks that nothing there cares about a change
48
+ * in data from the respective child in question. For
49
+ * example, most filters do not care when their child's data
50
+ * suddenly changes, as long as their parents do not care.)
51
+ */
52
+ if (s->children[i]->bs == to_replace) {
53
+ /*
54
+ * We now have to ensure that there is no other parent
55
+ * that cares about replacing this child by a node with
56
+ * potentially different data.
57
+ * We do so by checking whether there are any other parents
58
+ * at all, which is stricter than necessary, but also very
59
+ * simple. (We may decide to implement something more
60
+ * complex and permissive when there is an actual need for
61
+ * it.)
62
+ */
63
+ return QLIST_FIRST(&to_replace->parents) == s->children[i] &&
64
+ QLIST_NEXT(s->children[i], next_parent) == NULL;
65
+ }
69
+ }
66
+ }
70
+ }
67
+
71
+
68
+ return false;
72
if (drv->bdrv_co_truncate) {
69
+}
73
if (flags & ~bs->supported_truncate_flags) {
70
+
74
error_setg(errp, "Block driver does not support requested flags");
71
static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
72
{
73
74
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
75
76
.is_filter = true,
77
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
78
+ .bdrv_recurse_can_replace = quorum_recurse_can_replace,
79
80
.strong_runtime_opts = quorum_strong_runtime_opts,
81
};
82
--
75
--
83
2.20.1
76
2.25.3
84
77
85
78
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
We want to keep TEST_IMG for the full path of the main test image, but
2
filter_testfiles() must be called for other test images before replacing
3
other things like the image format because the test directory path could
4
contain the format as a substring.
2
5
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Insert a filter_testfiles() call between both.
4
Message-Id: <20200218103454.296704-15-mreitz@redhat.com>
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Message-Id: <20200424125448.63318-9-kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
13
---
7
tests/qemu-iotests/iotests.py | 59 +++++++++++++++++++++++++++++++++++
14
tests/qemu-iotests/iotests.py | 5 +++--
8
1 file changed, 59 insertions(+)
15
1 file changed, 3 insertions(+), 2 deletions(-)
9
16
10
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
17
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
11
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/qemu-iotests/iotests.py
19
--- a/tests/qemu-iotests/iotests.py
13
+++ b/tests/qemu-iotests/iotests.py
20
+++ b/tests/qemu-iotests/iotests.py
14
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
21
@@ -XXX,XX +XXX,XX @@ def filter_img_info(output, filename):
15
22
for line in output.split('\n'):
16
return fields.items() <= ret.items()
23
if 'disk size' in line or 'actual-size' in line:
17
24
continue
18
+ def assert_block_path(self, root, path, expected_node, graph=None):
25
- line = line.replace(filename, 'TEST_IMG') \
19
+ """
26
- .replace(imgfmt, 'IMGFMT')
20
+ Check whether the node under the given path in the block graph
27
+ line = line.replace(filename, 'TEST_IMG')
21
+ is @expected_node.
28
+ line = filter_testfiles(line)
22
+
29
+ line = line.replace(imgfmt, 'IMGFMT')
23
+ @root is the node name of the node where the @path is rooted.
30
line = re.sub('iters: [0-9]+', 'iters: XXX', line)
24
+
31
line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line)
25
+ @path is a string that consists of child names separated by
32
line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line)
26
+ slashes. It must begin with a slash.
27
+
28
+ Examples for @root + @path:
29
+ - root="qcow2-node", path="/backing/file"
30
+ - root="quorum-node", path="/children.2/file"
31
+
32
+ Hypothetically, @path could be empty, in which case it would
33
+ point to @root. However, in practice this case is not useful
34
+ and hence not allowed.
35
+
36
+ @expected_node may be None. (All elements of the path but the
37
+ leaf must still exist.)
38
+
39
+ @graph may be None or the result of an x-debug-query-block-graph
40
+ call that has already been performed.
41
+ """
42
+ if graph is None:
43
+ graph = self.qmp('x-debug-query-block-graph')['return']
44
+
45
+ iter_path = iter(path.split('/'))
46
+
47
+ # Must start with a /
48
+ assert next(iter_path) == ''
49
+
50
+ node = next((node for node in graph['nodes'] if node['name'] == root),
51
+ None)
52
+
53
+ # An empty @path is not allowed, so the root node must be present
54
+ assert node is not None, 'Root node %s not found' % root
55
+
56
+ for child_name in iter_path:
57
+ assert node is not None, 'Cannot follow path %s%s' % (root, path)
58
+
59
+ try:
60
+ node_id = next(edge['child'] for edge in graph['edges'] \
61
+ if edge['parent'] == node['id'] and
62
+ edge['name'] == child_name)
63
+
64
+ node = next(node for node in graph['nodes'] \
65
+ if node['id'] == node_id)
66
+ except StopIteration:
67
+ node = None
68
+
69
+ if node is None:
70
+ assert expected_node is None, \
71
+ 'No node found under %s (but expected %s)' % \
72
+ (path, expected_node)
73
+ else:
74
+ assert node['name'] == expected_node, \
75
+ 'Found node %s under %s (but expected %s)' % \
76
+ (node['name'], path, expected_node)
77
78
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
79
80
--
33
--
81
2.20.1
34
2.25.3
82
35
83
36
diff view generated by jsdifflib
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
2
Message-Id: <20200424125448.63318-10-kwolf@redhat.com>
3
Fix warning reported by Clang static code analyzer:
3
Reviewed-by: Max Reitz <mreitz@redhat.com>
4
5
CC block/qcow2-bitmap.o
6
block/qcow2-bitmap.c:650:5: warning: Value stored to 'ret' is never read
7
ret = -EINVAL;
8
^ ~~~~~~~
9
10
Fixes: 88ddffae8
11
Reported-by: Clang Static Analyzer
12
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
Message-Id: <20200215161557.4077-2-philmd@redhat.com>
14
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
15
Reviewed-by: Ján Tomko <jtomko@redhat.com>
16
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
6
---
19
block/qcow2-bitmap.c | 1 -
7
tests/qemu-iotests/274 | 155 +++++++++++++++++++++
20
1 file changed, 1 deletion(-)
8
tests/qemu-iotests/274.out | 268 +++++++++++++++++++++++++++++++++++++
9
tests/qemu-iotests/group | 1 +
10
3 files changed, 424 insertions(+)
11
create mode 100755 tests/qemu-iotests/274
12
create mode 100644 tests/qemu-iotests/274.out
21
13
22
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
14
diff --git a/tests/qemu-iotests/274 b/tests/qemu-iotests/274
15
new file mode 100755
16
index XXXXXXX..XXXXXXX
17
--- /dev/null
18
+++ b/tests/qemu-iotests/274
19
@@ -XXX,XX +XXX,XX @@
20
+#!/usr/bin/env python3
21
+#
22
+# Copyright (C) 2019 Red Hat, Inc.
23
+#
24
+# This program is free software; you can redistribute it and/or modify
25
+# it under the terms of the GNU General Public License as published by
26
+# the Free Software Foundation; either version 2 of the License, or
27
+# (at your option) any later version.
28
+#
29
+# This program is distributed in the hope that it will be useful,
30
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
31
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
+# GNU General Public License for more details.
33
+#
34
+# You should have received a copy of the GNU General Public License
35
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
36
+#
37
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
38
+#
39
+# Some tests for short backing files and short overlays
40
+
41
+import iotests
42
+
43
+iotests.verify_image_format(supported_fmts=['qcow2'])
44
+iotests.verify_platform(['linux'])
45
+
46
+size_short = 1 * 1024 * 1024
47
+size_long = 2 * 1024 * 1024
48
+size_diff = size_long - size_short
49
+
50
+def create_chain() -> None:
51
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, base,
52
+ str(size_long))
53
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base, mid,
54
+ str(size_short))
55
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', mid, top,
56
+ str(size_long))
57
+
58
+ iotests.qemu_io_log('-c', 'write -P 1 0 %d' % size_long, base)
59
+
60
+def create_vm() -> iotests.VM:
61
+ vm = iotests.VM()
62
+ vm.add_blockdev('file,filename=%s,node-name=base-file' % base)
63
+ vm.add_blockdev('%s,file=base-file,node-name=base' % iotests.imgfmt)
64
+ vm.add_blockdev('file,filename=%s,node-name=mid-file' % mid)
65
+ vm.add_blockdev('%s,file=mid-file,node-name=mid,backing=base'
66
+ % iotests.imgfmt)
67
+ vm.add_drive(top, 'backing=mid,node-name=top')
68
+ return vm
69
+
70
+with iotests.FilePath('base') as base, \
71
+ iotests.FilePath('mid') as mid, \
72
+ iotests.FilePath('top') as top:
73
+
74
+ iotests.log('== Commit tests ==')
75
+
76
+ create_chain()
77
+
78
+ iotests.log('=== Check visible data ===')
79
+
80
+ iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, top)
81
+ iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), top)
82
+
83
+ iotests.log('=== Checking allocation status ===')
84
+
85
+ iotests.qemu_io_log('-c', 'alloc 0 %d' % size_short,
86
+ '-c', 'alloc %d %d' % (size_short, size_diff),
87
+ base)
88
+
89
+ iotests.qemu_io_log('-c', 'alloc 0 %d' % size_short,
90
+ '-c', 'alloc %d %d' % (size_short, size_diff),
91
+ mid)
92
+
93
+ iotests.qemu_io_log('-c', 'alloc 0 %d' % size_short,
94
+ '-c', 'alloc %d %d' % (size_short, size_diff),
95
+ top)
96
+
97
+ iotests.log('=== Checking map ===')
98
+
99
+ iotests.qemu_img_log('map', '--output=json', base)
100
+ iotests.qemu_img_log('map', '--output=human', base)
101
+ iotests.qemu_img_log('map', '--output=json', mid)
102
+ iotests.qemu_img_log('map', '--output=human', mid)
103
+ iotests.qemu_img_log('map', '--output=json', top)
104
+ iotests.qemu_img_log('map', '--output=human', top)
105
+
106
+ iotests.log('=== Testing qemu-img commit (top -> mid) ===')
107
+
108
+ iotests.qemu_img_log('commit', top)
109
+ iotests.img_info_log(mid)
110
+ iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, mid)
111
+ iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), mid)
112
+
113
+ iotests.log('=== Testing HMP commit (top -> mid) ===')
114
+
115
+ create_chain()
116
+ with create_vm() as vm:
117
+ vm.launch()
118
+ vm.qmp_log('human-monitor-command', command_line='commit drive0')
119
+
120
+ iotests.img_info_log(mid)
121
+ iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, mid)
122
+ iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), mid)
123
+
124
+ iotests.log('=== Testing QMP active commit (top -> mid) ===')
125
+
126
+ create_chain()
127
+ with create_vm() as vm:
128
+ vm.launch()
129
+ vm.qmp_log('block-commit', device='top', base_node='mid',
130
+ job_id='job0', auto_dismiss=False)
131
+ vm.run_job('job0', wait=5)
132
+
133
+ iotests.img_info_log(mid)
134
+ iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, mid)
135
+ iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), mid)
136
+
137
+
138
+ iotests.log('== Resize tests ==')
139
+
140
+ # Use different sizes for different allocation modes:
141
+ #
142
+ # We want to have at least one test where 32 bit truncation in the size of
143
+ # the overlapping area becomes visible. This is covered by the
144
+ # prealloc='off' case (1G to 6G is an overlap of 5G).
145
+ #
146
+ # However, we can only do this for modes that don't preallocate data
147
+ # because otherwise we might run out of space on the test host.
148
+ #
149
+ # We also want to test some unaligned combinations.
150
+ for (prealloc, base_size, top_size_old, top_size_new, off) in [
151
+ ('off', '6G', '1G', '8G', '5G'),
152
+ ('metadata', '32G', '30G', '33G', '31G'),
153
+ ('falloc', '10M', '5M', '15M', '9M'),
154
+ ('full', '16M', '8M', '12M', '11M'),
155
+ ('off', '384k', '253k', '512k', '253k'),
156
+ ('off', '400k', '256k', '512k', '336k'),
157
+ ('off', '512k', '256k', '500k', '436k')]:
158
+
159
+ iotests.log('=== preallocation=%s ===' % prealloc)
160
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, base, base_size)
161
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base, top,
162
+ top_size_old)
163
+ iotests.qemu_io_log('-c', 'write -P 1 %s 64k' % off, base)
164
+
165
+ # After this, top_size_old to base_size should be allocated/zeroed.
166
+ #
167
+ # In theory, leaving base_size to top_size_new unallocated would be
168
+ # correct, but in practice, if we zero out anything, we zero out
169
+ # everything up to top_size_new.
170
+ iotests.qemu_img_log('resize', '-f', iotests.imgfmt,
171
+ '--preallocation', prealloc, top, top_size_new)
172
+ iotests.qemu_io_log('-c', 'read -P 0 %s 64k' % off, top)
173
+ iotests.qemu_io_log('-c', 'map', top)
174
+ iotests.qemu_img_log('map', '--output=json', top)
175
diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out
176
new file mode 100644
177
index XXXXXXX..XXXXXXX
178
--- /dev/null
179
+++ b/tests/qemu-iotests/274.out
180
@@ -XXX,XX +XXX,XX @@
181
+== Commit tests ==
182
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16
183
+
184
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
185
+
186
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16
187
+
188
+wrote 2097152/2097152 bytes at offset 0
189
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
190
+
191
+=== Check visible data ===
192
+read 1048576/1048576 bytes at offset 0
193
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
194
+
195
+read 1048576/1048576 bytes at offset 1048576
196
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
197
+
198
+=== Checking allocation status ===
199
+1048576/1048576 bytes allocated at offset 0 bytes
200
+1048576/1048576 bytes allocated at offset 1 MiB
201
+
202
+0/1048576 bytes allocated at offset 0 bytes
203
+0/0 bytes allocated at offset 1 MiB
204
+
205
+0/1048576 bytes allocated at offset 0 bytes
206
+0/1048576 bytes allocated at offset 1 MiB
207
+
208
+=== Checking map ===
209
+[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true, "offset": 327680}]
210
+
211
+Offset Length Mapped to File
212
+0 0x200000 0x50000 TEST_DIR/PID-base
213
+
214
+[{ "start": 0, "length": 1048576, "depth": 1, "zero": false, "data": true, "offset": 327680}]
215
+
216
+Offset Length Mapped to File
217
+0 0x100000 0x50000 TEST_DIR/PID-base
218
+
219
+[{ "start": 0, "length": 1048576, "depth": 2, "zero": false, "data": true, "offset": 327680},
220
+{ "start": 1048576, "length": 1048576, "depth": 0, "zero": true, "data": false}]
221
+
222
+Offset Length Mapped to File
223
+0 0x100000 0x50000 TEST_DIR/PID-base
224
+
225
+=== Testing qemu-img commit (top -> mid) ===
226
+Image committed.
227
+
228
+image: TEST_IMG
229
+file format: IMGFMT
230
+virtual size: 2 MiB (2097152 bytes)
231
+cluster_size: 65536
232
+backing file: TEST_DIR/PID-base
233
+Format specific information:
234
+ compat: 1.1
235
+ lazy refcounts: false
236
+ refcount bits: 16
237
+ corrupt: false
238
+
239
+read 1048576/1048576 bytes at offset 0
240
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
241
+
242
+read 1048576/1048576 bytes at offset 1048576
243
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
244
+
245
+=== Testing HMP commit (top -> mid) ===
246
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16
247
+
248
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
249
+
250
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16
251
+
252
+wrote 2097152/2097152 bytes at offset 0
253
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
254
+
255
+{"execute": "human-monitor-command", "arguments": {"command-line": "commit drive0"}}
256
+{"return": ""}
257
+image: TEST_IMG
258
+file format: IMGFMT
259
+virtual size: 2 MiB (2097152 bytes)
260
+cluster_size: 65536
261
+backing file: TEST_DIR/PID-base
262
+Format specific information:
263
+ compat: 1.1
264
+ lazy refcounts: false
265
+ refcount bits: 16
266
+ corrupt: false
267
+
268
+read 1048576/1048576 bytes at offset 0
269
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
270
+
271
+read 1048576/1048576 bytes at offset 1048576
272
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
273
+
274
+=== Testing QMP active commit (top -> mid) ===
275
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=2097152 cluster_size=65536 lazy_refcounts=off refcount_bits=16
276
+
277
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 size=1048576 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
278
+
279
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=2097152 backing_file=TEST_DIR/PID-mid cluster_size=65536 lazy_refcounts=off refcount_bits=16
280
+
281
+wrote 2097152/2097152 bytes at offset 0
282
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
283
+
284
+{"execute": "block-commit", "arguments": {"auto-dismiss": false, "base-node": "mid", "device": "top", "job-id": "job0"}}
285
+{"return": {}}
286
+{"execute": "job-complete", "arguments": {"id": "job0"}}
287
+{"return": {}}
288
+{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
289
+{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
290
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
291
+{"return": {}}
292
+image: TEST_IMG
293
+file format: IMGFMT
294
+virtual size: 2 MiB (2097152 bytes)
295
+cluster_size: 65536
296
+backing file: TEST_DIR/PID-base
297
+Format specific information:
298
+ compat: 1.1
299
+ lazy refcounts: false
300
+ refcount bits: 16
301
+ corrupt: false
302
+
303
+read 1048576/1048576 bytes at offset 0
304
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
305
+
306
+read 1048576/1048576 bytes at offset 1048576
307
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
308
+
309
+== Resize tests ==
310
+=== preallocation=off ===
311
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=6442450944 cluster_size=65536 lazy_refcounts=off refcount_bits=16
312
+
313
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=1073741824 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
314
+
315
+wrote 65536/65536 bytes at offset 5368709120
316
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
317
+
318
+Image resized.
319
+
320
+read 65536/65536 bytes at offset 5368709120
321
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
322
+
323
+1 GiB (0x40000000) bytes not allocated at offset 0 bytes (0x0)
324
+7 GiB (0x1c0000000) bytes allocated at offset 1 GiB (0x40000000)
325
+
326
+[{ "start": 0, "length": 1073741824, "depth": 1, "zero": true, "data": false},
327
+{ "start": 1073741824, "length": 7516192768, "depth": 0, "zero": true, "data": false}]
328
+
329
+=== preallocation=metadata ===
330
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=34359738368 cluster_size=65536 lazy_refcounts=off refcount_bits=16
331
+
332
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=32212254720 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
333
+
334
+wrote 65536/65536 bytes at offset 33285996544
335
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
336
+
337
+Image resized.
338
+
339
+read 65536/65536 bytes at offset 33285996544
340
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
341
+
342
+30 GiB (0x780000000) bytes not allocated at offset 0 bytes (0x0)
343
+3 GiB (0xc0000000) bytes allocated at offset 30 GiB (0x780000000)
344
+
345
+[{ "start": 0, "length": 32212254720, "depth": 1, "zero": true, "data": false},
346
+{ "start": 32212254720, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 327680},
347
+{ "start": 32749125632, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 537264128},
348
+{ "start": 33285996544, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 1074200576},
349
+{ "start": 33822867456, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 1611137024},
350
+{ "start": 34359738368, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 2148139008},
351
+{ "start": 34896609280, "length": 536870912, "depth": 0, "zero": true, "data": false, "offset": 2685075456}]
352
+
353
+=== preallocation=falloc ===
354
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=10485760 cluster_size=65536 lazy_refcounts=off refcount_bits=16
355
+
356
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=5242880 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
357
+
358
+wrote 65536/65536 bytes at offset 9437184
359
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
360
+
361
+Image resized.
362
+
363
+read 65536/65536 bytes at offset 9437184
364
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
365
+
366
+5 MiB (0x500000) bytes not allocated at offset 0 bytes (0x0)
367
+10 MiB (0xa00000) bytes allocated at offset 5 MiB (0x500000)
368
+
369
+[{ "start": 0, "length": 5242880, "depth": 1, "zero": true, "data": false},
370
+{ "start": 5242880, "length": 10485760, "depth": 0, "zero": true, "data": false, "offset": 327680}]
371
+
372
+=== preallocation=full ===
373
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=16777216 cluster_size=65536 lazy_refcounts=off refcount_bits=16
374
+
375
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=8388608 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
376
+
377
+wrote 65536/65536 bytes at offset 11534336
378
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
379
+
380
+Image resized.
381
+
382
+read 65536/65536 bytes at offset 11534336
383
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
384
+
385
+8 MiB (0x800000) bytes not allocated at offset 0 bytes (0x0)
386
+4 MiB (0x400000) bytes allocated at offset 8 MiB (0x800000)
387
+
388
+[{ "start": 0, "length": 8388608, "depth": 1, "zero": true, "data": false},
389
+{ "start": 8388608, "length": 4194304, "depth": 0, "zero": true, "data": false, "offset": 327680}]
390
+
391
+=== preallocation=off ===
392
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=393216 cluster_size=65536 lazy_refcounts=off refcount_bits=16
393
+
394
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=259072 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
395
+
396
+wrote 65536/65536 bytes at offset 259072
397
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
398
+
399
+Image resized.
400
+
401
+read 65536/65536 bytes at offset 259072
402
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
403
+
404
+192 KiB (0x30000) bytes not allocated at offset 0 bytes (0x0)
405
+320 KiB (0x50000) bytes allocated at offset 192 KiB (0x30000)
406
+
407
+[{ "start": 0, "length": 196608, "depth": 1, "zero": true, "data": false},
408
+{ "start": 196608, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": 327680},
409
+{ "start": 262144, "length": 262144, "depth": 0, "zero": true, "data": false}]
410
+
411
+=== preallocation=off ===
412
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=409600 cluster_size=65536 lazy_refcounts=off refcount_bits=16
413
+
414
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=262144 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
415
+
416
+wrote 65536/65536 bytes at offset 344064
417
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
418
+
419
+Image resized.
420
+
421
+read 65536/65536 bytes at offset 344064
422
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
423
+
424
+256 KiB (0x40000) bytes not allocated at offset 0 bytes (0x0)
425
+256 KiB (0x40000) bytes allocated at offset 256 KiB (0x40000)
426
+
427
+[{ "start": 0, "length": 262144, "depth": 1, "zero": true, "data": false},
428
+{ "start": 262144, "length": 262144, "depth": 0, "zero": true, "data": false}]
429
+
430
+=== preallocation=off ===
431
+Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=524288 cluster_size=65536 lazy_refcounts=off refcount_bits=16
432
+
433
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 size=262144 backing_file=TEST_DIR/PID-base cluster_size=65536 lazy_refcounts=off refcount_bits=16
434
+
435
+wrote 65536/65536 bytes at offset 446464
436
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
437
+
438
+Image resized.
439
+
440
+read 65536/65536 bytes at offset 446464
441
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
442
+
443
+256 KiB (0x40000) bytes not allocated at offset 0 bytes (0x0)
444
+244 KiB (0x3d000) bytes allocated at offset 256 KiB (0x40000)
445
+
446
+[{ "start": 0, "length": 262144, "depth": 1, "zero": true, "data": false},
447
+{ "start": 262144, "length": 249856, "depth": 0, "zero": true, "data": false}]
448
+
449
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
23
index XXXXXXX..XXXXXXX 100644
450
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2-bitmap.c
451
--- a/tests/qemu-iotests/group
25
+++ b/block/qcow2-bitmap.c
452
+++ b/tests/qemu-iotests/group
26
@@ -XXX,XX +XXX,XX @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
453
@@ -XXX,XX +XXX,XX @@
27
return bm_list;
454
270 rw backing quick
28
455
272 rw
29
broken_dir:
456
273 backing quick
30
- ret = -EINVAL;
457
+274 rw backing
31
error_setg(errp, "Broken bitmap directory");
458
277 rw quick
32
459
279 rw backing quick
33
fail:
460
280 rw migration quick
34
--
461
--
35
2.20.1
462
2.25.3
36
463
37
464
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
The BDRV_REQ_ZERO_WRITE is currently implemented in a way that first the
2
image is possibly preallocated and then the zero flag is added to all
3
clusters. This means that a copy-on-write operation may be needed when
4
writing to these clusters, despite having used preallocation, negating
5
one of the major benefits of preallocation.
2
6
3
I/O requests to encrypted media should be aligned to the sector size
7
Instead, try to forward the BDRV_REQ_ZERO_WRITE to the protocol driver,
4
used by the underlying encryption method, not to BDRV_SECTOR_SIZE.
8
and if the protocol driver can ensure that the new area reads as zeros,
5
Fortunately this doesn't break anything at the moment because
9
we can skip setting the zero flag in the qcow2 layer.
6
both existing QCRYPTO_BLOCK_*_SECTOR_SIZE have the same value as
7
BDRV_SECTOR_SIZE.
8
10
9
The checks in qcow2_co_preadv_encrypted() are also unnecessary because
11
Unfortunately, the same approach doesn't work for metadata
10
they are repeated immediately afterwards in qcow2_co_encdec().
12
preallocation, so we'll still set the zero flag there.
11
13
12
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20200213171646.15876-1-berto@igalia.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
16
Message-Id: <20200424142701.67053-1-kwolf@redhat.com>
17
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
19
---
17
block/qcow2-threads.c | 12 ++++++++----
20
block/qcow2.c | 22 +++++++++++++++++++---
18
block/qcow2.c | 2 --
21
tests/qemu-iotests/274.out | 4 ++--
19
2 files changed, 8 insertions(+), 6 deletions(-)
22
2 files changed, 21 insertions(+), 5 deletions(-)
20
23
21
diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block/qcow2-threads.c
24
+++ b/block/qcow2-threads.c
25
@@ -XXX,XX +XXX,XX @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
26
.len = len,
27
.func = func,
28
};
29
+ uint64_t sector_size;
30
31
- assert(QEMU_IS_ALIGNED(guest_offset, BDRV_SECTOR_SIZE));
32
- assert(QEMU_IS_ALIGNED(host_offset, BDRV_SECTOR_SIZE));
33
- assert(QEMU_IS_ALIGNED(len, BDRV_SECTOR_SIZE));
34
assert(s->crypto);
35
36
+ sector_size = qcrypto_block_get_sector_size(s->crypto);
37
+ assert(QEMU_IS_ALIGNED(guest_offset, sector_size));
38
+ assert(QEMU_IS_ALIGNED(host_offset, sector_size));
39
+ assert(QEMU_IS_ALIGNED(len, sector_size));
40
+
41
return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
42
}
43
44
@@ -XXX,XX +XXX,XX @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
45
* will be written to the underlying storage device at
46
* @host_offset
47
*
48
- * @len - length of the buffer (must be a BDRV_SECTOR_SIZE multiple)
49
+ * @len - length of the buffer (must be a multiple of the encryption
50
+ * sector size)
51
*
52
* Depending on the encryption method, @host_offset and/or @guest_offset
53
* may be used for generating the initialization vector for
54
diff --git a/block/qcow2.c b/block/qcow2.c
24
diff --git a/block/qcow2.c b/block/qcow2.c
55
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
56
--- a/block/qcow2.c
26
--- a/block/qcow2.c
57
+++ b/block/qcow2.c
27
+++ b/block/qcow2.c
58
@@ -XXX,XX +XXX,XX @@ qcow2_co_preadv_encrypted(BlockDriverState *bs,
28
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
59
goto fail;
29
/* Allocate the data area */
60
}
30
new_file_size = allocation_start +
61
31
nb_new_data_clusters * s->cluster_size;
62
- assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
32
- /* Image file grows, so @exact does not matter */
63
- assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
33
- ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, 0,
64
if (qcow2_co_decrypt(bs,
34
- errp);
65
file_cluster_offset + offset_into_cluster(s, offset),
35
+ /*
66
offset, buf, bytes) < 0)
36
+ * Image file grows, so @exact does not matter.
37
+ *
38
+ * If we need to zero out the new area, try first whether the protocol
39
+ * driver can already take care of this.
40
+ */
41
+ if (flags & BDRV_REQ_ZERO_WRITE) {
42
+ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc,
43
+ BDRV_REQ_ZERO_WRITE, NULL);
44
+ if (ret >= 0) {
45
+ flags &= ~BDRV_REQ_ZERO_WRITE;
46
+ }
47
+ } else {
48
+ ret = -1;
49
+ }
50
+ if (ret < 0) {
51
+ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, 0,
52
+ errp);
53
+ }
54
if (ret < 0) {
55
error_prepend(errp, "Failed to resize underlying file: ");
56
qcow2_free_clusters(bs, allocation_start,
57
diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out
58
index XXXXXXX..XXXXXXX 100644
59
--- a/tests/qemu-iotests/274.out
60
+++ b/tests/qemu-iotests/274.out
61
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 9437184
62
10 MiB (0xa00000) bytes allocated at offset 5 MiB (0x500000)
63
64
[{ "start": 0, "length": 5242880, "depth": 1, "zero": true, "data": false},
65
-{ "start": 5242880, "length": 10485760, "depth": 0, "zero": true, "data": false, "offset": 327680}]
66
+{ "start": 5242880, "length": 10485760, "depth": 0, "zero": false, "data": true, "offset": 327680}]
67
68
=== preallocation=full ===
69
Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=16777216 cluster_size=65536 lazy_refcounts=off refcount_bits=16
70
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 11534336
71
4 MiB (0x400000) bytes allocated at offset 8 MiB (0x800000)
72
73
[{ "start": 0, "length": 8388608, "depth": 1, "zero": true, "data": false},
74
-{ "start": 8388608, "length": 4194304, "depth": 0, "zero": true, "data": false, "offset": 327680}]
75
+{ "start": 8388608, "length": 4194304, "depth": 0, "zero": false, "data": true, "offset": 327680}]
76
77
=== preallocation=off ===
78
Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=393216 cluster_size=65536 lazy_refcounts=off refcount_bits=16
67
--
79
--
68
2.20.1
80
2.25.3
69
81
70
82
diff view generated by jsdifflib
1
From: Hikaru Nishida <hikarupsp@gmail.com>
1
From: Andrzej Jakowski <andrzej.jakowski@linux.intel.com>
2
2
3
Before this commit, BDRVVVFATState.qcow is unrefed in write_target_close
3
This patch introduces support for PMR that has been defined as part of NVMe 1.4
4
on closing backing bdrv of vvfat. However, qcow bdrv is opend as a child
4
spec. User can now specify a pmrdev option that should point to HostMemoryBackend.
5
of vvfat in enable_write_target() so it will be also unrefed on closing
5
pmrdev memory region will subsequently be exposed as PCI BAR 2 in emulated NVMe
6
vvfat itself. This causes use-after-free of qcow on freeing vvfat which
6
device. Guest OS can perform mmio read and writes to the PMR region that will stay
7
has backing bdrv and qcow bdrv as children in this order because
7
persistent across system reboot.
8
bdrv_close(vvfat) tries to free qcow bdrv after freeing backing bdrv
9
as QLIST_FOREACH_SAFE() loop keeps next pointer, but BdrvChild of qcow
10
is already freed in bdrv_close(backing bdrv).
11
8
12
Signed-off-by: Hikaru Nishida <hikarupsp@gmail.com>
9
Signed-off-by: Andrzej Jakowski <andrzej.jakowski@linux.intel.com>
13
Message-Id: <20200209175156.85748-1-hikarupsp@gmail.com>
10
Reviewed-by: Klaus Jensen <k.jensen@samsung.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Message-Id: <20200330164656.9348-1-andrzej.jakowski@linux.intel.com>
13
Reviewed-by: Keith Busch <kbusch@kernel.org>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
15
---
16
block/vvfat.c | 7 -------
16
hw/block/nvme.h | 2 +
17
1 file changed, 7 deletions(-)
17
include/block/nvme.h | 172 +++++++++++++++++++++++++++++++++++++++++
18
hw/block/nvme.c | 109 ++++++++++++++++++++++++++
19
hw/block/Makefile.objs | 2 +-
20
hw/block/trace-events | 4 +
21
5 files changed, 288 insertions(+), 1 deletion(-)
18
22
19
diff --git a/block/vvfat.c b/block/vvfat.c
23
diff --git a/hw/block/nvme.h b/hw/block/nvme.h
20
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
21
--- a/block/vvfat.c
25
--- a/hw/block/nvme.h
22
+++ b/block/vvfat.c
26
+++ b/hw/block/nvme.h
23
@@ -XXX,XX +XXX,XX @@ write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
27
@@ -XXX,XX +XXX,XX @@ typedef struct NvmeCtrl {
24
return ret;
28
uint64_t timestamp_set_qemu_clock_ms; /* QEMU clock time */
29
30
char *serial;
31
+ HostMemoryBackend *pmrdev;
32
+
33
NvmeNamespace *namespaces;
34
NvmeSQueue **sq;
35
NvmeCQueue **cq;
36
diff --git a/include/block/nvme.h b/include/block/nvme.h
37
index XXXXXXX..XXXXXXX 100644
38
--- a/include/block/nvme.h
39
+++ b/include/block/nvme.h
40
@@ -XXX,XX +XXX,XX @@ typedef struct NvmeBar {
41
uint64_t acq;
42
uint32_t cmbloc;
43
uint32_t cmbsz;
44
+ uint8_t padding[3520]; /* not used by QEMU */
45
+ uint32_t pmrcap;
46
+ uint32_t pmrctl;
47
+ uint32_t pmrsts;
48
+ uint32_t pmrebs;
49
+ uint32_t pmrswtp;
50
+ uint32_t pmrmsc;
51
} NvmeBar;
52
53
enum NvmeCapShift {
54
@@ -XXX,XX +XXX,XX @@ enum NvmeCapShift {
55
CAP_CSS_SHIFT = 37,
56
CAP_MPSMIN_SHIFT = 48,
57
CAP_MPSMAX_SHIFT = 52,
58
+ CAP_PMR_SHIFT = 56,
59
};
60
61
enum NvmeCapMask {
62
@@ -XXX,XX +XXX,XX @@ enum NvmeCapMask {
63
CAP_CSS_MASK = 0xff,
64
CAP_MPSMIN_MASK = 0xf,
65
CAP_MPSMAX_MASK = 0xf,
66
+ CAP_PMR_MASK = 0x1,
67
};
68
69
#define NVME_CAP_MQES(cap) (((cap) >> CAP_MQES_SHIFT) & CAP_MQES_MASK)
70
@@ -XXX,XX +XXX,XX @@ enum NvmeCapMask {
71
<< CAP_MPSMIN_SHIFT)
72
#define NVME_CAP_SET_MPSMAX(cap, val) (cap |= (uint64_t)(val & CAP_MPSMAX_MASK)\
73
<< CAP_MPSMAX_SHIFT)
74
+#define NVME_CAP_SET_PMRS(cap, val) (cap |= (uint64_t)(val & CAP_PMR_MASK)\
75
+ << CAP_PMR_SHIFT)
76
77
enum NvmeCcShift {
78
CC_EN_SHIFT = 0,
79
@@ -XXX,XX +XXX,XX @@ enum NvmeCmbszMask {
80
#define NVME_CMBSZ_GETSIZE(cmbsz) \
81
(NVME_CMBSZ_SZ(cmbsz) * (1 << (12 + 4 * NVME_CMBSZ_SZU(cmbsz))))
82
83
+enum NvmePmrcapShift {
84
+ PMRCAP_RDS_SHIFT = 3,
85
+ PMRCAP_WDS_SHIFT = 4,
86
+ PMRCAP_BIR_SHIFT = 5,
87
+ PMRCAP_PMRTU_SHIFT = 8,
88
+ PMRCAP_PMRWBM_SHIFT = 10,
89
+ PMRCAP_PMRTO_SHIFT = 16,
90
+ PMRCAP_CMSS_SHIFT = 24,
91
+};
92
+
93
+enum NvmePmrcapMask {
94
+ PMRCAP_RDS_MASK = 0x1,
95
+ PMRCAP_WDS_MASK = 0x1,
96
+ PMRCAP_BIR_MASK = 0x7,
97
+ PMRCAP_PMRTU_MASK = 0x3,
98
+ PMRCAP_PMRWBM_MASK = 0xf,
99
+ PMRCAP_PMRTO_MASK = 0xff,
100
+ PMRCAP_CMSS_MASK = 0x1,
101
+};
102
+
103
+#define NVME_PMRCAP_RDS(pmrcap) \
104
+ ((pmrcap >> PMRCAP_RDS_SHIFT) & PMRCAP_RDS_MASK)
105
+#define NVME_PMRCAP_WDS(pmrcap) \
106
+ ((pmrcap >> PMRCAP_WDS_SHIFT) & PMRCAP_WDS_MASK)
107
+#define NVME_PMRCAP_BIR(pmrcap) \
108
+ ((pmrcap >> PMRCAP_BIR_SHIFT) & PMRCAP_BIR_MASK)
109
+#define NVME_PMRCAP_PMRTU(pmrcap) \
110
+ ((pmrcap >> PMRCAP_PMRTU_SHIFT) & PMRCAP_PMRTU_MASK)
111
+#define NVME_PMRCAP_PMRWBM(pmrcap) \
112
+ ((pmrcap >> PMRCAP_PMRWBM_SHIFT) & PMRCAP_PMRWBM_MASK)
113
+#define NVME_PMRCAP_PMRTO(pmrcap) \
114
+ ((pmrcap >> PMRCAP_PMRTO_SHIFT) & PMRCAP_PMRTO_MASK)
115
+#define NVME_PMRCAP_CMSS(pmrcap) \
116
+ ((pmrcap >> PMRCAP_CMSS_SHIFT) & PMRCAP_CMSS_MASK)
117
+
118
+#define NVME_PMRCAP_SET_RDS(pmrcap, val) \
119
+ (pmrcap |= (uint64_t)(val & PMRCAP_RDS_MASK) << PMRCAP_RDS_SHIFT)
120
+#define NVME_PMRCAP_SET_WDS(pmrcap, val) \
121
+ (pmrcap |= (uint64_t)(val & PMRCAP_WDS_MASK) << PMRCAP_WDS_SHIFT)
122
+#define NVME_PMRCAP_SET_BIR(pmrcap, val) \
123
+ (pmrcap |= (uint64_t)(val & PMRCAP_BIR_MASK) << PMRCAP_BIR_SHIFT)
124
+#define NVME_PMRCAP_SET_PMRTU(pmrcap, val) \
125
+ (pmrcap |= (uint64_t)(val & PMRCAP_PMRTU_MASK) << PMRCAP_PMRTU_SHIFT)
126
+#define NVME_PMRCAP_SET_PMRWBM(pmrcap, val) \
127
+ (pmrcap |= (uint64_t)(val & PMRCAP_PMRWBM_MASK) << PMRCAP_PMRWBM_SHIFT)
128
+#define NVME_PMRCAP_SET_PMRTO(pmrcap, val) \
129
+ (pmrcap |= (uint64_t)(val & PMRCAP_PMRTO_MASK) << PMRCAP_PMRTO_SHIFT)
130
+#define NVME_PMRCAP_SET_CMSS(pmrcap, val) \
131
+ (pmrcap |= (uint64_t)(val & PMRCAP_CMSS_MASK) << PMRCAP_CMSS_SHIFT)
132
+
133
+enum NvmePmrctlShift {
134
+ PMRCTL_EN_SHIFT = 0,
135
+};
136
+
137
+enum NvmePmrctlMask {
138
+ PMRCTL_EN_MASK = 0x1,
139
+};
140
+
141
+#define NVME_PMRCTL_EN(pmrctl) ((pmrctl >> PMRCTL_EN_SHIFT) & PMRCTL_EN_MASK)
142
+
143
+#define NVME_PMRCTL_SET_EN(pmrctl, val) \
144
+ (pmrctl |= (uint64_t)(val & PMRCTL_EN_MASK) << PMRCTL_EN_SHIFT)
145
+
146
+enum NvmePmrstsShift {
147
+ PMRSTS_ERR_SHIFT = 0,
148
+ PMRSTS_NRDY_SHIFT = 8,
149
+ PMRSTS_HSTS_SHIFT = 9,
150
+ PMRSTS_CBAI_SHIFT = 12,
151
+};
152
+
153
+enum NvmePmrstsMask {
154
+ PMRSTS_ERR_MASK = 0xff,
155
+ PMRSTS_NRDY_MASK = 0x1,
156
+ PMRSTS_HSTS_MASK = 0x7,
157
+ PMRSTS_CBAI_MASK = 0x1,
158
+};
159
+
160
+#define NVME_PMRSTS_ERR(pmrsts) \
161
+ ((pmrsts >> PMRSTS_ERR_SHIFT) & PMRSTS_ERR_MASK)
162
+#define NVME_PMRSTS_NRDY(pmrsts) \
163
+ ((pmrsts >> PMRSTS_NRDY_SHIFT) & PMRSTS_NRDY_MASK)
164
+#define NVME_PMRSTS_HSTS(pmrsts) \
165
+ ((pmrsts >> PMRSTS_HSTS_SHIFT) & PMRSTS_HSTS_MASK)
166
+#define NVME_PMRSTS_CBAI(pmrsts) \
167
+ ((pmrsts >> PMRSTS_CBAI_SHIFT) & PMRSTS_CBAI_MASK)
168
+
169
+#define NVME_PMRSTS_SET_ERR(pmrsts, val) \
170
+ (pmrsts |= (uint64_t)(val & PMRSTS_ERR_MASK) << PMRSTS_ERR_SHIFT)
171
+#define NVME_PMRSTS_SET_NRDY(pmrsts, val) \
172
+ (pmrsts |= (uint64_t)(val & PMRSTS_NRDY_MASK) << PMRSTS_NRDY_SHIFT)
173
+#define NVME_PMRSTS_SET_HSTS(pmrsts, val) \
174
+ (pmrsts |= (uint64_t)(val & PMRSTS_HSTS_MASK) << PMRSTS_HSTS_SHIFT)
175
+#define NVME_PMRSTS_SET_CBAI(pmrsts, val) \
176
+ (pmrsts |= (uint64_t)(val & PMRSTS_CBAI_MASK) << PMRSTS_CBAI_SHIFT)
177
+
178
+enum NvmePmrebsShift {
179
+ PMREBS_PMRSZU_SHIFT = 0,
180
+ PMREBS_RBB_SHIFT = 4,
181
+ PMREBS_PMRWBZ_SHIFT = 8,
182
+};
183
+
184
+enum NvmePmrebsMask {
185
+ PMREBS_PMRSZU_MASK = 0xf,
186
+ PMREBS_RBB_MASK = 0x1,
187
+ PMREBS_PMRWBZ_MASK = 0xffffff,
188
+};
189
+
190
+#define NVME_PMREBS_PMRSZU(pmrebs) \
191
+ ((pmrebs >> PMREBS_PMRSZU_SHIFT) & PMREBS_PMRSZU_MASK)
192
+#define NVME_PMREBS_RBB(pmrebs) \
193
+ ((pmrebs >> PMREBS_RBB_SHIFT) & PMREBS_RBB_MASK)
194
+#define NVME_PMREBS_PMRWBZ(pmrebs) \
195
+ ((pmrebs >> PMREBS_PMRWBZ_SHIFT) & PMREBS_PMRWBZ_MASK)
196
+
197
+#define NVME_PMREBS_SET_PMRSZU(pmrebs, val) \
198
+ (pmrebs |= (uint64_t)(val & PMREBS_PMRSZU_MASK) << PMREBS_PMRSZU_SHIFT)
199
+#define NVME_PMREBS_SET_RBB(pmrebs, val) \
200
+ (pmrebs |= (uint64_t)(val & PMREBS_RBB_MASK) << PMREBS_RBB_SHIFT)
201
+#define NVME_PMREBS_SET_PMRWBZ(pmrebs, val) \
202
+ (pmrebs |= (uint64_t)(val & PMREBS_PMRWBZ_MASK) << PMREBS_PMRWBZ_SHIFT)
203
+
204
+enum NvmePmrswtpShift {
205
+ PMRSWTP_PMRSWTU_SHIFT = 0,
206
+ PMRSWTP_PMRSWTV_SHIFT = 8,
207
+};
208
+
209
+enum NvmePmrswtpMask {
210
+ PMRSWTP_PMRSWTU_MASK = 0xf,
211
+ PMRSWTP_PMRSWTV_MASK = 0xffffff,
212
+};
213
+
214
+#define NVME_PMRSWTP_PMRSWTU(pmrswtp) \
215
+ ((pmrswtp >> PMRSWTP_PMRSWTU_SHIFT) & PMRSWTP_PMRSWTU_MASK)
216
+#define NVME_PMRSWTP_PMRSWTV(pmrswtp) \
217
+ ((pmrswtp >> PMRSWTP_PMRSWTV_SHIFT) & PMRSWTP_PMRSWTV_MASK)
218
+
219
+#define NVME_PMRSWTP_SET_PMRSWTU(pmrswtp, val) \
220
+ (pmrswtp |= (uint64_t)(val & PMRSWTP_PMRSWTU_MASK) << PMRSWTP_PMRSWTU_SHIFT)
221
+#define NVME_PMRSWTP_SET_PMRSWTV(pmrswtp, val) \
222
+ (pmrswtp |= (uint64_t)(val & PMRSWTP_PMRSWTV_MASK) << PMRSWTP_PMRSWTV_SHIFT)
223
+
224
+enum NvmePmrmscShift {
225
+ PMRMSC_CMSE_SHIFT = 1,
226
+ PMRMSC_CBA_SHIFT = 12,
227
+};
228
+
229
+enum NvmePmrmscMask {
230
+ PMRMSC_CMSE_MASK = 0x1,
231
+ PMRMSC_CBA_MASK = 0xfffffffffffff,
232
+};
233
+
234
+#define NVME_PMRMSC_CMSE(pmrmsc) \
235
+ ((pmrmsc >> PMRMSC_CMSE_SHIFT) & PMRMSC_CMSE_MASK)
236
+#define NVME_PMRMSC_CBA(pmrmsc) \
237
+ ((pmrmsc >> PMRMSC_CBA_SHIFT) & PMRMSC_CBA_MASK)
238
+
239
+#define NVME_PMRMSC_SET_CMSE(pmrmsc, val) \
240
+ (pmrmsc |= (uint64_t)(val & PMRMSC_CMSE_MASK) << PMRMSC_CMSE_SHIFT)
241
+#define NVME_PMRMSC_SET_CBA(pmrmsc, val) \
242
+ (pmrmsc |= (uint64_t)(val & PMRMSC_CBA_MASK) << PMRMSC_CBA_SHIFT)
243
+
244
typedef struct NvmeCmd {
245
uint8_t opcode;
246
uint8_t fuse;
247
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
248
index XXXXXXX..XXXXXXX 100644
249
--- a/hw/block/nvme.c
250
+++ b/hw/block/nvme.c
251
@@ -XXX,XX +XXX,XX @@
252
* -drive file=<file>,if=none,id=<drive_id>
253
* -device nvme,drive=<drive_id>,serial=<serial>,id=<id[optional]>, \
254
* cmb_size_mb=<cmb_size_mb[optional]>, \
255
+ * [pmrdev=<mem_backend_file_id>,] \
256
* num_queues=<N[optional]>
257
*
258
* Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at
259
* offset 0 in BAR2 and supports only WDS, RDS and SQS for now.
260
+ *
261
+ * cmb_size_mb= and pmrdev= options are mutually exclusive due to limitation
262
+ * in available BAR's. cmb_size_mb= will take precedence over pmrdev= when
263
+ * both provided.
264
+ * Enabling pmr emulation can be achieved by pointing to memory-backend-file.
265
+ * For example:
266
+ * -object memory-backend-file,id=<mem_id>,share=on,mem-path=<file_path>, \
267
+ * size=<size> .... -device nvme,...,pmrdev=<mem_id>
268
*/
269
270
#include "qemu/osdep.h"
271
@@ -XXX,XX +XXX,XX @@
272
#include "sysemu/sysemu.h"
273
#include "qapi/error.h"
274
#include "qapi/visitor.h"
275
+#include "sysemu/hostmem.h"
276
#include "sysemu/block-backend.h"
277
+#include "exec/ram_addr.h"
278
279
#include "qemu/log.h"
280
#include "qemu/module.h"
281
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
282
NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
283
"invalid write to read only CMBSZ, ignored");
284
return;
285
+ case 0xE00: /* PMRCAP */
286
+ NVME_GUEST_ERR(nvme_ub_mmiowr_pmrcap_readonly,
287
+ "invalid write to PMRCAP register, ignored");
288
+ return;
289
+ case 0xE04: /* TODO PMRCTL */
290
+ break;
291
+ case 0xE08: /* PMRSTS */
292
+ NVME_GUEST_ERR(nvme_ub_mmiowr_pmrsts_readonly,
293
+ "invalid write to PMRSTS register, ignored");
294
+ return;
295
+ case 0xE0C: /* PMREBS */
296
+ NVME_GUEST_ERR(nvme_ub_mmiowr_pmrebs_readonly,
297
+ "invalid write to PMREBS register, ignored");
298
+ return;
299
+ case 0xE10: /* PMRSWTP */
300
+ NVME_GUEST_ERR(nvme_ub_mmiowr_pmrswtp_readonly,
301
+ "invalid write to PMRSWTP register, ignored");
302
+ return;
303
+ case 0xE14: /* TODO PMRMSC */
304
+ break;
305
default:
306
NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
307
"invalid MMIO write,"
308
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
309
}
310
311
if (addr < sizeof(n->bar)) {
312
+ /*
313
+ * When PMRWBM bit 1 is set then read from
314
+ * from PMRSTS should ensure prior writes
315
+ * made it to persistent media
316
+ */
317
+ if (addr == 0xE08 &&
318
+ (NVME_PMRCAP_PMRWBM(n->bar.pmrcap) & 0x02)) {
319
+ qemu_ram_writeback(n->pmrdev->mr.ram_block,
320
+ 0, n->pmrdev->size);
321
+ }
322
memcpy(&val, ptr + addr, size);
323
} else {
324
NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
325
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
326
error_setg(errp, "serial property not set");
327
return;
328
}
329
+
330
+ if (!n->cmb_size_mb && n->pmrdev) {
331
+ if (host_memory_backend_is_mapped(n->pmrdev)) {
332
+ char *path = object_get_canonical_path_component(OBJECT(n->pmrdev));
333
+ error_setg(errp, "can't use already busy memdev: %s", path);
334
+ g_free(path);
335
+ return;
336
+ }
337
+
338
+ if (!is_power_of_2(n->pmrdev->size)) {
339
+ error_setg(errp, "pmr backend size needs to be power of 2 in size");
340
+ return;
341
+ }
342
+
343
+ host_memory_backend_set_mapped(n->pmrdev, true);
344
+ }
345
+
346
blkconf_blocksizes(&n->conf);
347
if (!blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
348
false, errp)) {
349
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
350
PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 |
351
PCI_BASE_ADDRESS_MEM_PREFETCH, &n->ctrl_mem);
352
353
+ } else if (n->pmrdev) {
354
+ /* Controller Capabilities register */
355
+ NVME_CAP_SET_PMRS(n->bar.cap, 1);
356
+
357
+ /* PMR Capabities register */
358
+ n->bar.pmrcap = 0;
359
+ NVME_PMRCAP_SET_RDS(n->bar.pmrcap, 0);
360
+ NVME_PMRCAP_SET_WDS(n->bar.pmrcap, 0);
361
+ NVME_PMRCAP_SET_BIR(n->bar.pmrcap, 2);
362
+ NVME_PMRCAP_SET_PMRTU(n->bar.pmrcap, 0);
363
+ /* Turn on bit 1 support */
364
+ NVME_PMRCAP_SET_PMRWBM(n->bar.pmrcap, 0x02);
365
+ NVME_PMRCAP_SET_PMRTO(n->bar.pmrcap, 0);
366
+ NVME_PMRCAP_SET_CMSS(n->bar.pmrcap, 0);
367
+
368
+ /* PMR Control register */
369
+ n->bar.pmrctl = 0;
370
+ NVME_PMRCTL_SET_EN(n->bar.pmrctl, 0);
371
+
372
+ /* PMR Status register */
373
+ n->bar.pmrsts = 0;
374
+ NVME_PMRSTS_SET_ERR(n->bar.pmrsts, 0);
375
+ NVME_PMRSTS_SET_NRDY(n->bar.pmrsts, 0);
376
+ NVME_PMRSTS_SET_HSTS(n->bar.pmrsts, 0);
377
+ NVME_PMRSTS_SET_CBAI(n->bar.pmrsts, 0);
378
+
379
+ /* PMR Elasticity Buffer Size register */
380
+ n->bar.pmrebs = 0;
381
+ NVME_PMREBS_SET_PMRSZU(n->bar.pmrebs, 0);
382
+ NVME_PMREBS_SET_RBB(n->bar.pmrebs, 0);
383
+ NVME_PMREBS_SET_PMRWBZ(n->bar.pmrebs, 0);
384
+
385
+ /* PMR Sustained Write Throughput register */
386
+ n->bar.pmrswtp = 0;
387
+ NVME_PMRSWTP_SET_PMRSWTU(n->bar.pmrswtp, 0);
388
+ NVME_PMRSWTP_SET_PMRSWTV(n->bar.pmrswtp, 0);
389
+
390
+ /* PMR Memory Space Control register */
391
+ n->bar.pmrmsc = 0;
392
+ NVME_PMRMSC_SET_CMSE(n->bar.pmrmsc, 0);
393
+ NVME_PMRMSC_SET_CBA(n->bar.pmrmsc, 0);
394
+
395
+ pci_register_bar(pci_dev, NVME_PMRCAP_BIR(n->bar.pmrcap),
396
+ PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 |
397
+ PCI_BASE_ADDRESS_MEM_PREFETCH, &n->pmrdev->mr);
398
}
399
400
for (i = 0; i < n->num_namespaces; i++) {
401
@@ -XXX,XX +XXX,XX @@ static void nvme_exit(PCIDevice *pci_dev)
402
if (n->cmb_size_mb) {
403
g_free(n->cmbuf);
404
}
405
+
406
+ if (n->pmrdev) {
407
+ host_memory_backend_set_mapped(n->pmrdev, false);
408
+ }
409
msix_uninit_exclusive_bar(pci_dev);
25
}
410
}
26
411
27
-static void write_target_close(BlockDriverState *bs) {
412
static Property nvme_props[] = {
28
- BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
413
DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf),
29
- bdrv_unref_child(s->bs, s->qcow);
414
+ DEFINE_PROP_LINK("pmrdev", NvmeCtrl, pmrdev, TYPE_MEMORY_BACKEND,
30
- g_free(s->qcow_filename);
415
+ HostMemoryBackend *),
31
-}
416
DEFINE_PROP_STRING("serial", NvmeCtrl, serial),
32
-
417
DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, cmb_size_mb, 0),
33
static BlockDriver vvfat_write_target = {
418
DEFINE_PROP_UINT32("num_queues", NvmeCtrl, num_queues, 64),
34
.format_name = "vvfat_write_target",
419
diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
35
.instance_size = sizeof(void*),
420
index XXXXXXX..XXXXXXX 100644
36
.bdrv_co_pwritev = write_target_commit,
421
--- a/hw/block/Makefile.objs
37
- .bdrv_close = write_target_close,
422
+++ b/hw/block/Makefile.objs
38
};
423
@@ -XXX,XX +XXX,XX @@ common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
39
424
common-obj-$(CONFIG_XEN) += xen-block.o
40
static void vvfat_qcow_options(int *child_flags, QDict *child_options,
425
common-obj-$(CONFIG_ECC) += ecc.o
426
common-obj-$(CONFIG_ONENAND) += onenand.o
427
-common-obj-$(CONFIG_NVME_PCI) += nvme.o
428
common-obj-$(CONFIG_SWIM) += swim.o
429
430
common-obj-$(CONFIG_SH4) += tc58128.o
431
432
obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o
433
obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o
434
+obj-$(CONFIG_NVME_PCI) += nvme.o
435
436
obj-y += dataplane/
437
diff --git a/hw/block/trace-events b/hw/block/trace-events
438
index XXXXXXX..XXXXXXX 100644
439
--- a/hw/block/trace-events
440
+++ b/hw/block/trace-events
441
@@ -XXX,XX +XXX,XX @@ nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CA
442
nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
443
nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
444
nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
445
+nvme_ub_mmiowr_pmrcap_readonly(void) "invalid write to read only PMRCAP, ignored"
446
+nvme_ub_mmiowr_pmrsts_readonly(void) "invalid write to read only PMRSTS, ignored"
447
+nvme_ub_mmiowr_pmrebs_readonly(void) "invalid write to read only PMREBS, ignored"
448
+nvme_ub_mmiowr_pmrswtp_readonly(void) "invalid write to read only PMRSWTP, ignored"
449
nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
450
nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
451
nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
41
--
452
--
42
2.20.1
453
2.25.3
43
454
44
455
diff view generated by jsdifflib
Deleted patch
1
In the case that update_refcount() frees a refcount block, it evicts it
2
from the metadata cache. Before doing so, however, it returns the
3
currently used refcount block to the cache because it might be the same.
4
Returning the refcount block early means that we need to reset
5
old_table_index so that we reload the refcount block in the next
6
iteration if it is actually still in use.
7
1
8
Fixes: f71c08ea8e60f035485a512fd2af8908567592f0
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200211094900.17315-2-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block/qcow2-refcount.c | 1 +
14
1 file changed, 1 insertion(+)
15
16
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2-refcount.c
19
+++ b/block/qcow2-refcount.c
20
@@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
21
offset);
22
if (table != NULL) {
23
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
24
+ old_table_index = -1;
25
qcow2_cache_discard(s->refcount_block_cache, table);
26
}
27
28
--
29
2.20.1
30
31
diff view generated by jsdifflib
Deleted patch
1
The bytes_written variable is only ever written to, it serves no
2
purpose. This has actually been the case since the commit job was first
3
introduced in commit 747ff602636.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200214200812.28180-3-kwolf@redhat.com>
7
Reviewed-by: Ján Tomko <jtomko@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block/commit.c | 2 --
11
1 file changed, 2 deletions(-)
12
13
diff --git a/block/commit.c b/block/commit.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/commit.c
16
+++ b/block/commit.c
17
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
18
int ret = 0;
19
int64_t n = 0; /* bytes */
20
void *buf = NULL;
21
- int bytes_written = 0;
22
int64_t len, base_len;
23
24
ret = len = blk_getlength(s->top);
25
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
26
trace_commit_one_iteration(s, offset, n, ret);
27
if (copy) {
28
ret = commit_populate(s->top, s->base, offset, n, buf);
29
- bytes_written += n;
30
}
31
if (ret < 0) {
32
BlockErrorAction action =
33
--
34
2.20.1
35
36
diff view generated by jsdifflib
Deleted patch
1
The block_job_error_action() error call in the commit job gives the
2
on_err and is_read arguments in the wrong order. Fix this.
3
1
4
(Of course, hard-coded is_read = false is wrong, too, but that's a
5
separate problem for a separate patch.)
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20200214200812.28180-4-kwolf@redhat.com>
9
Reviewed-by: Ján Tomko <jtomko@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/commit.c | 2 +-
13
1 file changed, 1 insertion(+), 1 deletion(-)
14
15
diff --git a/block/commit.c b/block/commit.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/commit.c
18
+++ b/block/commit.c
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
20
}
21
if (ret < 0) {
22
BlockErrorAction action =
23
- block_job_error_action(&s->common, false, s->on_error, -ret);
24
+ block_job_error_action(&s->common, s->on_error, false, -ret);
25
if (action == BLOCK_ERROR_ACTION_REPORT) {
26
goto out;
27
} else {
28
--
29
2.20.1
30
31
diff view generated by jsdifflib
Deleted patch
1
commit_populate() is a very short function and only called in a single
2
place. Its return value doesn't tell us whether an error happened while
3
reading or writing, which would be necessary for sending the right data
4
in the BLOCK_JOB_ERROR QMP event.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20200214200812.28180-5-kwolf@redhat.com>
8
Reviewed-by: Ján Tomko <jtomko@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block/commit.c | 28 ++++++----------------------
12
1 file changed, 6 insertions(+), 22 deletions(-)
13
14
diff --git a/block/commit.c b/block/commit.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/commit.c
17
+++ b/block/commit.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct CommitBlockJob {
19
char *backing_file_str;
20
} CommitBlockJob;
21
22
-static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
23
- int64_t offset, uint64_t bytes,
24
- void *buf)
25
-{
26
- int ret = 0;
27
-
28
- assert(bytes < SIZE_MAX);
29
-
30
- ret = blk_co_pread(bs, offset, bytes, buf, 0);
31
- if (ret < 0) {
32
- return ret;
33
- }
34
-
35
- ret = blk_co_pwrite(base, offset, bytes, buf, 0);
36
- if (ret < 0) {
37
- return ret;
38
- }
39
-
40
- return 0;
41
-}
42
-
43
static int commit_prepare(Job *job)
44
{
45
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
46
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
47
copy = (ret == 1);
48
trace_commit_one_iteration(s, offset, n, ret);
49
if (copy) {
50
- ret = commit_populate(s->top, s->base, offset, n, buf);
51
+ assert(n < SIZE_MAX);
52
+
53
+ ret = blk_co_pread(s->top, offset, n, buf, 0);
54
+ if (ret >= 0) {
55
+ ret = blk_co_pwrite(s->base, offset, n, buf, 0);
56
+ }
57
}
58
if (ret < 0) {
59
BlockErrorAction action =
60
--
61
2.20.1
62
63
diff view generated by jsdifflib
Deleted patch
1
block_job_error_action() needs to know if reading from the top node or
2
writing to the base node failed so that it can set the right 'operation'
3
in the BLOCK_JOB_ERROR QMP event.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200214200812.28180-6-kwolf@redhat.com>
7
Reviewed-by: Ján Tomko <jtomko@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block/commit.c | 7 ++++++-
11
1 file changed, 6 insertions(+), 1 deletion(-)
12
13
diff --git a/block/commit.c b/block/commit.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/commit.c
16
+++ b/block/commit.c
17
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
18
19
for (offset = 0; offset < len; offset += n) {
20
bool copy;
21
+ bool error_in_source = true;
22
23
/* Note that even when no rate limit is applied we need to yield
24
* with no pending I/O here so that bdrv_drain_all() returns.
25
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
26
ret = blk_co_pread(s->top, offset, n, buf, 0);
27
if (ret >= 0) {
28
ret = blk_co_pwrite(s->base, offset, n, buf, 0);
29
+ if (ret < 0) {
30
+ error_in_source = false;
31
+ }
32
}
33
}
34
if (ret < 0) {
35
BlockErrorAction action =
36
- block_job_error_action(&s->common, s->on_error, false, -ret);
37
+ block_job_error_action(&s->common, s->on_error,
38
+ error_in_source, -ret);
39
if (action == BLOCK_ERROR_ACTION_REPORT) {
40
goto out;
41
} else {
42
--
43
2.20.1
44
45
diff view generated by jsdifflib
Deleted patch
1
Now that the error handling in the common block job is fixed, we can
2
expose the on-error option in QMP instead of hard-coding it as 'report'
3
in qmp_block_commit().
4
1
5
This fulfills the promise that the old comment in that function made,
6
even if a bit later than expected: "This will be part of the QMP
7
command, if/when the BlockdevOnError change for blkmirror makes it in".
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200214200812.28180-7-kwolf@redhat.com>
11
Reviewed-by: Ján Tomko <jtomko@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
qapi/block-core.json | 4 ++++
15
blockdev.c | 8 ++++----
16
2 files changed, 8 insertions(+), 4 deletions(-)
17
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
19
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
21
+++ b/qapi/block-core.json
22
@@ -XXX,XX +XXX,XX @@
23
#
24
# @speed: the maximum speed, in bytes per second
25
#
26
+# @on-error: the action to take on an error. 'ignore' means that the request
27
+# should be retried. (default: report; Since: 5.0)
28
+#
29
# @filter-node-name: the node name that should be assigned to the
30
# filter driver that the commit job inserts into the graph
31
# above @top. If this option is not given, a node name is
32
@@ -XXX,XX +XXX,XX @@
33
'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str',
34
'*base': 'str', '*top-node': 'str', '*top': 'str',
35
'*backing-file': 'str', '*speed': 'int',
36
+ '*on-error': 'BlockdevOnError',
37
'*filter-node-name': 'str',
38
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
39
40
diff --git a/blockdev.c b/blockdev.c
41
index XXXXXXX..XXXXXXX 100644
42
--- a/blockdev.c
43
+++ b/blockdev.c
44
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
45
bool has_top, const char *top,
46
bool has_backing_file, const char *backing_file,
47
bool has_speed, int64_t speed,
48
+ bool has_on_error, BlockdevOnError on_error,
49
bool has_filter_node_name, const char *filter_node_name,
50
bool has_auto_finalize, bool auto_finalize,
51
bool has_auto_dismiss, bool auto_dismiss,
52
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
53
BlockDriverState *base_bs, *top_bs;
54
AioContext *aio_context;
55
Error *local_err = NULL;
56
- /* This will be part of the QMP command, if/when the
57
- * BlockdevOnError change for blkmirror makes it in
58
- */
59
- BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
60
int job_flags = JOB_DEFAULT;
61
62
if (!has_speed) {
63
speed = 0;
64
}
65
+ if (!has_on_error) {
66
+ on_error = BLOCKDEV_ON_ERROR_REPORT;
67
+ }
68
if (!has_filter_node_name) {
69
filter_node_name = NULL;
70
}
71
--
72
2.20.1
73
74
diff view generated by jsdifflib
Deleted patch
1
This tests both read failure (from the top node) and write failure (to
2
the base node) for on-error=report/stop/ignore.
3
1
4
As block-commit actually starts two different types of block jobs
5
(mirror.c for committing the active later, commit.c for intermediate
6
layers), all tests are run for both cases.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20200214200812.28180-8-kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
tests/qemu-iotests/040 | 283 +++++++++++++++++++++++++++++++++++++
13
tests/qemu-iotests/040.out | 4 +-
14
2 files changed, 285 insertions(+), 2 deletions(-)
15
16
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
17
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/qemu-iotests/040
19
+++ b/tests/qemu-iotests/040
20
@@ -XXX,XX +XXX,XX @@ class TestReopenOverlay(ImageCommitTestCase):
21
def test_reopen_overlay(self):
22
self.run_commit_test(self.img1, self.img0)
23
24
+class TestErrorHandling(iotests.QMPTestCase):
25
+ image_len = 2 * 1024 * 1024
26
+
27
+ def setUp(self):
28
+ iotests.create_image(backing_img, self.image_len)
29
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
30
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
31
+
32
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img)
33
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img)
34
+
35
+ self.vm = iotests.VM()
36
+ self.vm.launch()
37
+
38
+ self.blkdebug_file = iotests.file_path("blkdebug.conf")
39
+
40
+ def tearDown(self):
41
+ self.vm.shutdown()
42
+ os.remove(test_img)
43
+ os.remove(mid_img)
44
+ os.remove(backing_img)
45
+
46
+ def blockdev_add(self, **kwargs):
47
+ result = self.vm.qmp('blockdev-add', **kwargs)
48
+ self.assert_qmp(result, 'return', {})
49
+
50
+ def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None):
51
+ self.blockdev_add(node_name='base-file', driver='file',
52
+ filename=backing_img)
53
+ self.blockdev_add(node_name='mid-file', driver='file',
54
+ filename=mid_img)
55
+ self.blockdev_add(node_name='top-file', driver='file',
56
+ filename=test_img)
57
+
58
+ if base_debug:
59
+ self.blockdev_add(node_name='base-dbg', driver='blkdebug',
60
+ image='base-file', inject_error=base_debug)
61
+ if mid_debug:
62
+ self.blockdev_add(node_name='mid-dbg', driver='blkdebug',
63
+ image='mid-file', inject_error=mid_debug)
64
+ if top_debug:
65
+ self.blockdev_add(node_name='top-dbg', driver='blkdebug',
66
+ image='top-file', inject_error=top_debug)
67
+
68
+ self.blockdev_add(node_name='base-fmt', driver='raw',
69
+ file=('base-dbg' if base_debug else 'base-file'))
70
+ self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt,
71
+ file=('mid-dbg' if mid_debug else 'mid-file'),
72
+ backing='base-fmt')
73
+ self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt,
74
+ file=('top-dbg' if top_debug else 'top-file'),
75
+ backing='mid-fmt')
76
+
77
+ def run_job(self, expected_events, error_pauses_job=False):
78
+ match_device = {'data': {'device': 'job0'}}
79
+ events = [
80
+ ('BLOCK_JOB_COMPLETED', match_device),
81
+ ('BLOCK_JOB_CANCELLED', match_device),
82
+ ('BLOCK_JOB_ERROR', match_device),
83
+ ('BLOCK_JOB_READY', match_device),
84
+ ]
85
+
86
+ completed = False
87
+ log = []
88
+ while not completed:
89
+ ev = self.vm.events_wait(events, timeout=5.0)
90
+ if ev['event'] == 'BLOCK_JOB_COMPLETED':
91
+ completed = True
92
+ elif ev['event'] == 'BLOCK_JOB_ERROR':
93
+ if error_pauses_job:
94
+ result = self.vm.qmp('block-job-resume', device='job0')
95
+ self.assert_qmp(result, 'return', {})
96
+ elif ev['event'] == 'BLOCK_JOB_READY':
97
+ result = self.vm.qmp('block-job-complete', device='job0')
98
+ self.assert_qmp(result, 'return', {})
99
+ else:
100
+ self.fail("Unexpected event: %s" % ev)
101
+ log.append(iotests.filter_qmp_event(ev))
102
+
103
+ self.maxDiff = None
104
+ self.assertEqual(expected_events, log)
105
+
106
+ def event_error(self, op, action):
107
+ return {
108
+ 'event': 'BLOCK_JOB_ERROR',
109
+ 'data': {'action': action, 'device': 'job0', 'operation': op},
110
+ 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}
111
+ }
112
+
113
+ def event_ready(self):
114
+ return {
115
+ 'event': 'BLOCK_JOB_READY',
116
+ 'data': {'device': 'job0',
117
+ 'len': 524288,
118
+ 'offset': 524288,
119
+ 'speed': 0,
120
+ 'type': 'commit'},
121
+ 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'},
122
+ }
123
+
124
+ def event_completed(self, errmsg=None, active=True):
125
+ max_len = 524288 if active else self.image_len
126
+ data = {
127
+ 'device': 'job0',
128
+ 'len': max_len,
129
+ 'offset': 0 if errmsg else max_len,
130
+ 'speed': 0,
131
+ 'type': 'commit'
132
+ }
133
+ if errmsg:
134
+ data['error'] = errmsg
135
+
136
+ return {
137
+ 'event': 'BLOCK_JOB_COMPLETED',
138
+ 'data': data,
139
+ 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'},
140
+ }
141
+
142
+ def blkdebug_event(self, event, is_raw=False):
143
+ if event:
144
+ return [{
145
+ 'event': event,
146
+ 'sector': 512 if is_raw else 1024,
147
+ 'once': True,
148
+ }]
149
+ return None
150
+
151
+ def prepare_and_start_job(self, on_error, active=True,
152
+ top_event=None, mid_event=None, base_event=None):
153
+
154
+ top_debug = self.blkdebug_event(top_event)
155
+ mid_debug = self.blkdebug_event(mid_event)
156
+ base_debug = self.blkdebug_event(base_event, True)
157
+
158
+ self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug,
159
+ base_debug=base_debug)
160
+
161
+ result = self.vm.qmp('block-commit', job_id='job0', device='top-fmt',
162
+ top_node='top-fmt' if active else 'mid-fmt',
163
+ base_node='mid-fmt' if active else 'base-fmt',
164
+ on_error=on_error)
165
+ self.assert_qmp(result, 'return', {})
166
+
167
+ def testActiveReadErrorReport(self):
168
+ self.prepare_and_start_job('report', top_event='read_aio')
169
+ self.run_job([
170
+ self.event_error('read', 'report'),
171
+ self.event_completed('Input/output error')
172
+ ])
173
+
174
+ self.vm.shutdown()
175
+ self.assertFalse(iotests.compare_images(test_img, mid_img),
176
+ 'target image matches source after error')
177
+
178
+ def testActiveReadErrorStop(self):
179
+ self.prepare_and_start_job('stop', top_event='read_aio')
180
+ self.run_job([
181
+ self.event_error('read', 'stop'),
182
+ self.event_ready(),
183
+ self.event_completed()
184
+ ], error_pauses_job=True)
185
+
186
+ self.vm.shutdown()
187
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
188
+ 'target image does not match source after commit')
189
+
190
+ def testActiveReadErrorIgnore(self):
191
+ self.prepare_and_start_job('ignore', top_event='read_aio')
192
+ self.run_job([
193
+ self.event_error('read', 'ignore'),
194
+ self.event_ready(),
195
+ self.event_completed()
196
+ ])
197
+
198
+ # For commit, 'ignore' actually means retry, so this will succeed
199
+ self.vm.shutdown()
200
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
201
+ 'target image does not match source after commit')
202
+
203
+ def testActiveWriteErrorReport(self):
204
+ self.prepare_and_start_job('report', mid_event='write_aio')
205
+ self.run_job([
206
+ self.event_error('write', 'report'),
207
+ self.event_completed('Input/output error')
208
+ ])
209
+
210
+ self.vm.shutdown()
211
+ self.assertFalse(iotests.compare_images(test_img, mid_img),
212
+ 'target image matches source after error')
213
+
214
+ def testActiveWriteErrorStop(self):
215
+ self.prepare_and_start_job('stop', mid_event='write_aio')
216
+ self.run_job([
217
+ self.event_error('write', 'stop'),
218
+ self.event_ready(),
219
+ self.event_completed()
220
+ ], error_pauses_job=True)
221
+
222
+ self.vm.shutdown()
223
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
224
+ 'target image does not match source after commit')
225
+
226
+ def testActiveWriteErrorIgnore(self):
227
+ self.prepare_and_start_job('ignore', mid_event='write_aio')
228
+ self.run_job([
229
+ self.event_error('write', 'ignore'),
230
+ self.event_ready(),
231
+ self.event_completed()
232
+ ])
233
+
234
+ # For commit, 'ignore' actually means retry, so this will succeed
235
+ self.vm.shutdown()
236
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
237
+ 'target image does not match source after commit')
238
+
239
+ def testIntermediateReadErrorReport(self):
240
+ self.prepare_and_start_job('report', active=False, mid_event='read_aio')
241
+ self.run_job([
242
+ self.event_error('read', 'report'),
243
+ self.event_completed('Input/output error', active=False)
244
+ ])
245
+
246
+ self.vm.shutdown()
247
+ self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
248
+ 'target image matches source after error')
249
+
250
+ def testIntermediateReadErrorStop(self):
251
+ self.prepare_and_start_job('stop', active=False, mid_event='read_aio')
252
+ self.run_job([
253
+ self.event_error('read', 'stop'),
254
+ self.event_completed(active=False)
255
+ ], error_pauses_job=True)
256
+
257
+ self.vm.shutdown()
258
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
259
+ 'target image does not match source after commit')
260
+
261
+ def testIntermediateReadErrorIgnore(self):
262
+ self.prepare_and_start_job('ignore', active=False, mid_event='read_aio')
263
+ self.run_job([
264
+ self.event_error('read', 'ignore'),
265
+ self.event_completed(active=False)
266
+ ])
267
+
268
+ # For commit, 'ignore' actually means retry, so this will succeed
269
+ self.vm.shutdown()
270
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
271
+ 'target image does not match source after commit')
272
+
273
+ def testIntermediateWriteErrorReport(self):
274
+ self.prepare_and_start_job('report', active=False, base_event='write_aio')
275
+ self.run_job([
276
+ self.event_error('write', 'report'),
277
+ self.event_completed('Input/output error', active=False)
278
+ ])
279
+
280
+ self.vm.shutdown()
281
+ self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
282
+ 'target image matches source after error')
283
+
284
+ def testIntermediateWriteErrorStop(self):
285
+ self.prepare_and_start_job('stop', active=False, base_event='write_aio')
286
+ self.run_job([
287
+ self.event_error('write', 'stop'),
288
+ self.event_completed(active=False)
289
+ ], error_pauses_job=True)
290
+
291
+ self.vm.shutdown()
292
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
293
+ 'target image does not match source after commit')
294
+
295
+ def testIntermediateWriteErrorIgnore(self):
296
+ self.prepare_and_start_job('ignore', active=False, base_event='write_aio')
297
+ self.run_job([
298
+ self.event_error('write', 'ignore'),
299
+ self.event_completed(active=False)
300
+ ])
301
+
302
+ # For commit, 'ignore' actually means retry, so this will succeed
303
+ self.vm.shutdown()
304
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
305
+ 'target image does not match source after commit')
306
+
307
if __name__ == '__main__':
308
iotests.main(supported_fmts=['qcow2', 'qed'],
309
supported_protocols=['file'])
310
diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out
311
index XXXXXXX..XXXXXXX 100644
312
--- a/tests/qemu-iotests/040.out
313
+++ b/tests/qemu-iotests/040.out
314
@@ -XXX,XX +XXX,XX @@
315
-...............................................
316
+...........................................................
317
----------------------------------------------------------------------
318
-Ran 47 tests
319
+Ran 59 tests
320
321
OK
322
--
323
2.20.1
324
325
diff view generated by jsdifflib
Deleted patch
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
2
1
3
Fixes: 132ada80c4a
4
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
5
Message-Id: <20200218094402.26625-4-philmd@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
block.c | 4 ++--
9
1 file changed, 2 insertions(+), 2 deletions(-)
10
11
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block.c
14
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
16
if (bdrv_get_aio_context(child_bs) != ctx) {
17
ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
18
if (ret < 0 && child_role->can_set_aio_ctx) {
19
- GSList *ignore = g_slist_prepend(NULL, child);;
20
+ GSList *ignore = g_slist_prepend(NULL, child);
21
ctx = bdrv_get_aio_context(child_bs);
22
if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
23
error_free(local_err);
24
ret = 0;
25
g_slist_free(ignore);
26
- ignore = g_slist_prepend(NULL, child);;
27
+ ignore = g_slist_prepend(NULL, child);
28
child_role->set_aio_ctx(child, ctx, &ignore);
29
}
30
g_slist_free(ignore);
31
--
32
2.20.1
33
34
diff view generated by jsdifflib
Deleted patch
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
2
1
3
Fixes: 6663a0a3376
4
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
5
Message-Id: <20200218094402.26625-5-philmd@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
block/io_uring.c | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
10
11
diff --git a/block/io_uring.c b/block/io_uring.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/io_uring.c
14
+++ b/block/io_uring.c
15
@@ -XXX,XX +XXX,XX @@ static void luring_process_completions(LuringState *s)
16
ret = 0;
17
}
18
} else {
19
- ret = -ENOSPC;;
20
+ ret = -ENOSPC;
21
}
22
}
23
end:
24
--
25
2.20.1
26
27
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
There is no good reason why we would allow external snapshots only on
4
the first non-filter node in a chain. Parent BDSs should not care
5
whether their child is replaced by a snapshot. (If they do care, they
6
should announce that via freezing the chain, which is checked in
7
bdrv_append() through bdrv_set_backing_hd().)
8
9
Before we had bdrv_is_first_non_filter() here (since 212a5a8f095), there
10
was a special function bdrv_check_ext_snapshot() that allowed snapshots
11
by default, but block drivers could override this. Only blkverify did
12
so, however.
13
14
It is not clear to me why blkverify would do so; maybe just so that the
15
testee block driver would not be replaced. The introducing commit
16
f6186f49e2c does not explain why. Maybe because 08b24cfe376 would have
17
been the correct solution? (Which adds a .supports_backing check.)
18
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
21
Message-Id: <20200218103454.296704-2-mreitz@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
24
blockdev.c | 5 -----
25
1 file changed, 5 deletions(-)
26
27
diff --git a/blockdev.c b/blockdev.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/blockdev.c
30
+++ b/blockdev.c
31
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
32
}
33
}
34
35
- if (!bdrv_is_first_non_filter(state->old_bs)) {
36
- error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
37
- goto out;
38
- }
39
-
40
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
41
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
42
const char *format = s->has_format ? s->format : "qcow2";
43
--
44
2.20.1
45
46
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
Block nodes that do not allow resizing should not share BLK_PERM_RESIZE.
4
It does not matter whether they are the first non-filter in their chain
5
or not.
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20200218103454.296704-3-mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
blockdev.c | 5 -----
13
1 file changed, 5 deletions(-)
14
15
diff --git a/blockdev.c b/blockdev.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/blockdev.c
18
+++ b/blockdev.c
19
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
20
aio_context = bdrv_get_aio_context(bs);
21
aio_context_acquire(aio_context);
22
23
- if (!bdrv_is_first_non_filter(bs)) {
24
- error_setg(errp, QERR_FEATURE_DISABLED, "resize");
25
- goto out;
26
- }
27
-
28
if (size < 0) {
29
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
30
goto out;
31
--
32
2.20.1
33
34
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
Using -drive with default options means that a virtio-blk drive will be
4
created that has write access to the to-be quorum children. Quorum
5
should have exclusive write access to them, so we should use -blockdev
6
instead.
7
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Message-Id: <20200218103454.296704-5-mreitz@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
tests/qemu-iotests/041 | 5 ++++-
14
1 file changed, 4 insertions(+), 1 deletion(-)
15
16
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
17
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/qemu-iotests/041
19
+++ b/tests/qemu-iotests/041
20
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
21
# Assign a node name to each quorum image in order to manipulate
22
# them
23
opts = "node-name=img%i" % self.IMAGES.index(i)
24
- self.vm = self.vm.add_drive(i, opts)
25
+ opts += ',driver=%s' % iotests.imgfmt
26
+ opts += ',file.driver=file'
27
+ opts += ',file.filename=%s' % i
28
+ self.vm = self.vm.add_blockdev(opts)
29
30
self.vm.launch()
31
32
--
33
2.20.1
34
35
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
The QMP handler qmp_object_add() and the implementation of --object in
2
qemu-storage-daemon can share most of the code. Currently,
3
qemu-storage-daemon calls qmp_object_add(), but this is not correct
4
because different visitors need to be used.
2
5
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
As a first step towards a fix, make qmp_object_add() a wrapper around a
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
new function user_creatable_add_dict() that can get an additional
5
Message-Id: <20200218103454.296704-8-mreitz@redhat.com>
8
parameter. The handling of "props" is only required for compatibility
9
and not required for the qemu-storage-daemon command line, so it stays
10
in qmp_object_add().
11
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
13
---
8
block/blkverify.c | 15 +++++++++++++++
14
include/qom/object_interfaces.h | 12 ++++++++++++
9
1 file changed, 15 insertions(+)
15
qom/object_interfaces.c | 27 +++++++++++++++++++++++++++
16
qom/qom-qmp-cmds.c | 24 +-----------------------
17
3 files changed, 40 insertions(+), 23 deletions(-)
10
18
11
diff --git a/block/blkverify.c b/block/blkverify.c
19
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
12
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
13
--- a/block/blkverify.c
21
--- a/include/qom/object_interfaces.h
14
+++ b/block/blkverify.c
22
+++ b/include/qom/object_interfaces.h
15
@@ -XXX,XX +XXX,XX @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
23
@@ -XXX,XX +XXX,XX @@ Object *user_creatable_add_type(const char *type, const char *id,
16
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
24
const QDict *qdict,
25
Visitor *v, Error **errp);
26
27
+/**
28
+ * user_creatable_add_dict:
29
+ * @qdict: the object definition
30
+ * @errp: if an error occurs, a pointer to an area to store the error
31
+ *
32
+ * Create an instance of the user creatable object that is defined by
33
+ * @qdict. The object type is taken from the QDict key 'qom-type', its
34
+ * ID from the key 'id'. The remaining entries in @qdict are used to
35
+ * initialize the object properties.
36
+ */
37
+void user_creatable_add_dict(QDict *qdict, Error **errp);
38
+
39
/**
40
* user_creatable_add_opts:
41
* @opts: the object definition
42
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/qom/object_interfaces.c
45
+++ b/qom/object_interfaces.c
46
@@ -XXX,XX +XXX,XX @@
47
#include "qapi/qmp/qerror.h"
48
#include "qapi/qmp/qjson.h"
49
#include "qapi/qmp/qstring.h"
50
+#include "qapi/qobject-input-visitor.h"
51
#include "qom/object_interfaces.h"
52
#include "qemu/help_option.h"
53
#include "qemu/module.h"
54
@@ -XXX,XX +XXX,XX @@ out:
55
return obj;
17
}
56
}
18
57
19
+static bool blkverify_recurse_can_replace(BlockDriverState *bs,
58
+void user_creatable_add_dict(QDict *qdict, Error **errp)
20
+ BlockDriverState *to_replace)
21
+{
59
+{
22
+ BDRVBlkverifyState *s = bs->opaque;
60
+ Visitor *v;
61
+ Object *obj;
62
+ g_autofree char *type = NULL;
63
+ g_autofree char *id = NULL;
23
+
64
+
24
+ /*
65
+ type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
25
+ * blkverify quits the whole qemu process if there is a mismatch
66
+ if (!type) {
26
+ * between bs->file->bs and s->test_file->bs. Therefore, we know
67
+ error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
27
+ * know that both must match bs and we can recurse down to either.
68
+ return;
28
+ */
69
+ }
29
+ return bdrv_recurse_can_replace(bs->file->bs, to_replace) ||
70
+ qdict_del(qdict, "qom-type");
30
+ bdrv_recurse_can_replace(s->test_file->bs, to_replace);
71
+
72
+ id = g_strdup(qdict_get_try_str(qdict, "id"));
73
+ if (!id) {
74
+ error_setg(errp, QERR_MISSING_PARAMETER, "id");
75
+ return;
76
+ }
77
+ qdict_del(qdict, "id");
78
+
79
+ v = qobject_input_visitor_new(QOBJECT(qdict));
80
+ obj = user_creatable_add_type(type, id, qdict, v, errp);
81
+ visit_free(v);
82
+ object_unref(obj);
31
+}
83
+}
32
+
84
33
static void blkverify_refresh_filename(BlockDriverState *bs)
85
Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
34
{
86
{
35
BDRVBlkverifyState *s = bs->opaque;
87
diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
36
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = {
88
index XXXXXXX..XXXXXXX 100644
37
89
--- a/qom/qom-qmp-cmds.c
38
.is_filter = true,
90
+++ b/qom/qom-qmp-cmds.c
39
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
91
@@ -XXX,XX +XXX,XX @@
40
+ .bdrv_recurse_can_replace = blkverify_recurse_can_replace,
92
#include "qapi/qapi-commands-qom.h"
41
};
93
#include "qapi/qmp/qdict.h"
42
94
#include "qapi/qmp/qerror.h"
43
static void bdrv_blkverify_init(void)
95
-#include "qapi/qobject-input-visitor.h"
96
#include "qemu/cutils.h"
97
#include "qom/object_interfaces.h"
98
#include "qom/qom-qobject.h"
99
@@ -XXX,XX +XXX,XX @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
100
{
101
QObject *props;
102
QDict *pdict;
103
- Visitor *v;
104
- Object *obj;
105
- g_autofree char *type = NULL;
106
- g_autofree char *id = NULL;
107
-
108
- type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
109
- if (!type) {
110
- error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
111
- return;
112
- }
113
- qdict_del(qdict, "qom-type");
114
-
115
- id = g_strdup(qdict_get_try_str(qdict, "id"));
116
- if (!id) {
117
- error_setg(errp, QERR_MISSING_PARAMETER, "id");
118
- return;
119
- }
120
- qdict_del(qdict, "id");
121
122
props = qdict_get(qdict, "props");
123
if (props) {
124
@@ -XXX,XX +XXX,XX @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
125
qobject_unref(pdict);
126
}
127
128
- v = qobject_input_visitor_new(QOBJECT(qdict));
129
- obj = user_creatable_add_type(type, id, qdict, v, errp);
130
- visit_free(v);
131
- object_unref(obj);
132
+ user_creatable_add_dict(qdict, errp);
133
}
134
135
void qmp_object_del(const char *id, Error **errp)
44
--
136
--
45
2.20.1
137
2.25.3
46
138
47
139
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
After processing the option string with the keyval parser, we get a
2
QDict that contains only strings. This QDict must be fed to a keyval
3
visitor which converts the strings into the right data types.
2
4
3
Quorum cannot share WRITE or RESIZE on its children. Presumably, it
5
qmp_object_add(), however, uses the normal QObject input visitor, which
4
only does so because as a filter, it seemed intuitively correct to point
6
expects a QDict where all properties already have the QType that matches
5
its .bdrv_child_perm to bdrv_filter_default_perm().
7
the data type required by the QOM object type.
6
8
7
However, it is not really a filter, and bdrv_filter_default_perm() does
9
Change the --object implementation in qemu-storage-daemon so that it
8
not work for it, so we have to provide a custom .bdrv_child_perm
10
doesn't call qmp_object_add(), but calls user_creatable_add_dict()
9
implementation.
11
directly instead and pass it a new keyval boolean that decides which
12
visitor must be used.
10
13
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
Reported-by: Coiby Xu <coiby.xu@gmail.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Message-Id: <20200218103454.296704-6-mreitz@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
---
16
block/quorum.c | 19 ++++++++++++++++++-
17
include/qom/object_interfaces.h | 6 +++++-
17
1 file changed, 18 insertions(+), 1 deletion(-)
18
qemu-storage-daemon.c | 4 +---
19
qom/object_interfaces.c | 8 ++++++--
20
qom/qom-qmp-cmds.c | 2 +-
21
4 files changed, 13 insertions(+), 7 deletions(-)
18
22
19
diff --git a/block/quorum.c b/block/quorum.c
23
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
20
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
21
--- a/block/quorum.c
25
--- a/include/qom/object_interfaces.h
22
+++ b/block/quorum.c
26
+++ b/include/qom/object_interfaces.h
23
@@ -XXX,XX +XXX,XX @@ static char *quorum_dirname(BlockDriverState *bs, Error **errp)
27
@@ -XXX,XX +XXX,XX @@ Object *user_creatable_add_type(const char *type, const char *id,
24
return NULL;
28
/**
29
* user_creatable_add_dict:
30
* @qdict: the object definition
31
+ * @keyval: if true, use a keyval visitor for processing @qdict (i.e.
32
+ * assume that all @qdict values are strings); otherwise, use
33
+ * the normal QObject visitor (i.e. assume all @qdict values
34
+ * have the QType expected by the QOM object type)
35
* @errp: if an error occurs, a pointer to an area to store the error
36
*
37
* Create an instance of the user creatable object that is defined by
38
@@ -XXX,XX +XXX,XX @@ Object *user_creatable_add_type(const char *type, const char *id,
39
* ID from the key 'id'. The remaining entries in @qdict are used to
40
* initialize the object properties.
41
*/
42
-void user_creatable_add_dict(QDict *qdict, Error **errp);
43
+void user_creatable_add_dict(QDict *qdict, bool keyval, Error **errp);
44
45
/**
46
* user_creatable_add_opts:
47
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
48
index XXXXXXX..XXXXXXX 100644
49
--- a/qemu-storage-daemon.c
50
+++ b/qemu-storage-daemon.c
51
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
52
QemuOpts *opts;
53
const char *type;
54
QDict *args;
55
- QObject *ret_data = NULL;
56
57
/* FIXME The keyval parser rejects 'help' arguments, so we must
58
* unconditionall try QemuOpts first. */
59
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
60
qemu_opts_del(opts);
61
62
args = keyval_parse(optarg, "qom-type", &error_fatal);
63
- qmp_object_add(args, &ret_data, &error_fatal);
64
+ user_creatable_add_dict(args, true, &error_fatal);
65
qobject_unref(args);
66
- qobject_unref(ret_data);
67
break;
68
}
69
default:
70
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/qom/object_interfaces.c
73
+++ b/qom/object_interfaces.c
74
@@ -XXX,XX +XXX,XX @@ out:
75
return obj;
25
}
76
}
26
77
27
+static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c,
78
-void user_creatable_add_dict(QDict *qdict, Error **errp)
28
+ const BdrvChildRole *role,
79
+void user_creatable_add_dict(QDict *qdict, bool keyval, Error **errp)
29
+ BlockReopenQueue *reopen_queue,
80
{
30
+ uint64_t perm, uint64_t shared,
81
Visitor *v;
31
+ uint64_t *nperm, uint64_t *nshared)
82
Object *obj;
32
+{
83
@@ -XXX,XX +XXX,XX @@ void user_creatable_add_dict(QDict *qdict, Error **errp)
33
+ *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
84
}
34
+
85
qdict_del(qdict, "id");
35
+ /*
86
36
+ * We cannot share RESIZE or WRITE, as this would make the
87
- v = qobject_input_visitor_new(QOBJECT(qdict));
37
+ * children differ from each other.
88
+ if (keyval) {
38
+ */
89
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
39
+ *nshared = (shared & (BLK_PERM_CONSISTENT_READ |
90
+ } else {
40
+ BLK_PERM_WRITE_UNCHANGED))
91
+ v = qobject_input_visitor_new(QOBJECT(qdict));
41
+ | DEFAULT_PERM_UNCHANGED;
92
+ }
42
+}
93
obj = user_creatable_add_type(type, id, qdict, v, errp);
43
+
94
visit_free(v);
44
static const char *const quorum_strong_runtime_opts[] = {
95
object_unref(obj);
45
QUORUM_OPT_VOTE_THRESHOLD,
96
diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
46
QUORUM_OPT_BLKVERIFY,
97
index XXXXXXX..XXXXXXX 100644
47
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
98
--- a/qom/qom-qmp-cmds.c
48
.bdrv_add_child = quorum_add_child,
99
+++ b/qom/qom-qmp-cmds.c
49
.bdrv_del_child = quorum_del_child,
100
@@ -XXX,XX +XXX,XX @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
50
101
qobject_unref(pdict);
51
- .bdrv_child_perm = bdrv_filter_default_perms,
102
}
52
+ .bdrv_child_perm = quorum_child_perm,
103
53
104
- user_creatable_add_dict(qdict, errp);
54
.is_filter = true,
105
+ user_creatable_add_dict(qdict, false, errp);
55
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
106
}
107
108
void qmp_object_del(const char *id, Error **errp)
56
--
109
--
57
2.20.1
110
2.25.3
58
111
59
112
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
There is no guarantee that we can still replace the node we want to
4
replace at the end of the mirror job. Double-check by calling
5
bdrv_recurse_can_replace().
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20200218103454.296704-12-mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/mirror.c | 14 +++++++++++++-
13
1 file changed, 13 insertions(+), 1 deletion(-)
14
15
diff --git a/block/mirror.c b/block/mirror.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/mirror.c
18
+++ b/block/mirror.c
19
@@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job)
20
* drain potential other users of the BDS before changing the graph. */
21
assert(s->in_drain);
22
bdrv_drained_begin(target_bs);
23
- bdrv_replace_node(to_replace, target_bs, &local_err);
24
+ /*
25
+ * Cannot use check_to_replace_node() here, because that would
26
+ * check for an op blocker on @to_replace, and we have our own
27
+ * there.
28
+ */
29
+ if (bdrv_recurse_can_replace(src, to_replace)) {
30
+ bdrv_replace_node(to_replace, target_bs, &local_err);
31
+ } else {
32
+ error_setg(&local_err, "Can no longer replace '%s' by '%s', "
33
+ "because it can no longer be guaranteed that doing so "
34
+ "would not lead to an abrupt change of visible data",
35
+ to_replace->node_name, target_bs->node_name);
36
+ }
37
bdrv_drained_end(target_bs);
38
if (local_err) {
39
error_report_err(local_err);
40
--
41
2.20.1
42
43
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
Quorum is not a filter, for example because it cannot guarantee which of
4
its children will serve the next request. Thus, any of its children may
5
differ from the data visible to quorum's parents.
6
7
We have other filters with multiple children, but they differ in this
8
aspect:
9
10
- blkverify quits the whole qemu process if its children differ. As
11
such, we can always skip it when we want to skip it (as a filter node)
12
by going to any of its children. Both have the same data.
13
14
- replication generally serves requests from bs->file, so this is its
15
only actually filtered child.
16
17
- Block job filters currently only have one child, but they will
18
probably get more children in the future. Still, they will always
19
have only one actually filtered child.
20
21
Having "filters" as a dedicated node category only makes sense if you
22
can skip them by going to a one fixed child that always shows the same
23
data as the filter node. Quorum cannot fulfill this, so it is not a
24
filter.
25
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
28
Message-Id: <20200218103454.296704-13-mreitz@redhat.com>
29
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
30
---
31
block/quorum.c | 1 -
32
1 file changed, 1 deletion(-)
33
34
diff --git a/block/quorum.c b/block/quorum.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/block/quorum.c
37
+++ b/block/quorum.c
38
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
39
40
.bdrv_child_perm = quorum_child_perm,
41
42
- .is_filter = true,
43
.bdrv_recurse_can_replace = quorum_recurse_can_replace,
44
45
.strong_runtime_opts = quorum_strong_runtime_opts,
46
--
47
2.20.1
48
49
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
This way, we get to see errors during the completion phase.
4
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Message-Id: <20200218103454.296704-14-mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
tests/qemu-iotests/155 | 7 +------
11
1 file changed, 1 insertion(+), 6 deletions(-)
12
13
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
14
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/155
16
+++ b/tests/qemu-iotests/155
17
@@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass):
18
19
self.assert_qmp(result, 'return', {})
20
21
- self.vm.event_wait('BLOCK_JOB_READY')
22
-
23
- result = self.vm.qmp('block-job-complete', device='mirror-job')
24
- self.assert_qmp(result, 'return', {})
25
-
26
- self.vm.event_wait('BLOCK_JOB_COMPLETED')
27
+ self.complete_and_wait('mirror-job')
28
29
def testFull(self):
30
self.runMirror('full')
31
--
32
2.20.1
33
34
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
All tearDowns in 041 shutdown the VM. Thus, test cases do not need to
4
do it themselves (unless they need the VM to be down for some
5
post-operation check).
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20200218103454.296704-16-mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
tests/qemu-iotests/041 | 11 -----------
13
1 file changed, 11 deletions(-)
14
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
16
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/041
18
+++ b/tests/qemu-iotests/041
19
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
20
self.cancel_and_wait(force=True)
21
result = self.vm.qmp('query-block')
22
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
23
- self.vm.shutdown()
24
25
def test_cancel_after_ready(self):
26
self.assert_no_active_block_jobs()
27
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
28
self.assert_qmp(result, 'return[0]/node-name', 'top')
29
self.assert_qmp(result, 'return[0]/backing/node-name', 'base')
30
31
- self.vm.shutdown()
32
-
33
def test_medium_not_found(self):
34
if iotests.qemu_default_machine != 'pc':
35
return
36
@@ -XXX,XX +XXX,XX @@ new_state = "1"
37
self.assert_qmp(event, 'data/id', 'drive0')
38
39
self.assert_no_active_block_jobs()
40
- self.vm.shutdown()
41
42
def test_ignore_read(self):
43
self.assert_no_active_block_jobs()
44
@@ -XXX,XX +XXX,XX @@ new_state = "1"
45
result = self.vm.qmp('query-block-jobs')
46
self.assert_qmp(result, 'return[0]/paused', False)
47
self.complete_and_wait()
48
- self.vm.shutdown()
49
50
def test_large_cluster(self):
51
self.assert_no_active_block_jobs()
52
@@ -XXX,XX +XXX,XX @@ new_state = "1"
53
54
self.complete_and_wait(wait_ready=False)
55
self.assert_no_active_block_jobs()
56
- self.vm.shutdown()
57
58
class TestWriteErrors(iotests.QMPTestCase):
59
image_len = 2 * 1024 * 1024 # MB
60
@@ -XXX,XX +XXX,XX @@ new_state = "1"
61
completed = True
62
63
self.assert_no_active_block_jobs()
64
- self.vm.shutdown()
65
66
def test_ignore_write(self):
67
self.assert_no_active_block_jobs()
68
@@ -XXX,XX +XXX,XX @@ new_state = "1"
69
result = self.vm.qmp('query-block-jobs')
70
self.assert_qmp(result, 'return[0]/paused', False)
71
self.complete_and_wait()
72
- self.vm.shutdown()
73
74
def test_stop_write(self):
75
self.assert_no_active_block_jobs()
76
@@ -XXX,XX +XXX,XX @@ new_state = "1"
77
78
self.complete_and_wait(wait_ready=False)
79
self.assert_no_active_block_jobs()
80
- self.vm.shutdown()
81
82
class TestSetSpeed(iotests.QMPTestCase):
83
image_len = 80 * 1024 * 1024 # MB
84
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
85
# here we check that the last registered quorum file has not been
86
# swapped out and unref
87
self.assert_has_block_node(None, quorum_img3)
88
- self.vm.shutdown()
89
90
def test_cancel_after_ready(self):
91
self.assert_no_active_block_jobs()
92
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
93
self.assert_has_block_node("repair0", quorum_repair_img)
94
# TODO: a better test requiring some QEMU infrastructure will be added
95
# to check that this file is really driven by quorum
96
- self.vm.shutdown()
97
98
# Test mirroring with a source that does not have any parents (not even a
99
# BlockBackend)
100
--
101
2.20.1
102
103
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Message-Id: <20200218103454.296704-17-mreitz@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
tests/qemu-iotests/041 | 6 ++----
9
1 file changed, 2 insertions(+), 4 deletions(-)
10
11
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/041
14
+++ b/tests/qemu-iotests/041
15
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
16
17
self.complete_and_wait(drive="job0")
18
self.assert_has_block_node("repair0", quorum_repair_img)
19
- # TODO: a better test requiring some QEMU infrastructure will be added
20
- # to check that this file is really driven by quorum
21
+ self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
22
self.vm.shutdown()
23
self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
24
'target image does not match source after mirroring')
25
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
26
27
self.complete_and_wait('job0')
28
self.assert_has_block_node("repair0", quorum_repair_img)
29
- # TODO: a better test requiring some QEMU infrastructure will be added
30
- # to check that this file is really driven by quorum
31
+ self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
32
33
# Test mirroring with a source that does not have any parents (not even a
34
# BlockBackend)
35
--
36
2.20.1
37
38
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
041's TestRepairQuorum has its own image_len, no need to refer to
4
TestSingleDrive. (This patch allows commenting out TestSingleDrive to
5
speed up 041 during test testing.)
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20200218103454.296704-18-mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
tests/qemu-iotests/041 | 2 +-
13
1 file changed, 1 insertion(+), 1 deletion(-)
14
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
16
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/041
18
+++ b/tests/qemu-iotests/041
19
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
20
# Add each individual quorum images
21
for i in self.IMAGES:
22
qemu_img('create', '-f', iotests.imgfmt, i,
23
- str(TestSingleDrive.image_len))
24
+ str(self.image_len))
25
# Assign a node name to each quorum image in order to manipulate
26
# them
27
opts = "node-name=img%i" % self.IMAGES.index(i)
28
--
29
2.20.1
30
31
diff view generated by jsdifflib
Deleted patch
1
From: Max Reitz <mreitz@redhat.com>
2
1
3
Add two tests to see that you cannot replace a Quorum child with the
4
mirror job while the child is in use by a different parent.
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20200218103454.296704-19-mreitz@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
tests/qemu-iotests/041 | 70 +++++++++++++++++++++++++++++++++++++-
12
tests/qemu-iotests/041.out | 4 +--
13
2 files changed, 71 insertions(+), 3 deletions(-)
14
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
16
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/041
18
+++ b/tests/qemu-iotests/041
19
@@ -XXX,XX +XXX,XX @@
20
21
import time
22
import os
23
+import re
24
import iotests
25
from iotests import qemu_img, qemu_io
26
27
@@ -XXX,XX +XXX,XX @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
28
quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
29
quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
30
31
+nbd_sock_path = os.path.join(iotests.test_dir, 'nbd.sock')
32
+
33
class TestSingleDrive(iotests.QMPTestCase):
34
image_len = 1 * 1024 * 1024 # MB
35
qmp_cmd = 'drive-mirror'
36
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
37
38
def tearDown(self):
39
self.vm.shutdown()
40
- for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file ]:
41
+ for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file,
42
+ nbd_sock_path ]:
43
# Do a try/except because the test may have deleted some images
44
try:
45
os.remove(i)
46
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
47
self.assert_has_block_node("repair0", quorum_repair_img)
48
self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
49
50
+ def test_with_other_parent(self):
51
+ """
52
+ Check that we cannot replace a Quorum child when it has other
53
+ parents.
54
+ """
55
+ result = self.vm.qmp('nbd-server-start',
56
+ addr={
57
+ 'type': 'unix',
58
+ 'data': {'path': nbd_sock_path}
59
+ })
60
+ self.assert_qmp(result, 'return', {})
61
+
62
+ result = self.vm.qmp('nbd-server-add', device='img1')
63
+ self.assert_qmp(result, 'return', {})
64
+
65
+ result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
66
+ sync='full', node_name='repair0', replaces='img1',
67
+ target=quorum_repair_img, format=iotests.imgfmt)
68
+ self.assert_qmp(result, 'error/desc',
69
+ "Cannot replace 'img1' by a node mirrored from "
70
+ "'quorum0', because it cannot be guaranteed that doing "
71
+ "so would not lead to an abrupt change of visible data")
72
+
73
+ def test_with_other_parents_after_mirror_start(self):
74
+ """
75
+ The same as test_with_other_parent(), but add the NBD server
76
+ only when the mirror job is already running.
77
+ """
78
+ result = self.vm.qmp('nbd-server-start',
79
+ addr={
80
+ 'type': 'unix',
81
+ 'data': {'path': nbd_sock_path}
82
+ })
83
+ self.assert_qmp(result, 'return', {})
84
+
85
+ result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
86
+ sync='full', node_name='repair0', replaces='img1',
87
+ target=quorum_repair_img, format=iotests.imgfmt)
88
+ self.assert_qmp(result, 'return', {})
89
+
90
+ result = self.vm.qmp('nbd-server-add', device='img1')
91
+ self.assert_qmp(result, 'return', {})
92
+
93
+ # The full error message goes to stderr, we will check it later
94
+ self.complete_and_wait('mirror',
95
+ completion_error='Operation not permitted')
96
+
97
+ # Should not have been replaced
98
+ self.vm.assert_block_path('quorum0', '/children.1', 'img1')
99
+
100
+ # Check the full error message now
101
+ self.vm.shutdown()
102
+ log = self.vm.get_log()
103
+ log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
104
+ log = re.sub(r'^Formatting.*\n', '', log)
105
+ log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
106
+ log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
107
+
108
+ self.assertEqual(log,
109
+ "Can no longer replace 'img1' by 'repair0', because " +
110
+ "it can no longer be guaranteed that doing so would " +
111
+ "not lead to an abrupt change of visible data")
112
+
113
+
114
# Test mirroring with a source that does not have any parents (not even a
115
# BlockBackend)
116
class TestOrphanedSource(iotests.QMPTestCase):
117
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
118
index XXXXXXX..XXXXXXX 100644
119
--- a/tests/qemu-iotests/041.out
120
+++ b/tests/qemu-iotests/041.out
121
@@ -XXX,XX +XXX,XX @@
122
-...........................................................................................
123
+.............................................................................................
124
----------------------------------------------------------------------
125
-Ran 91 tests
126
+Ran 93 tests
127
128
OK
129
--
130
2.20.1
131
132
diff view generated by jsdifflib