1 | The following changes since commit fb68096da3d35e64c88cd610c1fa42766c58e92a: | 1 | The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8: |
---|---|---|---|
2 | 2 | ||
3 | Revert "tests: use memfd in vhost-user-test" (2018-02-13 09:51:52 +0000) | 3 | Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging (2017-12-22 00:11:36 +0000) |
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 0a4dc980e6c935e9be745ce3ee1a4c71629ecd00: | 9 | for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8: |
10 | 10 | ||
11 | Merge remote-tracking branch 'mreitz/tags/pull-block-2018-02-13' into queue-block (2018-02-13 17:01:13 +0100) | 11 | block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches | 14 | Block layer patches |
15 | 15 | ||
16 | ---------------------------------------------------------------- | 16 | ---------------------------------------------------------------- |
17 | Alberto Garcia (40): | 17 | Doug Gale (1): |
18 | qcow2: Use g_try_realloc() in qcow2_expand_zero_clusters() | 18 | nvme: Add tracing |
19 | qcow2: Fix documentation of get_cluster_table() | ||
20 | qcow2: Add table size field to Qcow2Cache | ||
21 | qcow2: Remove BDS parameter from qcow2_cache_get_table_addr() | ||
22 | qcow2: Remove BDS parameter from qcow2_cache_get_table_idx() | ||
23 | qcow2: Remove BDS parameter from qcow2_cache_table_release() | ||
24 | qcow2: Remove BDS parameter from qcow2_cache_entry_mark_dirty() | ||
25 | qcow2: Remove BDS parameter from qcow2_cache_put() | ||
26 | qcow2: Remove BDS parameter from qcow2_cache_destroy() | ||
27 | qcow2: Remove BDS parameter from qcow2_cache_clean_unused() | ||
28 | qcow2: Remove BDS parameter from qcow2_cache_discard() | ||
29 | qcow2: Remove BDS parameter from qcow2_cache_is_table_offset() | ||
30 | qcow2: Add offset_to_l1_index() | ||
31 | qcow2: Add l2_slice_size field to BDRVQcow2State | ||
32 | qcow2: Add offset_to_l2_slice_index() | ||
33 | qcow2: Update l2_load() to support L2 slices | ||
34 | qcow2: Prepare l2_allocate() for adding L2 slice support | ||
35 | qcow2: Update l2_allocate() to support L2 slices | ||
36 | qcow2: Refactor get_cluster_table() | ||
37 | qcow2: Update get_cluster_table() to support L2 slices | ||
38 | qcow2: Update qcow2_get_cluster_offset() to support L2 slices | ||
39 | qcow2: Update qcow2_alloc_cluster_link_l2() to support L2 slices | ||
40 | qcow2: Update handle_copied() to support L2 slices | ||
41 | qcow2: Update handle_alloc() to support L2 slices | ||
42 | qcow2: Update discard_single_l2() to support L2 slices | ||
43 | qcow2: Update zero_single_l2() to support L2 slices | ||
44 | qcow2: Prepare qcow2_update_snapshot_refcount() for adding L2 slice support | ||
45 | qcow2: Update qcow2_update_snapshot_refcount() to support L2 slices | ||
46 | qcow2: Read refcount before L2 table in expand_zero_clusters_in_l1() | ||
47 | qcow2: Prepare expand_zero_clusters_in_l1() for adding L2 slice support | ||
48 | qcow2: Update expand_zero_clusters_in_l1() to support L2 slices | ||
49 | qcow2: Update qcow2_truncate() to support L2 slices | ||
50 | qcow2: Rename l2_table in qcow2_alloc_compressed_cluster_offset() | ||
51 | qcow2: Rename l2_table in count_contiguous_clusters() | ||
52 | qcow2: Rename l2_table in count_contiguous_clusters_unallocated() | ||
53 | qcow2: Rename l2_table in count_cow_clusters() | ||
54 | qcow2: Allow configuring the L2 slice size | ||
55 | iotests: Test valid values of l2-cache-entry-size | ||
56 | iotests: Test downgrading an image using a small L2 slice size | ||
57 | iotests: Add l2-cache-entry-size to iotest 137 | ||
58 | 19 | ||
59 | Daniel P. Berrangé (1): | 20 | Edgar Kaziakhmedov (1): |
60 | qemu-io: fix EOF Ctrl-D handling in qemu-io readline code | 21 | qcow2: get rid of qcow2_backing_read1 routine |
61 | 22 | ||
62 | Fam Zheng (4): | 23 | Fam Zheng (2): |
63 | iotests: Fix CID for VMDK afl image | 24 | block: Open backing image in force share mode for size probe |
64 | qemu-img.texi: Clean up parameter list | 25 | block: Remove unused bdrv_requests_pending |
65 | qemu-img: Document --force-share / -U | ||
66 | docs: Document share-rw property more thoroughly | ||
67 | 26 | ||
68 | Kevin Wolf (1): | 27 | John Snow (1): |
69 | Merge remote-tracking branch 'mreitz/tags/pull-block-2018-02-13' into queue-block | 28 | iotests: fix 197 for vpc |
70 | 29 | ||
71 | Max Reitz (8): | 30 | Kevin Wolf (27): |
72 | iotests: Use virtio-blk in 155 | 31 | block: Formats don't need CONSISTENT_READ with NO_IO |
73 | gluster: Move glfs_close() to create's clean-up | 32 | block: Make bdrv_drain_invoke() recursive |
74 | gluster: Pull truncation from qemu_gluster_create | 33 | block: Call .drain_begin only once in bdrv_drain_all_begin() |
75 | gluster: Query current size in do_truncate() | 34 | test-bdrv-drain: Test BlockDriver callbacks for drain |
76 | gluster: Add preallocated truncation | 35 | block: bdrv_drain_recurse(): Remove unused begin parameter |
77 | sheepdog: Make sd_prealloc() take a BDS | 36 | block: Don't wait for requests in bdrv_drain*_end() |
78 | sheepdog: Pass old and new size to sd_prealloc() | 37 | block: Unify order in drain functions |
79 | sheepdog: Allow fully preallocated truncation | 38 | block: Don't acquire AioContext in hmp_qemu_io() |
39 | block: Document that x-blockdev-change breaks quorum children list | ||
40 | block: Assert drain_all is only called from main AioContext | ||
41 | block: Make bdrv_drain() driver callbacks non-recursive | ||
42 | test-bdrv-drain: Test callback for bdrv_drain | ||
43 | test-bdrv-drain: Test bs->quiesce_counter | ||
44 | blockjob: Pause job on draining any job BDS | ||
45 | test-bdrv-drain: Test drain vs. block jobs | ||
46 | block: Don't block_job_pause_all() in bdrv_drain_all() | ||
47 | block: Nested drain_end must still call callbacks | ||
48 | test-bdrv-drain: Test nested drain sections | ||
49 | block: Don't notify parents in drain call chain | ||
50 | block: Add bdrv_subtree_drained_begin/end() | ||
51 | test-bdrv-drain: Tests for bdrv_subtree_drain | ||
52 | test-bdrv-drain: Test behaviour in coroutine context | ||
53 | test-bdrv-drain: Recursive draining with multiple parents | ||
54 | block: Allow graph changes in subtree drained section | ||
55 | test-bdrv-drain: Test graph changes in drained section | ||
56 | commit: Simplify reopen of base | ||
57 | block: Keep nodes drained between reopen_queue/multiple | ||
80 | 58 | ||
81 | Paolo Bonzini (1): | 59 | Thomas Huth (3): |
82 | block: early check for blockers on drive-mirror | 60 | block: Remove the obsolete -drive boot=on|off parameter |
61 | block: Remove the deprecated -hdachs option | ||
62 | block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter | ||
83 | 63 | ||
84 | Vladimir Sementsov-Ogievskiy (1): | 64 | qapi/block-core.json | 4 + |
85 | block: maintain persistent disabled bitmaps | 65 | block/qcow2.h | 3 - |
66 | include/block/block.h | 15 +- | ||
67 | include/block/block_int.h | 6 +- | ||
68 | block.c | 75 ++++- | ||
69 | block/commit.c | 8 +- | ||
70 | block/io.c | 164 +++++++--- | ||
71 | block/qcow2.c | 51 +-- | ||
72 | block/replication.c | 6 + | ||
73 | blockdev.c | 11 - | ||
74 | blockjob.c | 22 +- | ||
75 | hmp.c | 6 - | ||
76 | hw/block/nvme.c | 349 +++++++++++++++++---- | ||
77 | qemu-io-cmds.c | 3 + | ||
78 | tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++ | ||
79 | vl.c | 86 +----- | ||
80 | hw/block/trace-events | 93 ++++++ | ||
81 | qemu-doc.texi | 29 +- | ||
82 | qemu-options.hx | 19 +- | ||
83 | tests/Makefile.include | 2 + | ||
84 | tests/qemu-iotests/197 | 4 + | ||
85 | tests/qemu-iotests/common.filter | 3 +- | ||
86 | 22 files changed, 1294 insertions(+), 316 deletions(-) | ||
87 | create mode 100644 tests/test-bdrv-drain.c | ||
86 | 88 | ||
87 | qapi/block-core.json | 12 +- | ||
88 | block/qcow2.h | 33 +- | ||
89 | include/block/dirty-bitmap.h | 1 - | ||
90 | block/dirty-bitmap.c | 18 - | ||
91 | block/gluster.c | 116 +++--- | ||
92 | block/qcow2-bitmap.c | 12 +- | ||
93 | block/qcow2-cache.c | 80 ++-- | ||
94 | block/qcow2-cluster.c | 519 +++++++++++++------------ | ||
95 | block/qcow2-refcount.c | 206 +++++----- | ||
96 | block/qcow2.c | 63 ++- | ||
97 | block/sheepdog.c | 56 ++- | ||
98 | blockdev.c | 15 +- | ||
99 | qemu-io.c | 27 +- | ||
100 | docs/qemu-block-drivers.texi | 10 + | ||
101 | qemu-doc.texi | 7 + | ||
102 | qemu-img.texi | 74 ++-- | ||
103 | tests/qemu-iotests/059.out | 2 +- | ||
104 | tests/qemu-iotests/061 | 16 + | ||
105 | tests/qemu-iotests/061.out | 61 +++ | ||
106 | tests/qemu-iotests/103 | 17 + | ||
107 | tests/qemu-iotests/103.out | 3 + | ||
108 | tests/qemu-iotests/137 | 5 + | ||
109 | tests/qemu-iotests/137.out | 2 + | ||
110 | tests/qemu-iotests/155 | 14 +- | ||
111 | tests/qemu-iotests/165 | 2 +- | ||
112 | tests/qemu-iotests/176 | 2 +- | ||
113 | tests/qemu-iotests/sample_images/afl9.vmdk.bz2 | Bin 178 -> 618 bytes | ||
114 | 27 files changed, 816 insertions(+), 557 deletions(-) | ||
115 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently |
---|---|---|---|
2 | in use as a mirror target. It is not enough for image formats, though, | ||
3 | as these still unconditionally request BLK_PERM_CONSISTENT_READ. | ||
2 | 4 | ||
3 | glfs_close() is a classical clean-up operation, as can be seen by the | 5 | As this permission is geared towards whether the guest-visible data is |
4 | fact that it is executed even if the truncation before it failed. | 6 | consistent, and has no impact on whether the metadata is sane, and |
5 | Also, moving it to clean-up makes it more clear that if it fails, we do | 7 | 'qemu-img info' does not read guest-visible data (except for the raw |
6 | not want it to overwrite the current ret value if that signifies an | 8 | format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there |
7 | error already. | 9 | is not going to be any guest I/O performed, regardless of image format. |
8 | 10 | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 12 | --- |
13 | block/gluster.c | 10 ++++++---- | 13 | block.c | 6 +++++- |
14 | 1 file changed, 6 insertions(+), 4 deletions(-) | 14 | 1 file changed, 5 insertions(+), 1 deletion(-) |
15 | 15 | ||
16 | diff --git a/block/gluster.c b/block/gluster.c | 16 | diff --git a/block.c b/block.c |
17 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/gluster.c | 18 | --- a/block.c |
19 | +++ b/block/gluster.c | 19 | +++ b/block.c |
20 | @@ -XXX,XX +XXX,XX @@ static int qemu_gluster_create(const char *filename, | 20 | @@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, |
21 | { | 21 | assert(role == &child_backing || role == &child_file); |
22 | BlockdevOptionsGluster *gconf; | 22 | |
23 | struct glfs *glfs; | 23 | if (!backing) { |
24 | - struct glfs_fd *fd; | 24 | + int flags = bdrv_reopen_get_flags(reopen_queue, bs); |
25 | + struct glfs_fd *fd = NULL; | 25 | + |
26 | int ret = 0; | 26 | /* Apart from the modifications below, the same permissions are |
27 | PreallocMode prealloc; | 27 | * forwarded and left alone as for filters */ |
28 | int64_t total_size = 0; | 28 | bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, |
29 | @@ -XXX,XX +XXX,XX @@ static int qemu_gluster_create(const char *filename, | 29 | @@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, |
30 | break; | 30 | |
31 | } | 31 | /* bs->file always needs to be consistent because of the metadata. We |
32 | 32 | * can never allow other users to resize or write to it. */ | |
33 | - if (glfs_close(fd) != 0) { | 33 | - perm |= BLK_PERM_CONSISTENT_READ; |
34 | - ret = -errno; | 34 | + if (!(flags & BDRV_O_NO_IO)) { |
35 | - } | 35 | + perm |= BLK_PERM_CONSISTENT_READ; |
36 | out: | ||
37 | + if (fd) { | ||
38 | + if (glfs_close(fd) != 0 && ret == 0) { | ||
39 | + ret = -errno; | ||
40 | + } | 36 | + } |
41 | + } | 37 | shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); |
42 | qapi_free_BlockdevOptionsGluster(gconf); | 38 | } else { |
43 | glfs_clear_preopened(glfs); | 39 | /* We want consistent read from backing files if the parent needs it. |
44 | return ret; | ||
45 | -- | 40 | -- |
46 | 2.13.6 | 41 | 2.13.6 |
47 | 42 | ||
48 | 43 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | From: John Snow <jsnow@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | qcow2_get_cluster_offset() checks how many contiguous bytes are | 3 | VPC has some difficulty creating geometries of particular size. |
4 | available at a given offset. The returned number of bytes is limited | 4 | However, we can indeed force it to use a literal one, so let's |
5 | by the amount that can be addressed without having to load more than | 5 | do that for the sake of test 197, which is testing some specific |
6 | one L2 table. | 6 | offsets. |
7 | 7 | ||
8 | Since we'll be loading L2 slices instead of full tables this patch | 8 | Signed-off-by: John Snow <jsnow@redhat.com> |
9 | changes the limit accordingly using the size of the L2 slice for the | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
10 | calculations instead of the full table size. | 10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Reviewed-by: Lukáš Doktor <ldoktor@redhat.com> | ||
13 | --- | ||
14 | tests/qemu-iotests/197 | 4 ++++ | ||
15 | tests/qemu-iotests/common.filter | 3 ++- | ||
16 | 2 files changed, 6 insertions(+), 1 deletion(-) | ||
11 | 17 | ||
12 | One consequence of this is that with small L2 slices operations such | 18 | diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 |
13 | as 'qemu-img map' will need to iterate in more steps because each | 19 | index XXXXXXX..XXXXXXX 100755 |
14 | qcow2_get_cluster_offset() call will potentially return a smaller | 20 | --- a/tests/qemu-iotests/197 |
15 | number. However the code is already prepared for that so this doesn't | 21 | +++ b/tests/qemu-iotests/197 |
16 | break semantics. | 22 | @@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ===' |
17 | 23 | echo | |
18 | The l2_table variable is also renamed to l2_slice to reflect this, and | 24 | |
19 | offset_to_l2_index() is replaced with offset_to_l2_slice_index(). | 25 | # Prep the images |
20 | 26 | +# VPC rounds image sizes to a specific geometry, force a specific size. | |
21 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 27 | +if [ "$IMGFMT" = "vpc" ]; then |
22 | Reviewed-by: Eric Blake <eblake@redhat.com> | 28 | + IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size") |
23 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 29 | +fi |
24 | Message-id: 6b602260acb33da56ed6af9611731cb7acd110eb.1517840877.git.berto@igalia.com | 30 | _make_test_img 4G |
25 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 31 | $QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io |
26 | --- | 32 | IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ |
27 | block/qcow2-cluster.c | 30 +++++++++++++++--------------- | 33 | diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter |
28 | 1 file changed, 15 insertions(+), 15 deletions(-) | ||
29 | |||
30 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
32 | --- a/block/qcow2-cluster.c | 35 | --- a/tests/qemu-iotests/common.filter |
33 | +++ b/block/qcow2-cluster.c | 36 | +++ b/tests/qemu-iotests/common.filter |
34 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | 37 | @@ -XXX,XX +XXX,XX @@ _filter_img_create() |
35 | { | 38 | -e "s# log_size=[0-9]\\+##g" \ |
36 | BDRVQcow2State *s = bs->opaque; | 39 | -e "s# refcount_bits=[0-9]\\+##g" \ |
37 | unsigned int l2_index; | 40 | -e "s# key-secret=[a-zA-Z0-9]\\+##g" \ |
38 | - uint64_t l1_index, l2_offset, *l2_table; | 41 | - -e "s# iter-time=[0-9]\\+##g" |
39 | - int l1_bits, c; | 42 | + -e "s# iter-time=[0-9]\\+##g" \ |
40 | + uint64_t l1_index, l2_offset, *l2_slice; | 43 | + -e "s# force_size=\\(on\\|off\\)##g" |
41 | + int c; | ||
42 | unsigned int offset_in_cluster; | ||
43 | uint64_t bytes_available, bytes_needed, nb_clusters; | ||
44 | QCow2ClusterType type; | ||
45 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
46 | offset_in_cluster = offset_into_cluster(s, offset); | ||
47 | bytes_needed = (uint64_t) *bytes + offset_in_cluster; | ||
48 | |||
49 | - l1_bits = s->l2_bits + s->cluster_bits; | ||
50 | - | ||
51 | /* compute how many bytes there are between the start of the cluster | ||
52 | - * containing offset and the end of the l1 entry */ | ||
53 | - bytes_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1)) | ||
54 | - + offset_in_cluster; | ||
55 | + * containing offset and the end of the l2 slice that contains | ||
56 | + * the entry pointing to it */ | ||
57 | + bytes_available = | ||
58 | + ((uint64_t) (s->l2_slice_size - offset_to_l2_slice_index(s, offset))) | ||
59 | + << s->cluster_bits; | ||
60 | |||
61 | if (bytes_needed > bytes_available) { | ||
62 | bytes_needed = bytes_available; | ||
63 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
64 | return -EIO; | ||
65 | } | ||
66 | |||
67 | - /* load the l2 table in memory */ | ||
68 | + /* load the l2 slice in memory */ | ||
69 | |||
70 | - ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
71 | + ret = l2_load(bs, offset, l2_offset, &l2_slice); | ||
72 | if (ret < 0) { | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | /* find the cluster offset for the given disk offset */ | ||
77 | |||
78 | - l2_index = offset_to_l2_index(s, offset); | ||
79 | - *cluster_offset = be64_to_cpu(l2_table[l2_index]); | ||
80 | + l2_index = offset_to_l2_slice_index(s, offset); | ||
81 | + *cluster_offset = be64_to_cpu(l2_slice[l2_index]); | ||
82 | |||
83 | nb_clusters = size_to_clusters(s, bytes_needed); | ||
84 | /* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned | ||
85 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
86 | case QCOW2_CLUSTER_UNALLOCATED: | ||
87 | /* how many empty clusters ? */ | ||
88 | c = count_contiguous_clusters_unallocated(nb_clusters, | ||
89 | - &l2_table[l2_index], type); | ||
90 | + &l2_slice[l2_index], type); | ||
91 | *cluster_offset = 0; | ||
92 | break; | ||
93 | case QCOW2_CLUSTER_ZERO_ALLOC: | ||
94 | case QCOW2_CLUSTER_NORMAL: | ||
95 | /* how many allocated clusters ? */ | ||
96 | c = count_contiguous_clusters(nb_clusters, s->cluster_size, | ||
97 | - &l2_table[l2_index], QCOW_OFLAG_ZERO); | ||
98 | + &l2_slice[l2_index], QCOW_OFLAG_ZERO); | ||
99 | *cluster_offset &= L2E_OFFSET_MASK; | ||
100 | if (offset_into_cluster(s, *cluster_offset)) { | ||
101 | qcow2_signal_corruption(bs, true, -1, -1, | ||
102 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
103 | abort(); | ||
104 | } | ||
105 | |||
106 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
107 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
108 | |||
109 | bytes_available = (int64_t)c * s->cluster_size; | ||
110 | |||
111 | @@ -XXX,XX +XXX,XX @@ out: | ||
112 | return type; | ||
113 | |||
114 | fail: | ||
115 | - qcow2_cache_put(s->l2_table_cache, (void **)&l2_table); | ||
116 | + qcow2_cache_put(s->l2_table_cache, (void **)&l2_slice); | ||
117 | return ret; | ||
118 | } | 44 | } |
119 | 45 | ||
46 | _filter_img_info() | ||
120 | -- | 47 | -- |
121 | 2.13.6 | 48 | 2.13.6 |
122 | 49 | ||
123 | 50 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | This change separates bdrv_drain_invoke(), which calls the BlockDriver |
---|---|---|---|
2 | drain callbacks, from bdrv_drain_recurse(). Instead, the function | ||
3 | performs its own recursion now. | ||
2 | 4 | ||
3 | Now that the code is ready to handle L2 slices we can finally add an | 5 | One reason for this is that bdrv_drain_recurse() can be called multiple |
4 | option to allow configuring their size. | 6 | times by bdrv_drain_all_begin(), but the callbacks may only be called |
7 | once. The separation is necessary to fix this bug. | ||
5 | 8 | ||
6 | An L2 slice is the portion of an L2 table that is read by the qcow2 | 9 | The other reason is that we intend to go to a model where we call all |
7 | cache. Until now the cache was always reading full L2 tables, and | 10 | driver callbacks first, and only then start polling. This is not fully |
8 | since the L2 table size is equal to the cluster size this was not very | 11 | achieved yet with this patch, as bdrv_drain_invoke() contains a |
9 | efficient with large clusters. Here's a more detailed explanation of | 12 | BDRV_POLL_WHILE() loop for the block driver callbacks, which can still |
10 | why it makes sense to have smaller cache entries in order to load L2 | 13 | call callbacks for any unrelated event. It's a step in this direction |
11 | data: | 14 | anyway. |
12 | 15 | ||
13 | https://lists.gnu.org/archive/html/qemu-block/2017-09/msg00635.html | 16 | Cc: qemu-stable@nongnu.org |
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
18 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
19 | --- | ||
20 | block/io.c | 14 +++++++++++--- | ||
21 | 1 file changed, 11 insertions(+), 3 deletions(-) | ||
14 | 22 | ||
15 | This patch introduces a new command-line option to the qcow2 driver | 23 | diff --git a/block/io.c b/block/io.c |
16 | named l2-cache-entry-size (cf. l2-cache-size). The cache entry size | ||
17 | has the same restrictions as the cluster size: it must be a power of | ||
18 | two and it has the same range of allowed values, with the additional | ||
19 | requirement that it must not be larger than the cluster size. | ||
20 | |||
21 | The L2 cache entry size (L2 slice size) remains equal to the cluster | ||
22 | size for now by default, so this feature must be explicitly enabled. | ||
23 | Although my tests show that 4KB slices consistently improve | ||
24 | performance and give the best results, let's wait and make more tests | ||
25 | with different cluster sizes before deciding on an optimal default. | ||
26 | |||
27 | Now that the cache entry size is not necessarily equal to the cluster | ||
28 | size we need to reflect that in the MIN_L2_CACHE_SIZE documentation. | ||
29 | That minimum value is a requirement of the COW algorithm: we need to | ||
30 | read two L2 slices (and not two L2 tables) in order to do COW, see | ||
31 | l2_allocate() for the actual code. | ||
32 | |||
33 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
34 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
35 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
36 | Message-id: c73e5611ff4a9ec5d20de68a6c289553a13d2354.1517840877.git.berto@igalia.com | ||
37 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
38 | --- | ||
39 | qapi/block-core.json | 6 ++++++ | ||
40 | block/qcow2.h | 6 ++++-- | ||
41 | block/qcow2-cache.c | 10 ++++++++-- | ||
42 | block/qcow2.c | 34 +++++++++++++++++++++++++++------- | ||
43 | 4 files changed, 45 insertions(+), 11 deletions(-) | ||
44 | |||
45 | diff --git a/qapi/block-core.json b/qapi/block-core.json | ||
46 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
47 | --- a/qapi/block-core.json | 25 | --- a/block/io.c |
48 | +++ b/qapi/block-core.json | 26 | +++ b/block/io.c |
49 | @@ -XXX,XX +XXX,XX @@ | 27 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) |
50 | # @l2-cache-size: the maximum size of the L2 table cache in | 28 | bdrv_wakeup(bs); |
51 | # bytes (since 2.2) | ||
52 | # | ||
53 | +# @l2-cache-entry-size: the size of each entry in the L2 cache in | ||
54 | +# bytes. It must be a power of two between 512 | ||
55 | +# and the cluster size. The default value is | ||
56 | +# the cluster size (since 2.12) | ||
57 | +# | ||
58 | # @refcount-cache-size: the maximum size of the refcount block cache | ||
59 | # in bytes (since 2.2) | ||
60 | # | ||
61 | @@ -XXX,XX +XXX,XX @@ | ||
62 | '*overlap-check': 'Qcow2OverlapChecks', | ||
63 | '*cache-size': 'int', | ||
64 | '*l2-cache-size': 'int', | ||
65 | + '*l2-cache-entry-size': 'int', | ||
66 | '*refcount-cache-size': 'int', | ||
67 | '*cache-clean-interval': 'int', | ||
68 | '*encrypt': 'BlockdevQcow2Encryption' } } | ||
69 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
70 | index XXXXXXX..XXXXXXX 100644 | ||
71 | --- a/block/qcow2.h | ||
72 | +++ b/block/qcow2.h | ||
73 | @@ -XXX,XX +XXX,XX @@ | ||
74 | #define MAX_CLUSTER_BITS 21 | ||
75 | |||
76 | /* Must be at least 2 to cover COW */ | ||
77 | -#define MIN_L2_CACHE_SIZE 2 /* clusters */ | ||
78 | +#define MIN_L2_CACHE_SIZE 2 /* cache entries */ | ||
79 | |||
80 | /* Must be at least 4 to cover all cases of refcount table growth */ | ||
81 | #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ | ||
82 | @@ -XXX,XX +XXX,XX @@ | ||
83 | #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" | ||
84 | #define QCOW2_OPT_CACHE_SIZE "cache-size" | ||
85 | #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" | ||
86 | +#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" | ||
87 | #define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" | ||
88 | #define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" | ||
89 | |||
90 | @@ -XXX,XX +XXX,XX @@ void qcow2_free_snapshots(BlockDriverState *bs); | ||
91 | int qcow2_read_snapshots(BlockDriverState *bs); | ||
92 | |||
93 | /* qcow2-cache.c functions */ | ||
94 | -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); | ||
95 | +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, | ||
96 | + unsigned table_size); | ||
97 | int qcow2_cache_destroy(Qcow2Cache *c); | ||
98 | |||
99 | void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); | ||
100 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
101 | index XXXXXXX..XXXXXXX 100644 | ||
102 | --- a/block/qcow2-cache.c | ||
103 | +++ b/block/qcow2-cache.c | ||
104 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_clean_unused(Qcow2Cache *c) | ||
105 | c->cache_clean_lru_counter = c->lru_counter; | ||
106 | } | 29 | } |
107 | 30 | ||
108 | -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) | 31 | +/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ |
109 | +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, | 32 | static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) |
110 | + unsigned table_size) | ||
111 | { | 33 | { |
112 | BDRVQcow2State *s = bs->opaque; | 34 | + BdrvChild *child, *tmp; |
113 | Qcow2Cache *c; | 35 | BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; |
114 | 36 | ||
115 | + assert(num_tables > 0); | 37 | if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || |
116 | + assert(is_power_of_2(table_size)); | 38 | @@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) |
117 | + assert(table_size >= (1 << MIN_CLUSTER_BITS)); | 39 | data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); |
118 | + assert(table_size <= s->cluster_size); | 40 | bdrv_coroutine_enter(bs, data.co); |
41 | BDRV_POLL_WHILE(bs, !data.done); | ||
119 | + | 42 | + |
120 | c = g_new0(Qcow2Cache, 1); | 43 | + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { |
121 | c->size = num_tables; | 44 | + bdrv_drain_invoke(child->bs, begin); |
122 | - c->table_size = s->cluster_size; | ||
123 | + c->table_size = table_size; | ||
124 | c->entries = g_try_new0(Qcow2CachedTable, num_tables); | ||
125 | c->table_array = qemu_try_blockalign(bs->file->bs, | ||
126 | (size_t) num_tables * c->table_size); | ||
127 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
128 | index XXXXXXX..XXXXXXX 100644 | ||
129 | --- a/block/qcow2.c | ||
130 | +++ b/block/qcow2.c | ||
131 | @@ -XXX,XX +XXX,XX @@ static QemuOptsList qcow2_runtime_opts = { | ||
132 | .help = "Maximum L2 table cache size", | ||
133 | }, | ||
134 | { | ||
135 | + .name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE, | ||
136 | + .type = QEMU_OPT_SIZE, | ||
137 | + .help = "Size of each entry in the L2 cache", | ||
138 | + }, | ||
139 | + { | ||
140 | .name = QCOW2_OPT_REFCOUNT_CACHE_SIZE, | ||
141 | .type = QEMU_OPT_SIZE, | ||
142 | .help = "Maximum refcount block cache size", | ||
143 | @@ -XXX,XX +XXX,XX @@ static void qcow2_attach_aio_context(BlockDriverState *bs, | ||
144 | |||
145 | static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, | ||
146 | uint64_t *l2_cache_size, | ||
147 | + uint64_t *l2_cache_entry_size, | ||
148 | uint64_t *refcount_cache_size, Error **errp) | ||
149 | { | ||
150 | BDRVQcow2State *s = bs->opaque; | ||
151 | @@ -XXX,XX +XXX,XX @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, | ||
152 | *refcount_cache_size = qemu_opt_get_size(opts, | ||
153 | QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); | ||
154 | |||
155 | + *l2_cache_entry_size = qemu_opt_get_size( | ||
156 | + opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size); | ||
157 | + | ||
158 | if (combined_cache_size_set) { | ||
159 | if (l2_cache_size_set && refcount_cache_size_set) { | ||
160 | error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE | ||
161 | @@ -XXX,XX +XXX,XX @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, | ||
162 | / DEFAULT_L2_REFCOUNT_SIZE_RATIO; | ||
163 | } | ||
164 | } | ||
165 | + | ||
166 | + if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) || | ||
167 | + *l2_cache_entry_size > s->cluster_size || | ||
168 | + !is_power_of_2(*l2_cache_entry_size)) { | ||
169 | + error_setg(errp, "L2 cache entry size must be a power of two " | ||
170 | + "between %d and the cluster size (%d)", | ||
171 | + 1 << MIN_CLUSTER_BITS, s->cluster_size); | ||
172 | + return; | ||
173 | + } | 45 | + } |
174 | } | 46 | } |
175 | 47 | ||
176 | typedef struct Qcow2ReopenState { | 48 | static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) |
177 | @@ -XXX,XX +XXX,XX @@ static int qcow2_update_options_prepare(BlockDriverState *bs, | 49 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) |
178 | QemuOpts *opts = NULL; | 50 | BdrvChild *child, *tmp; |
179 | const char *opt_overlap_check, *opt_overlap_check_template; | 51 | bool waited; |
180 | int overlap_check_template = 0; | 52 | |
181 | - uint64_t l2_cache_size, refcount_cache_size; | 53 | - /* Ensure any pending metadata writes are submitted to bs->file. */ |
182 | + uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size; | 54 | - bdrv_drain_invoke(bs, begin); |
183 | int i; | 55 | - |
184 | const char *encryptfmt; | 56 | /* Wait for drained requests to finish */ |
185 | QDict *encryptopts = NULL; | 57 | waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); |
186 | @@ -XXX,XX +XXX,XX @@ static int qcow2_update_options_prepare(BlockDriverState *bs, | 58 | |
59 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) | ||
60 | bdrv_parent_drained_begin(bs); | ||
187 | } | 61 | } |
188 | 62 | ||
189 | /* get L2 table/refcount block cache size from command line options */ | 63 | + bdrv_drain_invoke(bs, true); |
190 | - read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, | 64 | bdrv_drain_recurse(bs, true); |
191 | - &local_err); | 65 | } |
192 | + read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, | 66 | |
193 | + &refcount_cache_size, &local_err); | 67 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) |
194 | if (local_err) { | ||
195 | error_propagate(errp, local_err); | ||
196 | ret = -EINVAL; | ||
197 | goto fail; | ||
198 | } | 68 | } |
199 | 69 | ||
200 | - l2_cache_size /= s->cluster_size; | 70 | bdrv_parent_drained_end(bs); |
201 | + l2_cache_size /= l2_cache_entry_size; | 71 | + bdrv_drain_invoke(bs, false); |
202 | if (l2_cache_size < MIN_L2_CACHE_SIZE) { | 72 | bdrv_drain_recurse(bs, false); |
203 | l2_cache_size = MIN_L2_CACHE_SIZE; | 73 | aio_enable_external(bdrv_get_aio_context(bs)); |
74 | } | ||
75 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
76 | aio_context_acquire(aio_context); | ||
77 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
78 | if (aio_context == bdrv_get_aio_context(bs)) { | ||
79 | + /* FIXME Calling this multiple times is wrong */ | ||
80 | + bdrv_drain_invoke(bs, true); | ||
81 | waited |= bdrv_drain_recurse(bs, true); | ||
82 | } | ||
83 | } | ||
84 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
85 | aio_context_acquire(aio_context); | ||
86 | aio_enable_external(aio_context); | ||
87 | bdrv_parent_drained_end(bs); | ||
88 | + bdrv_drain_invoke(bs, false); | ||
89 | bdrv_drain_recurse(bs, false); | ||
90 | aio_context_release(aio_context); | ||
204 | } | 91 | } |
205 | @@ -XXX,XX +XXX,XX @@ static int qcow2_update_options_prepare(BlockDriverState *bs, | ||
206 | } | ||
207 | } | ||
208 | |||
209 | - r->l2_slice_size = s->cluster_size / sizeof(uint64_t); | ||
210 | - r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); | ||
211 | - r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); | ||
212 | + r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t); | ||
213 | + r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size, | ||
214 | + l2_cache_entry_size); | ||
215 | + r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size, | ||
216 | + s->cluster_size); | ||
217 | if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { | ||
218 | error_setg(errp, "Could not allocate metadata caches"); | ||
219 | ret = -ENOMEM; | ||
220 | -- | 92 | -- |
221 | 2.13.6 | 93 | 2.13.6 |
222 | 94 | ||
223 | 95 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver |
---|---|---|---|
2 | callback inside its polling loop. This means that how many times it got | ||
3 | called for each node depended on long it had to poll the event loop. | ||
2 | 4 | ||
3 | Adding support for L2 slices to expand_zero_clusters_in_l1() needs | 5 | This is obviously not right and results in nodes that stay drained even |
4 | (among other things) an extra loop that iterates over all slices of | 6 | after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per |
5 | each L2 table. | 7 | node. |
6 | 8 | ||
7 | Putting all changes in one patch would make it hard to read because | 9 | Fix bdrv_drain_all_begin() to call the callback only once, too. |
8 | all semantic changes would be mixed with pure indentation changes. | ||
9 | 10 | ||
10 | To make things easier this patch simply creates a new block and | 11 | Cc: qemu-stable@nongnu.org |
11 | changes the indentation of all lines of code inside it. Thus, all | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | modifications in this patch are cosmetic. There are no semantic | 13 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
13 | changes and no variables are renamed yet. The next patch will take | 14 | --- |
14 | care of that. | 15 | block/io.c | 3 +-- |
16 | 1 file changed, 1 insertion(+), 2 deletions(-) | ||
15 | 17 | ||
16 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 18 | diff --git a/block/io.c b/block/io.c |
17 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
18 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
19 | Message-id: c2ae9f31ed5b6e591477ad4654448badd1c89d73.1517840877.git.berto@igalia.com | ||
20 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
21 | --- | ||
22 | block/qcow2-cluster.c | 187 ++++++++++++++++++++++++++------------------------ | ||
23 | 1 file changed, 96 insertions(+), 91 deletions(-) | ||
24 | |||
25 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
26 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/block/qcow2-cluster.c | 20 | --- a/block/io.c |
28 | +++ b/block/qcow2-cluster.c | 21 | +++ b/block/io.c |
29 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | 22 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) |
30 | goto fail; | 23 | aio_context_acquire(aio_context); |
31 | } | 24 | bdrv_parent_drained_begin(bs); |
32 | 25 | aio_disable_external(aio_context); | |
33 | - if (is_active_l1) { | 26 | + bdrv_drain_invoke(bs, true); |
34 | - /* get active L2 tables from cache */ | 27 | aio_context_release(aio_context); |
35 | - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, | 28 | |
36 | - (void **)&l2_table); | 29 | if (!g_slist_find(aio_ctxs, aio_context)) { |
37 | - } else { | 30 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) |
38 | - /* load inactive L2 tables from disk */ | 31 | aio_context_acquire(aio_context); |
39 | - ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, | 32 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { |
40 | - (void *)l2_table, s->cluster_sectors); | 33 | if (aio_context == bdrv_get_aio_context(bs)) { |
41 | - } | 34 | - /* FIXME Calling this multiple times is wrong */ |
42 | - if (ret < 0) { | 35 | - bdrv_drain_invoke(bs, true); |
43 | - goto fail; | 36 | waited |= bdrv_drain_recurse(bs, true); |
44 | - } | ||
45 | - | ||
46 | - for (j = 0; j < s->l2_size; j++) { | ||
47 | - uint64_t l2_entry = be64_to_cpu(l2_table[j]); | ||
48 | - int64_t offset = l2_entry & L2E_OFFSET_MASK; | ||
49 | - QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); | ||
50 | - | ||
51 | - if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && | ||
52 | - cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { | ||
53 | - continue; | ||
54 | + { | ||
55 | + if (is_active_l1) { | ||
56 | + /* get active L2 tables from cache */ | ||
57 | + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, | ||
58 | + (void **)&l2_table); | ||
59 | + } else { | ||
60 | + /* load inactive L2 tables from disk */ | ||
61 | + ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||
62 | + (void *)l2_table, s->cluster_sectors); | ||
63 | + } | ||
64 | + if (ret < 0) { | ||
65 | + goto fail; | ||
66 | } | ||
67 | |||
68 | - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
69 | - if (!bs->backing) { | ||
70 | - /* not backed; therefore we can simply deallocate the | ||
71 | - * cluster */ | ||
72 | - l2_table[j] = 0; | ||
73 | - l2_dirty = true; | ||
74 | + for (j = 0; j < s->l2_size; j++) { | ||
75 | + uint64_t l2_entry = be64_to_cpu(l2_table[j]); | ||
76 | + int64_t offset = l2_entry & L2E_OFFSET_MASK; | ||
77 | + QCow2ClusterType cluster_type = | ||
78 | + qcow2_get_cluster_type(l2_entry); | ||
79 | + | ||
80 | + if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && | ||
81 | + cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { | ||
82 | continue; | ||
83 | } | ||
84 | |||
85 | - offset = qcow2_alloc_clusters(bs, s->cluster_size); | ||
86 | - if (offset < 0) { | ||
87 | - ret = offset; | ||
88 | - goto fail; | ||
89 | - } | ||
90 | + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
91 | + if (!bs->backing) { | ||
92 | + /* not backed; therefore we can simply deallocate the | ||
93 | + * cluster */ | ||
94 | + l2_table[j] = 0; | ||
95 | + l2_dirty = true; | ||
96 | + continue; | ||
97 | + } | ||
98 | + | ||
99 | + offset = qcow2_alloc_clusters(bs, s->cluster_size); | ||
100 | + if (offset < 0) { | ||
101 | + ret = offset; | ||
102 | + goto fail; | ||
103 | + } | ||
104 | |||
105 | - if (l2_refcount > 1) { | ||
106 | - /* For shared L2 tables, set the refcount accordingly (it is | ||
107 | - * already 1 and needs to be l2_refcount) */ | ||
108 | - ret = qcow2_update_cluster_refcount(bs, | ||
109 | - offset >> s->cluster_bits, | ||
110 | + if (l2_refcount > 1) { | ||
111 | + /* For shared L2 tables, set the refcount accordingly | ||
112 | + * (it is already 1 and needs to be l2_refcount) */ | ||
113 | + ret = qcow2_update_cluster_refcount( | ||
114 | + bs, offset >> s->cluster_bits, | ||
115 | refcount_diff(1, l2_refcount), false, | ||
116 | QCOW2_DISCARD_OTHER); | ||
117 | - if (ret < 0) { | ||
118 | - qcow2_free_clusters(bs, offset, s->cluster_size, | ||
119 | - QCOW2_DISCARD_OTHER); | ||
120 | - goto fail; | ||
121 | + if (ret < 0) { | ||
122 | + qcow2_free_clusters(bs, offset, s->cluster_size, | ||
123 | + QCOW2_DISCARD_OTHER); | ||
124 | + goto fail; | ||
125 | + } | ||
126 | } | ||
127 | } | ||
128 | - } | ||
129 | |||
130 | - if (offset_into_cluster(s, offset)) { | ||
131 | - qcow2_signal_corruption(bs, true, -1, -1, | ||
132 | - "Cluster allocation offset " | ||
133 | - "%#" PRIx64 " unaligned (L2 offset: %#" | ||
134 | - PRIx64 ", L2 index: %#x)", offset, | ||
135 | - l2_offset, j); | ||
136 | - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
137 | - qcow2_free_clusters(bs, offset, s->cluster_size, | ||
138 | - QCOW2_DISCARD_ALWAYS); | ||
139 | + if (offset_into_cluster(s, offset)) { | ||
140 | + qcow2_signal_corruption( | ||
141 | + bs, true, -1, -1, | ||
142 | + "Cluster allocation offset " | ||
143 | + "%#" PRIx64 " unaligned (L2 offset: %#" | ||
144 | + PRIx64 ", L2 index: %#x)", offset, | ||
145 | + l2_offset, j); | ||
146 | + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
147 | + qcow2_free_clusters(bs, offset, s->cluster_size, | ||
148 | + QCOW2_DISCARD_ALWAYS); | ||
149 | + } | ||
150 | + ret = -EIO; | ||
151 | + goto fail; | ||
152 | } | ||
153 | - ret = -EIO; | ||
154 | - goto fail; | ||
155 | - } | ||
156 | |||
157 | - ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); | ||
158 | - if (ret < 0) { | ||
159 | - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
160 | - qcow2_free_clusters(bs, offset, s->cluster_size, | ||
161 | - QCOW2_DISCARD_ALWAYS); | ||
162 | + ret = qcow2_pre_write_overlap_check(bs, 0, offset, | ||
163 | + s->cluster_size); | ||
164 | + if (ret < 0) { | ||
165 | + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
166 | + qcow2_free_clusters(bs, offset, s->cluster_size, | ||
167 | + QCOW2_DISCARD_ALWAYS); | ||
168 | + } | ||
169 | + goto fail; | ||
170 | } | ||
171 | - goto fail; | ||
172 | - } | ||
173 | |||
174 | - ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); | ||
175 | - if (ret < 0) { | ||
176 | - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
177 | - qcow2_free_clusters(bs, offset, s->cluster_size, | ||
178 | - QCOW2_DISCARD_ALWAYS); | ||
179 | + ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); | ||
180 | + if (ret < 0) { | ||
181 | + if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
182 | + qcow2_free_clusters(bs, offset, s->cluster_size, | ||
183 | + QCOW2_DISCARD_ALWAYS); | ||
184 | + } | ||
185 | + goto fail; | ||
186 | } | ||
187 | - goto fail; | ||
188 | - } | ||
189 | |||
190 | - if (l2_refcount == 1) { | ||
191 | - l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); | ||
192 | - } else { | ||
193 | - l2_table[j] = cpu_to_be64(offset); | ||
194 | + if (l2_refcount == 1) { | ||
195 | + l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); | ||
196 | + } else { | ||
197 | + l2_table[j] = cpu_to_be64(offset); | ||
198 | + } | ||
199 | + l2_dirty = true; | ||
200 | } | ||
201 | - l2_dirty = true; | ||
202 | - } | ||
203 | |||
204 | - if (is_active_l1) { | ||
205 | - if (l2_dirty) { | ||
206 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
207 | - qcow2_cache_depends_on_flush(s->l2_table_cache); | ||
208 | - } | ||
209 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
210 | - } else { | ||
211 | - if (l2_dirty) { | ||
212 | - ret = qcow2_pre_write_overlap_check(bs, | ||
213 | - QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, l2_offset, | ||
214 | - s->cluster_size); | ||
215 | - if (ret < 0) { | ||
216 | - goto fail; | ||
217 | + if (is_active_l1) { | ||
218 | + if (l2_dirty) { | ||
219 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
220 | + qcow2_cache_depends_on_flush(s->l2_table_cache); | ||
221 | } | ||
222 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
223 | + } else { | ||
224 | + if (l2_dirty) { | ||
225 | + ret = qcow2_pre_write_overlap_check( | ||
226 | + bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, | ||
227 | + l2_offset, s->cluster_size); | ||
228 | + if (ret < 0) { | ||
229 | + goto fail; | ||
230 | + } | ||
231 | |||
232 | - ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||
233 | - (void *)l2_table, s->cluster_sectors); | ||
234 | - if (ret < 0) { | ||
235 | - goto fail; | ||
236 | + ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||
237 | + (void *)l2_table, s->cluster_sectors); | ||
238 | + if (ret < 0) { | ||
239 | + goto fail; | ||
240 | + } | ||
241 | } | 37 | } |
242 | } | 38 | } |
243 | } | ||
244 | -- | 39 | -- |
245 | 2.13.6 | 40 | 2.13.6 |
246 | 41 | ||
247 | 42 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | This adds a test case that the BlockDriver callbacks for drain are |
---|---|---|---|
2 | called in bdrv_drained_all_begin/end(), and that both of them are called | ||
3 | exactly once. | ||
2 | 4 | ||
3 | Pull out the truncation code from the qemu_cluster_create() function so | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | we can later reuse it in qemu_gluster_truncate(). | 6 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | --- | ||
9 | tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ | ||
10 | tests/Makefile.include | 2 + | ||
11 | 2 files changed, 139 insertions(+) | ||
12 | create mode 100644 tests/test-bdrv-drain.c | ||
5 | 13 | ||
6 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 14 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | 15 | new file mode 100644 |
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 16 | index XXXXXXX..XXXXXXX |
9 | --- | 17 | --- /dev/null |
10 | block/gluster.c | 74 +++++++++++++++++++++++++++++++-------------------------- | 18 | +++ b/tests/test-bdrv-drain.c |
11 | 1 file changed, 40 insertions(+), 34 deletions(-) | 19 | @@ -XXX,XX +XXX,XX @@ |
12 | 20 | +/* | |
13 | diff --git a/block/gluster.c b/block/gluster.c | 21 | + * Block node draining tests |
14 | index XXXXXXX..XXXXXXX 100644 | 22 | + * |
15 | --- a/block/gluster.c | 23 | + * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com> |
16 | +++ b/block/gluster.c | 24 | + * |
17 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, | 25 | + * Permission is hereby granted, free of charge, to any person obtaining a copy |
18 | } | 26 | + * of this software and associated documentation files (the "Software"), to deal |
19 | #endif | 27 | + * in the Software without restriction, including without limitation the rights |
20 | 28 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
21 | +static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset, | 29 | + * copies of the Software, and to permit persons to whom the Software is |
22 | + PreallocMode prealloc, Error **errp) | 30 | + * furnished to do so, subject to the following conditions: |
31 | + * | ||
32 | + * The above copyright notice and this permission notice shall be included in | ||
33 | + * all copies or substantial portions of the Software. | ||
34 | + * | ||
35 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
36 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
37 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
38 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
39 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
40 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
41 | + * THE SOFTWARE. | ||
42 | + */ | ||
43 | + | ||
44 | +#include "qemu/osdep.h" | ||
45 | +#include "block/block.h" | ||
46 | +#include "sysemu/block-backend.h" | ||
47 | +#include "qapi/error.h" | ||
48 | + | ||
49 | +typedef struct BDRVTestState { | ||
50 | + int drain_count; | ||
51 | +} BDRVTestState; | ||
52 | + | ||
53 | +static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) | ||
23 | +{ | 54 | +{ |
24 | + switch (prealloc) { | 55 | + BDRVTestState *s = bs->opaque; |
25 | +#ifdef CONFIG_GLUSTERFS_FALLOCATE | 56 | + s->drain_count++; |
26 | + case PREALLOC_MODE_FALLOC: | 57 | +} |
27 | + if (glfs_fallocate(fd, 0, 0, offset)) { | 58 | + |
28 | + error_setg_errno(errp, errno, "Could not preallocate data"); | 59 | +static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) |
29 | + return -errno; | 60 | +{ |
30 | + } | 61 | + BDRVTestState *s = bs->opaque; |
31 | + break; | 62 | + s->drain_count--; |
32 | +#endif /* CONFIG_GLUSTERFS_FALLOCATE */ | 63 | +} |
33 | +#ifdef CONFIG_GLUSTERFS_ZEROFILL | 64 | + |
34 | + case PREALLOC_MODE_FULL: | 65 | +static void bdrv_test_close(BlockDriverState *bs) |
35 | + if (glfs_ftruncate(fd, offset)) { | 66 | +{ |
36 | + error_setg_errno(errp, errno, "Could not resize file"); | 67 | + BDRVTestState *s = bs->opaque; |
37 | + return -errno; | 68 | + g_assert_cmpint(s->drain_count, >, 0); |
38 | + } | 69 | +} |
39 | + if (glfs_zerofill(fd, 0, offset)) { | 70 | + |
40 | + error_setg_errno(errp, errno, "Could not zerofill the new area"); | 71 | +static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, |
41 | + return -errno; | 72 | + uint64_t offset, uint64_t bytes, |
42 | + } | 73 | + QEMUIOVector *qiov, int flags) |
43 | + break; | 74 | +{ |
44 | +#endif /* CONFIG_GLUSTERFS_ZEROFILL */ | 75 | + /* We want this request to stay until the polling loop in drain waits for |
45 | + case PREALLOC_MODE_OFF: | 76 | + * it to complete. We need to sleep a while as bdrv_drain_invoke() comes |
46 | + if (glfs_ftruncate(fd, offset)) { | 77 | + * first and polls its result, too, but it shouldn't accidentally complete |
47 | + error_setg_errno(errp, errno, "Could not resize file"); | 78 | + * this request yet. */ |
48 | + return -errno; | 79 | + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); |
49 | + } | ||
50 | + break; | ||
51 | + default: | ||
52 | + error_setg(errp, "Unsupported preallocation mode: %s", | ||
53 | + PreallocMode_str(prealloc)); | ||
54 | + return -EINVAL; | ||
55 | + } | ||
56 | + | 80 | + |
57 | + return 0; | 81 | + return 0; |
58 | +} | 82 | +} |
59 | + | 83 | + |
60 | static int qemu_gluster_create(const char *filename, | 84 | +static BlockDriver bdrv_test = { |
61 | QemuOpts *opts, Error **errp) | 85 | + .format_name = "test", |
62 | { | 86 | + .instance_size = sizeof(BDRVTestState), |
63 | @@ -XXX,XX +XXX,XX @@ static int qemu_gluster_create(const char *filename, | 87 | + |
64 | goto out; | 88 | + .bdrv_close = bdrv_test_close, |
65 | } | 89 | + .bdrv_co_preadv = bdrv_test_co_preadv, |
66 | 90 | + | |
67 | - switch (prealloc) { | 91 | + .bdrv_co_drain_begin = bdrv_test_co_drain_begin, |
68 | -#ifdef CONFIG_GLUSTERFS_FALLOCATE | 92 | + .bdrv_co_drain_end = bdrv_test_co_drain_end, |
69 | - case PREALLOC_MODE_FALLOC: | 93 | +}; |
70 | - if (glfs_fallocate(fd, 0, 0, total_size)) { | 94 | + |
71 | - error_setg(errp, "Could not preallocate data for the new file"); | 95 | +static void aio_ret_cb(void *opaque, int ret) |
72 | - ret = -errno; | 96 | +{ |
73 | - } | 97 | + int *aio_ret = opaque; |
74 | - break; | 98 | + *aio_ret = ret; |
75 | -#endif /* CONFIG_GLUSTERFS_FALLOCATE */ | 99 | +} |
76 | -#ifdef CONFIG_GLUSTERFS_ZEROFILL | 100 | + |
77 | - case PREALLOC_MODE_FULL: | 101 | +static void test_drv_cb_drain_all(void) |
78 | - if (!glfs_ftruncate(fd, total_size)) { | 102 | +{ |
79 | - if (glfs_zerofill(fd, 0, total_size)) { | 103 | + BlockBackend *blk; |
80 | - error_setg(errp, "Could not zerofill the new file"); | 104 | + BlockDriverState *bs; |
81 | - ret = -errno; | 105 | + BDRVTestState *s; |
82 | - } | 106 | + BlockAIOCB *acb; |
83 | - } else { | 107 | + int aio_ret; |
84 | - error_setg(errp, "Could not resize file"); | 108 | + |
85 | - ret = -errno; | 109 | + QEMUIOVector qiov; |
86 | - } | 110 | + struct iovec iov = { |
87 | - break; | 111 | + .iov_base = NULL, |
88 | -#endif /* CONFIG_GLUSTERFS_ZEROFILL */ | 112 | + .iov_len = 0, |
89 | - case PREALLOC_MODE_OFF: | 113 | + }; |
90 | - if (glfs_ftruncate(fd, total_size) != 0) { | 114 | + qemu_iovec_init_external(&qiov, &iov, 1); |
91 | - ret = -errno; | 115 | + |
92 | - error_setg(errp, "Could not resize file"); | 116 | + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); |
93 | - } | 117 | + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, |
94 | - break; | 118 | + &error_abort); |
95 | - default: | 119 | + s = bs->opaque; |
96 | - ret = -EINVAL; | 120 | + blk_insert_bs(blk, bs, &error_abort); |
97 | - error_setg(errp, "Unsupported preallocation mode: %s", | 121 | + |
98 | - PreallocMode_str(prealloc)); | 122 | + /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ |
99 | - break; | 123 | + g_assert_cmpint(s->drain_count, ==, 0); |
100 | - } | 124 | + bdrv_drain_all_begin(); |
101 | + ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp); | 125 | + g_assert_cmpint(s->drain_count, ==, 1); |
102 | 126 | + bdrv_drain_all_end(); | |
103 | out: | 127 | + g_assert_cmpint(s->drain_count, ==, 0); |
104 | if (fd) { | 128 | + |
129 | + /* Now do the same while a request is pending */ | ||
130 | + aio_ret = -EINPROGRESS; | ||
131 | + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); | ||
132 | + g_assert(acb != NULL); | ||
133 | + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); | ||
134 | + | ||
135 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
136 | + bdrv_drain_all_begin(); | ||
137 | + g_assert_cmpint(aio_ret, ==, 0); | ||
138 | + g_assert_cmpint(s->drain_count, ==, 1); | ||
139 | + bdrv_drain_all_end(); | ||
140 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
141 | + | ||
142 | + bdrv_unref(bs); | ||
143 | + blk_unref(blk); | ||
144 | +} | ||
145 | + | ||
146 | +int main(int argc, char **argv) | ||
147 | +{ | ||
148 | + bdrv_init(); | ||
149 | + qemu_init_main_loop(&error_abort); | ||
150 | + | ||
151 | + g_test_init(&argc, &argv, NULL); | ||
152 | + | ||
153 | + g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); | ||
154 | + | ||
155 | + return g_test_run(); | ||
156 | +} | ||
157 | diff --git a/tests/Makefile.include b/tests/Makefile.include | ||
158 | index XXXXXXX..XXXXXXX 100644 | ||
159 | --- a/tests/Makefile.include | ||
160 | +++ b/tests/Makefile.include | ||
161 | @@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c | ||
162 | gcov-files-test-hbitmap-y = util/hbitmap.c | ||
163 | check-unit-y += tests/test-hbitmap$(EXESUF) | ||
164 | gcov-files-test-hbitmap-y = blockjob.c | ||
165 | +check-unit-y += tests/test-bdrv-drain$(EXESUF) | ||
166 | check-unit-y += tests/test-blockjob$(EXESUF) | ||
167 | check-unit-y += tests/test-blockjob-txn$(EXESUF) | ||
168 | check-unit-y += tests/test-x86-cpuid$(EXESUF) | ||
169 | @@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) | ||
170 | tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) | ||
171 | tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y) | ||
172 | tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) | ||
173 | +tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y) | ||
174 | tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) | ||
175 | tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) | ||
176 | tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) | ||
105 | -- | 177 | -- |
106 | 2.13.6 | 178 | 2.13.6 |
107 | 179 | ||
108 | 180 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Now that the bdrv_drain_invoke() calls are pulled up to the callers of |
---|---|---|---|
2 | bdrv_drain_recurse(), the 'begin' parameter isn't needed any more. | ||
2 | 3 | ||
3 | This function was only using the BlockDriverState parameter to pass it | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | to qcow2_cache_get_table_idx(). This is no longer necessary so this | 5 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
5 | parameter can be removed. | 6 | --- |
7 | block/io.c | 12 ++++++------ | ||
8 | 1 file changed, 6 insertions(+), 6 deletions(-) | ||
6 | 9 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 10 | diff --git a/block/io.c b/block/io.c |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 5c40516a91782b083c1428b7b6a41bb9e2679bfb.1517840876.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2.h | 3 +-- | ||
14 | block/qcow2-cache.c | 3 +-- | ||
15 | block/qcow2-cluster.c | 12 ++++++------ | ||
16 | block/qcow2-refcount.c | 14 ++++++-------- | ||
17 | 4 files changed, 14 insertions(+), 18 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
20 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/block/qcow2.h | 12 | --- a/block/io.c |
22 | +++ b/block/qcow2.h | 13 | +++ b/block/io.c |
23 | @@ -XXX,XX +XXX,XX @@ int qcow2_read_snapshots(BlockDriverState *bs); | 14 | @@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) |
24 | Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); | 15 | } |
25 | int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); | ||
26 | |||
27 | -void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, | ||
28 | - void *table); | ||
29 | +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); | ||
30 | int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); | ||
31 | int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); | ||
32 | int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, | ||
33 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
34 | index XXXXXXX..XXXXXXX 100644 | ||
35 | --- a/block/qcow2-cache.c | ||
36 | +++ b/block/qcow2-cache.c | ||
37 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | ||
38 | assert(c->entries[i].ref >= 0); | ||
39 | } | 16 | } |
40 | 17 | ||
41 | -void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, | 18 | -static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) |
42 | - void *table) | 19 | +static bool bdrv_drain_recurse(BlockDriverState *bs) |
43 | +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) | ||
44 | { | 20 | { |
45 | int i = qcow2_cache_get_table_idx(c, table); | 21 | BdrvChild *child, *tmp; |
46 | assert(c->entries[i].offset != 0); | 22 | bool waited; |
47 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | 23 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) |
48 | index XXXXXXX..XXXXXXX 100644 | 24 | */ |
49 | --- a/block/qcow2-cluster.c | 25 | bdrv_ref(bs); |
50 | +++ b/block/qcow2-cluster.c | 26 | } |
51 | @@ -XXX,XX +XXX,XX @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | 27 | - waited |= bdrv_drain_recurse(bs, begin); |
52 | BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); | 28 | + waited |= bdrv_drain_recurse(bs); |
53 | 29 | if (in_main_loop) { | |
54 | trace_qcow2_l2_allocate_write_l2(bs, l1_index); | 30 | bdrv_unref(bs); |
55 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | 31 | } |
56 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | 32 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) |
57 | ret = qcow2_cache_flush(bs, s->l2_table_cache); | ||
58 | if (ret < 0) { | ||
59 | goto fail; | ||
60 | @@ -XXX,XX +XXX,XX @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||
61 | /* compressed clusters never have the copied flag */ | ||
62 | |||
63 | BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); | ||
64 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||
65 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
66 | l2_table[l2_index] = cpu_to_be64(cluster_offset); | ||
67 | qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
68 | |||
69 | @@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||
70 | if (ret < 0) { | ||
71 | goto err; | ||
72 | } | 33 | } |
73 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | 34 | |
74 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | 35 | bdrv_drain_invoke(bs, true); |
75 | 36 | - bdrv_drain_recurse(bs, true); | |
76 | assert(l2_index + m->nb_clusters <= s->l2_size); | 37 | + bdrv_drain_recurse(bs); |
77 | for (i = 0; i < m->nb_clusters; i++) { | 38 | } |
78 | @@ -XXX,XX +XXX,XX @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | 39 | |
79 | } | 40 | void bdrv_drained_end(BlockDriverState *bs) |
80 | 41 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | |
81 | /* First remove L2 entries */ | 42 | |
82 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | 43 | bdrv_parent_drained_end(bs); |
83 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | 44 | bdrv_drain_invoke(bs, false); |
84 | if (!full_discard && s->qcow_version >= 3) { | 45 | - bdrv_drain_recurse(bs, false); |
85 | l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | 46 | + bdrv_drain_recurse(bs); |
86 | } else { | 47 | aio_enable_external(bdrv_get_aio_context(bs)); |
87 | @@ -XXX,XX +XXX,XX @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | 48 | } |
88 | continue; | 49 | |
89 | } | 50 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) |
90 | 51 | aio_context_acquire(aio_context); | |
91 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | 52 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { |
92 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | 53 | if (aio_context == bdrv_get_aio_context(bs)) { |
93 | if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) { | 54 | - waited |= bdrv_drain_recurse(bs, true); |
94 | l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | 55 | + waited |= bdrv_drain_recurse(bs); |
95 | qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); | ||
96 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
97 | |||
98 | if (is_active_l1) { | ||
99 | if (l2_dirty) { | ||
100 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||
101 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
102 | qcow2_cache_depends_on_flush(s->l2_table_cache); | ||
103 | } | ||
104 | qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
105 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/block/qcow2-refcount.c | ||
108 | +++ b/block/qcow2-refcount.c | ||
109 | @@ -XXX,XX +XXX,XX @@ static int alloc_refcount_block(BlockDriverState *bs, | ||
110 | |||
111 | /* Now the new refcount block needs to be written to disk */ | ||
112 | BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); | ||
113 | - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, *refcount_block); | ||
114 | + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); | ||
115 | ret = qcow2_cache_flush(bs, s->refcount_block_cache); | ||
116 | if (ret < 0) { | ||
117 | goto fail; | ||
118 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | ||
119 | goto fail; | ||
120 | } | ||
121 | memset(refblock_data, 0, s->cluster_size); | ||
122 | - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, | ||
123 | + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, | ||
124 | refblock_data); | ||
125 | |||
126 | new_table[i] = block_offset; | ||
127 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | ||
128 | s->set_refcount(refblock_data, j, 1); | ||
129 | } | ||
130 | |||
131 | - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, | ||
132 | + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, | ||
133 | refblock_data); | ||
134 | } | ||
135 | |||
136 | @@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||
137 | } | ||
138 | old_table_index = table_index; | ||
139 | |||
140 | - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, | ||
141 | - refcount_block); | ||
142 | + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); | ||
143 | |||
144 | /* we can update the count and save it */ | ||
145 | block_index = cluster_index & (s->refcount_block_size - 1); | ||
146 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
147 | s->refcount_block_cache); | ||
148 | } | ||
149 | l2_table[j] = cpu_to_be64(entry); | ||
150 | - qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, | ||
151 | - l2_table); | ||
152 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
153 | } | 56 | } |
154 | } | 57 | } |
155 | 58 | aio_context_release(aio_context); | |
156 | @@ -XXX,XX +XXX,XX @@ static int qcow2_discard_refcount_block(BlockDriverState *bs, | 59 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) |
60 | aio_enable_external(aio_context); | ||
61 | bdrv_parent_drained_end(bs); | ||
62 | bdrv_drain_invoke(bs, false); | ||
63 | - bdrv_drain_recurse(bs, false); | ||
64 | + bdrv_drain_recurse(bs); | ||
65 | aio_context_release(aio_context); | ||
157 | } | 66 | } |
158 | s->set_refcount(refblock, block_index, 0); | ||
159 | |||
160 | - qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, refblock); | ||
161 | + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock); | ||
162 | |||
163 | qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
164 | 67 | ||
165 | -- | 68 | -- |
166 | 2.13.6 | 69 | 2.13.6 |
167 | 70 | ||
168 | 71 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | The device is drained, so there is no point in waiting for requests at |
---|---|---|---|
2 | the end of the drained section. Remove the bdrv_drain_recurse() calls | ||
3 | there. | ||
2 | 4 | ||
3 | This function was only using the BlockDriverState parameter to pass it | 5 | The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e |
4 | to qcow2_cache_get_table_addr(). This is no longer necessary so this | 6 | in order to call the .bdrv_co_drain_end() driver callback. This is now |
5 | parameter can be removed. | 7 | done by a separate bdrv_drain_invoke() call. |
6 | 8 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 10 | Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> |
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 11 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
10 | Message-id: eb0ed90affcf302e5a954bafb5931b5215483d3a.1517840877.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | 12 | --- |
13 | block/qcow2.h | 3 +-- | 13 | block/io.c | 2 -- |
14 | block/qcow2-cache.c | 3 +-- | 14 | 1 file changed, 2 deletions(-) |
15 | block/qcow2-refcount.c | 6 +++--- | ||
16 | 3 files changed, 5 insertions(+), 7 deletions(-) | ||
17 | 15 | ||
18 | diff --git a/block/qcow2.h b/block/qcow2.h | 16 | diff --git a/block/io.c b/block/io.c |
19 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/block/qcow2.h | 18 | --- a/block/io.c |
21 | +++ b/block/qcow2.h | 19 | +++ b/block/io.c |
22 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | 20 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) |
23 | int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | 21 | |
24 | void **table); | 22 | bdrv_parent_drained_end(bs); |
25 | void qcow2_cache_put(Qcow2Cache *c, void **table); | 23 | bdrv_drain_invoke(bs, false); |
26 | -void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | 24 | - bdrv_drain_recurse(bs); |
27 | - uint64_t offset); | 25 | aio_enable_external(bdrv_get_aio_context(bs)); |
28 | +void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset); | ||
29 | void qcow2_cache_discard(Qcow2Cache *c, void *table); | ||
30 | |||
31 | /* qcow2-bitmap.c functions */ | ||
32 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/block/qcow2-cache.c | ||
35 | +++ b/block/qcow2-cache.c | ||
36 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) | ||
37 | c->entries[i].dirty = true; | ||
38 | } | 26 | } |
39 | 27 | ||
40 | -void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | 28 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) |
41 | - uint64_t offset) | 29 | aio_enable_external(aio_context); |
42 | +void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset) | 30 | bdrv_parent_drained_end(bs); |
43 | { | 31 | bdrv_drain_invoke(bs, false); |
44 | int i; | 32 | - bdrv_drain_recurse(bs); |
45 | 33 | aio_context_release(aio_context); | |
46 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
47 | index XXXXXXX..XXXXXXX 100644 | ||
48 | --- a/block/qcow2-refcount.c | ||
49 | +++ b/block/qcow2-refcount.c | ||
50 | @@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||
51 | if (refcount == 0) { | ||
52 | void *table; | ||
53 | |||
54 | - table = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, | ||
55 | + table = qcow2_cache_is_table_offset(s->refcount_block_cache, | ||
56 | offset); | ||
57 | if (table != NULL) { | ||
58 | qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||
59 | qcow2_cache_discard(s->refcount_block_cache, table); | ||
60 | } | ||
61 | |||
62 | - table = qcow2_cache_is_table_offset(bs, s->l2_table_cache, offset); | ||
63 | + table = qcow2_cache_is_table_offset(s->l2_table_cache, offset); | ||
64 | if (table != NULL) { | ||
65 | qcow2_cache_discard(s->l2_table_cache, table); | ||
66 | } | ||
67 | @@ -XXX,XX +XXX,XX @@ static int qcow2_discard_refcount_block(BlockDriverState *bs, | ||
68 | s->free_cluster_index = cluster_index; | ||
69 | } | 34 | } |
70 | 35 | ||
71 | - refblock = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, | ||
72 | + refblock = qcow2_cache_is_table_offset(s->refcount_block_cache, | ||
73 | discard_block_offs); | ||
74 | if (refblock) { | ||
75 | /* discard refblock from the cache if refblock is cached */ | ||
76 | -- | 36 | -- |
77 | 2.13.6 | 37 | 2.13.6 |
78 | 38 | ||
79 | 39 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Drain requests are propagated to child nodes, parent nodes and directly |
---|---|---|---|
2 | to the AioContext. The order in which this happened was different | ||
3 | between all combinations of drain/drain_all and begin/end. | ||
2 | 4 | ||
3 | This function doesn't need any changes to support L2 slices, but since | 5 | The correct order is to keep children only drained when their parents |
4 | it's now dealing with slices instead of full tables, the l2_table | 6 | are also drained. This means that at the start of a drained section, the |
5 | variable is renamed for clarity. | 7 | AioContext needs to be drained first, the parents second and only then |
8 | the children. The correct order for the end of a drained section is the | ||
9 | opposite. | ||
6 | 10 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 11 | This patch changes the three other functions to follow the example of |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 12 | bdrv_drained_begin(), which is the only one that got it right. |
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 13 | |
10 | Message-id: 0c5d4b9bf163aa3b49ec19cc512a50d83563f2ad.1517840877.git.berto@igalia.com | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
12 | --- | 16 | --- |
13 | block/qcow2-cluster.c | 16 ++++++++-------- | 17 | block/io.c | 12 ++++++++---- |
14 | 1 file changed, 8 insertions(+), 8 deletions(-) | 18 | 1 file changed, 8 insertions(+), 4 deletions(-) |
15 | 19 | ||
16 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | 20 | diff --git a/block/io.c b/block/io.c |
17 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/qcow2-cluster.c | 22 | --- a/block/io.c |
19 | +++ b/block/qcow2-cluster.c | 23 | +++ b/block/io.c |
20 | @@ -XXX,XX +XXX,XX @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | 24 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) |
21 | { | 25 | return; |
22 | BDRVQcow2State *s = bs->opaque; | ||
23 | int l2_index, ret; | ||
24 | - uint64_t *l2_table; | ||
25 | + uint64_t *l2_slice; | ||
26 | int64_t cluster_offset; | ||
27 | int nb_csectors; | ||
28 | |||
29 | - ret = get_cluster_table(bs, offset, &l2_table, &l2_index); | ||
30 | + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); | ||
31 | if (ret < 0) { | ||
32 | return 0; | ||
33 | } | 26 | } |
34 | 27 | ||
35 | /* Compression can't overwrite anything. Fail if the cluster was already | 28 | + /* Stop things in parent-to-child order */ |
36 | * allocated. */ | 29 | if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { |
37 | - cluster_offset = be64_to_cpu(l2_table[l2_index]); | 30 | aio_disable_external(bdrv_get_aio_context(bs)); |
38 | + cluster_offset = be64_to_cpu(l2_slice[l2_index]); | 31 | bdrv_parent_drained_begin(bs); |
39 | if (cluster_offset & L2E_OFFSET_MASK) { | 32 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) |
40 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | 33 | return; |
41 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
42 | return 0; | ||
43 | } | 34 | } |
44 | 35 | ||
45 | cluster_offset = qcow2_alloc_bytes(bs, compressed_size); | 36 | - bdrv_parent_drained_end(bs); |
46 | if (cluster_offset < 0) { | 37 | + /* Re-enable things in child-to-parent order */ |
47 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | 38 | bdrv_drain_invoke(bs, false); |
48 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | 39 | + bdrv_parent_drained_end(bs); |
49 | return 0; | 40 | aio_enable_external(bdrv_get_aio_context(bs)); |
41 | } | ||
42 | |||
43 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
44 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
45 | AioContext *aio_context = bdrv_get_aio_context(bs); | ||
46 | |||
47 | + /* Stop things in parent-to-child order */ | ||
48 | aio_context_acquire(aio_context); | ||
49 | - bdrv_parent_drained_begin(bs); | ||
50 | aio_disable_external(aio_context); | ||
51 | + bdrv_parent_drained_begin(bs); | ||
52 | bdrv_drain_invoke(bs, true); | ||
53 | aio_context_release(aio_context); | ||
54 | |||
55 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
56 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
57 | AioContext *aio_context = bdrv_get_aio_context(bs); | ||
58 | |||
59 | + /* Re-enable things in child-to-parent order */ | ||
60 | aio_context_acquire(aio_context); | ||
61 | - aio_enable_external(aio_context); | ||
62 | - bdrv_parent_drained_end(bs); | ||
63 | bdrv_drain_invoke(bs, false); | ||
64 | + bdrv_parent_drained_end(bs); | ||
65 | + aio_enable_external(aio_context); | ||
66 | aio_context_release(aio_context); | ||
50 | } | 67 | } |
51 | 68 | ||
52 | @@ -XXX,XX +XXX,XX @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||
53 | /* compressed clusters never have the copied flag */ | ||
54 | |||
55 | BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); | ||
56 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
57 | - l2_table[l2_index] = cpu_to_be64(cluster_offset); | ||
58 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
59 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | ||
60 | + l2_slice[l2_index] = cpu_to_be64(cluster_offset); | ||
61 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
62 | |||
63 | return cluster_offset; | ||
64 | } | ||
65 | -- | 69 | -- |
66 | 2.13.6 | 70 | 2.13.6 |
67 | 71 | ||
68 | 72 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Commit 15afd94a047 added code to acquire and release the AioContext in |
---|---|---|---|
2 | qemuio_command(). This means that the lock is taken twice now in the | ||
3 | call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for | ||
4 | any requests issued to nodes in a non-mainloop AioContext. | ||
2 | 5 | ||
3 | expand_zero_clusters_in_l1() expands zero clusters as a necessary step | 6 | Dropping the first locking from hmp_qemu_io() fixes the problem. |
4 | to downgrade qcow2 images to a version that doesn't support metadata | ||
5 | zero clusters. This function takes an L1 table (which may or may not | ||
6 | be active) and iterates over all its L2 tables looking for zero | ||
7 | clusters. | ||
8 | 7 | ||
9 | Since we'll be loading L2 slices instead of full tables we need to add | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | an extra loop that iterates over all slices of each L2 table, and we | 9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
11 | should also use the slice size when allocating the buffer used when | 10 | --- |
12 | the L1 table is not active. | 11 | hmp.c | 6 ------ |
12 | 1 file changed, 6 deletions(-) | ||
13 | 13 | ||
14 | This function doesn't need any additional changes so apart from that | 14 | diff --git a/hmp.c b/hmp.c |
15 | this patch simply updates the variable name from l2_table to l2_slice. | ||
16 | |||
17 | Finally, and since we have to touch the bdrv_read() / bdrv_write() | ||
18 | calls anyway, this patch takes the opportunity to replace them with | ||
19 | the byte-based bdrv_pread() / bdrv_pwrite(). | ||
20 | |||
21 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
22 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
23 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
24 | Message-id: 43590976f730501688096cff103f2923b72b0f32.1517840877.git.berto@igalia.com | ||
25 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
26 | --- | ||
27 | block/qcow2-cluster.c | 51 ++++++++++++++++++++++++++++----------------------- | ||
28 | 1 file changed, 28 insertions(+), 23 deletions(-) | ||
29 | |||
30 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
32 | --- a/block/qcow2-cluster.c | 16 | --- a/hmp.c |
33 | +++ b/block/qcow2-cluster.c | 17 | +++ b/hmp.c |
34 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | 18 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) |
35 | { | 19 | { |
36 | BDRVQcow2State *s = bs->opaque; | 20 | BlockBackend *blk; |
37 | bool is_active_l1 = (l1_table == s->l1_table); | 21 | BlockBackend *local_blk = NULL; |
38 | - uint64_t *l2_table = NULL; | 22 | - AioContext *aio_context; |
39 | + uint64_t *l2_slice = NULL; | 23 | const char* device = qdict_get_str(qdict, "device"); |
40 | + unsigned slice, slice_size2, n_slices; | 24 | const char* command = qdict_get_str(qdict, "command"); |
41 | int ret; | 25 | Error *err = NULL; |
42 | int i, j; | 26 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) |
43 | |||
44 | + slice_size2 = s->l2_slice_size * sizeof(uint64_t); | ||
45 | + n_slices = s->cluster_size / slice_size2; | ||
46 | + | ||
47 | if (!is_active_l1) { | ||
48 | /* inactive L2 tables require a buffer to be stored in when loading | ||
49 | * them from disk */ | ||
50 | - l2_table = qemu_try_blockalign(bs->file->bs, s->cluster_size); | ||
51 | - if (l2_table == NULL) { | ||
52 | + l2_slice = qemu_try_blockalign(bs->file->bs, slice_size2); | ||
53 | + if (l2_slice == NULL) { | ||
54 | return -ENOMEM; | ||
55 | } | 27 | } |
56 | } | 28 | } |
57 | 29 | ||
58 | for (i = 0; i < l1_size; i++) { | 30 | - aio_context = blk_get_aio_context(blk); |
59 | uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; | 31 | - aio_context_acquire(aio_context); |
60 | - bool l2_dirty = false; | 32 | - |
61 | uint64_t l2_refcount; | 33 | /* |
62 | 34 | * Notably absent: Proper permission management. This is sad, but it seems | |
63 | if (!l2_offset) { | 35 | * almost impossible to achieve without changing the semantics and thereby |
64 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | 36 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) |
65 | goto fail; | 37 | */ |
66 | } | 38 | qemuio_command(blk, command); |
67 | 39 | ||
68 | - { | 40 | - aio_context_release(aio_context); |
69 | + for (slice = 0; slice < n_slices; slice++) { | 41 | - |
70 | + uint64_t slice_offset = l2_offset + slice * slice_size2; | ||
71 | + bool l2_dirty = false; | ||
72 | if (is_active_l1) { | ||
73 | /* get active L2 tables from cache */ | ||
74 | - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, | ||
75 | - (void **)&l2_table); | ||
76 | + ret = qcow2_cache_get(bs, s->l2_table_cache, slice_offset, | ||
77 | + (void **)&l2_slice); | ||
78 | } else { | ||
79 | /* load inactive L2 tables from disk */ | ||
80 | - ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||
81 | - (void *)l2_table, s->cluster_sectors); | ||
82 | + ret = bdrv_pread(bs->file, slice_offset, l2_slice, slice_size2); | ||
83 | } | ||
84 | if (ret < 0) { | ||
85 | goto fail; | ||
86 | } | ||
87 | |||
88 | - for (j = 0; j < s->l2_size; j++) { | ||
89 | - uint64_t l2_entry = be64_to_cpu(l2_table[j]); | ||
90 | + for (j = 0; j < s->l2_slice_size; j++) { | ||
91 | + uint64_t l2_entry = be64_to_cpu(l2_slice[j]); | ||
92 | int64_t offset = l2_entry & L2E_OFFSET_MASK; | ||
93 | QCow2ClusterType cluster_type = | ||
94 | qcow2_get_cluster_type(l2_entry); | ||
95 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
96 | if (!bs->backing) { | ||
97 | /* not backed; therefore we can simply deallocate the | ||
98 | * cluster */ | ||
99 | - l2_table[j] = 0; | ||
100 | + l2_slice[j] = 0; | ||
101 | l2_dirty = true; | ||
102 | continue; | ||
103 | } | ||
104 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
105 | } | ||
106 | |||
107 | if (offset_into_cluster(s, offset)) { | ||
108 | + int l2_index = slice * s->l2_slice_size + j; | ||
109 | qcow2_signal_corruption( | ||
110 | bs, true, -1, -1, | ||
111 | "Cluster allocation offset " | ||
112 | "%#" PRIx64 " unaligned (L2 offset: %#" | ||
113 | PRIx64 ", L2 index: %#x)", offset, | ||
114 | - l2_offset, j); | ||
115 | + l2_offset, l2_index); | ||
116 | if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { | ||
117 | qcow2_free_clusters(bs, offset, s->cluster_size, | ||
118 | QCOW2_DISCARD_ALWAYS); | ||
119 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
120 | } | ||
121 | |||
122 | if (l2_refcount == 1) { | ||
123 | - l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); | ||
124 | + l2_slice[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); | ||
125 | } else { | ||
126 | - l2_table[j] = cpu_to_be64(offset); | ||
127 | + l2_slice[j] = cpu_to_be64(offset); | ||
128 | } | ||
129 | l2_dirty = true; | ||
130 | } | ||
131 | |||
132 | if (is_active_l1) { | ||
133 | if (l2_dirty) { | ||
134 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
135 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | ||
136 | qcow2_cache_depends_on_flush(s->l2_table_cache); | ||
137 | } | ||
138 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
139 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
140 | } else { | ||
141 | if (l2_dirty) { | ||
142 | ret = qcow2_pre_write_overlap_check( | ||
143 | bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, | ||
144 | - l2_offset, s->cluster_size); | ||
145 | + slice_offset, slice_size2); | ||
146 | if (ret < 0) { | ||
147 | goto fail; | ||
148 | } | ||
149 | |||
150 | - ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||
151 | - (void *)l2_table, s->cluster_sectors); | ||
152 | + ret = bdrv_pwrite(bs->file, slice_offset, | ||
153 | + l2_slice, slice_size2); | ||
154 | if (ret < 0) { | ||
155 | goto fail; | ||
156 | } | ||
157 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
158 | ret = 0; | ||
159 | |||
160 | fail: | 42 | fail: |
161 | - if (l2_table) { | 43 | blk_unref(local_blk); |
162 | + if (l2_slice) { | 44 | hmp_handle_error(mon, &err); |
163 | if (!is_active_l1) { | ||
164 | - qemu_vfree(l2_table); | ||
165 | + qemu_vfree(l2_slice); | ||
166 | } else { | ||
167 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
168 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
169 | } | ||
170 | } | ||
171 | return ret; | ||
172 | -- | 45 | -- |
173 | 2.13.6 | 46 | 2.13.6 |
174 | 47 | ||
175 | 48 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | This function was never using the BlockDriverState parameter so it can | 3 | Since bdrv_co_preadv does all neccessary checks including |
4 | be safely removed. | 4 | reading after the end of the backing file, avoid duplication |
5 | of verification before bdrv_co_preadv call. | ||
5 | 6 | ||
6 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 7 | Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com> |
8 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | Message-id: 49c74fe8b3aead9056e61a85b145ce787d06262b.1517840876.git.berto@igalia.com | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
11 | --- | 11 | --- |
12 | block/qcow2.h | 2 +- | 12 | block/qcow2.h | 3 --- |
13 | block/qcow2-cache.c | 2 +- | 13 | block/qcow2.c | 51 ++++++++------------------------------------------- |
14 | block/qcow2.c | 16 ++++++++-------- | 14 | 2 files changed, 8 insertions(+), 46 deletions(-) |
15 | 3 files changed, 10 insertions(+), 10 deletions(-) | ||
16 | 15 | ||
17 | diff --git a/block/qcow2.h b/block/qcow2.h | 16 | diff --git a/block/qcow2.h b/block/qcow2.h |
18 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block/qcow2.h | 18 | --- a/block/qcow2.h |
20 | +++ b/block/qcow2.h | 19 | +++ b/block/qcow2.h |
21 | @@ -XXX,XX +XXX,XX @@ int qcow2_read_snapshots(BlockDriverState *bs); | 20 | @@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset) |
22 | |||
23 | /* qcow2-cache.c functions */ | ||
24 | Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); | ||
25 | -int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); | ||
26 | +int qcow2_cache_destroy(Qcow2Cache *c); | ||
27 | |||
28 | void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); | ||
29 | int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); | ||
30 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | ||
32 | --- a/block/qcow2-cache.c | ||
33 | +++ b/block/qcow2-cache.c | ||
34 | @@ -XXX,XX +XXX,XX @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) | ||
35 | return c; | ||
36 | } | 21 | } |
37 | 22 | ||
38 | -int qcow2_cache_destroy(BlockDriverState *bs, Qcow2Cache *c) | 23 | /* qcow2.c functions */ |
39 | +int qcow2_cache_destroy(Qcow2Cache *c) | 24 | -int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, |
40 | { | 25 | - int64_t sector_num, int nb_sectors); |
41 | int i; | 26 | - |
42 | 27 | int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, | |
28 | int refcount_order, bool generous_increase, | ||
29 | uint64_t *refblock_count); | ||
43 | diff --git a/block/qcow2.c b/block/qcow2.c | 30 | diff --git a/block/qcow2.c b/block/qcow2.c |
44 | index XXXXXXX..XXXXXXX 100644 | 31 | index XXXXXXX..XXXXXXX 100644 |
45 | --- a/block/qcow2.c | 32 | --- a/block/qcow2.c |
46 | +++ b/block/qcow2.c | 33 | +++ b/block/qcow2.c |
47 | @@ -XXX,XX +XXX,XX @@ static void qcow2_update_options_commit(BlockDriverState *bs, | 34 | @@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, |
48 | int i; | 35 | return status; |
49 | 36 | } | |
50 | if (s->l2_table_cache) { | 37 | |
51 | - qcow2_cache_destroy(bs, s->l2_table_cache); | 38 | -/* handle reading after the end of the backing file */ |
52 | + qcow2_cache_destroy(s->l2_table_cache); | 39 | -int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, |
53 | } | 40 | - int64_t offset, int bytes) |
54 | if (s->refcount_block_cache) { | 41 | -{ |
55 | - qcow2_cache_destroy(bs, s->refcount_block_cache); | 42 | - uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE; |
56 | + qcow2_cache_destroy(s->refcount_block_cache); | 43 | - int n1; |
57 | } | 44 | - |
58 | s->l2_table_cache = r->l2_table_cache; | 45 | - if ((offset + bytes) <= bs_size) { |
59 | s->refcount_block_cache = r->refcount_block_cache; | 46 | - return bytes; |
60 | @@ -XXX,XX +XXX,XX @@ static void qcow2_update_options_abort(BlockDriverState *bs, | 47 | - } |
61 | Qcow2ReopenState *r) | 48 | - |
49 | - if (offset >= bs_size) { | ||
50 | - n1 = 0; | ||
51 | - } else { | ||
52 | - n1 = bs_size - offset; | ||
53 | - } | ||
54 | - | ||
55 | - qemu_iovec_memset(qiov, n1, 0, bytes - n1); | ||
56 | - | ||
57 | - return n1; | ||
58 | -} | ||
59 | - | ||
60 | static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, | ||
61 | uint64_t bytes, QEMUIOVector *qiov, | ||
62 | int flags) | ||
62 | { | 63 | { |
63 | if (r->l2_table_cache) { | 64 | BDRVQcow2State *s = bs->opaque; |
64 | - qcow2_cache_destroy(bs, r->l2_table_cache); | 65 | - int offset_in_cluster, n1; |
65 | + qcow2_cache_destroy(r->l2_table_cache); | 66 | + int offset_in_cluster; |
66 | } | 67 | int ret; |
67 | if (r->refcount_block_cache) { | 68 | unsigned int cur_bytes; /* number of bytes in current iteration */ |
68 | - qcow2_cache_destroy(bs, r->refcount_block_cache); | 69 | uint64_t cluster_offset = 0; |
69 | + qcow2_cache_destroy(r->refcount_block_cache); | 70 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, |
70 | } | 71 | case QCOW2_CLUSTER_UNALLOCATED: |
71 | qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); | 72 | |
72 | } | 73 | if (bs->backing) { |
73 | @@ -XXX,XX +XXX,XX @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, | 74 | - /* read from the base image */ |
74 | s->l1_table = NULL; | 75 | - n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov, |
75 | cache_clean_timer_del(bs); | 76 | - offset, cur_bytes); |
76 | if (s->l2_table_cache) { | 77 | - if (n1 > 0) { |
77 | - qcow2_cache_destroy(bs, s->l2_table_cache); | 78 | - QEMUIOVector local_qiov; |
78 | + qcow2_cache_destroy(s->l2_table_cache); | 79 | - |
79 | } | 80 | - qemu_iovec_init(&local_qiov, hd_qiov.niov); |
80 | if (s->refcount_block_cache) { | 81 | - qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1); |
81 | - qcow2_cache_destroy(bs, s->refcount_block_cache); | 82 | - |
82 | + qcow2_cache_destroy(s->refcount_block_cache); | 83 | - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); |
83 | } | 84 | - qemu_co_mutex_unlock(&s->lock); |
84 | qcrypto_block_free(s->crypto); | 85 | - ret = bdrv_co_preadv(bs->backing, offset, n1, |
85 | qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); | 86 | - &local_qiov, 0); |
86 | @@ -XXX,XX +XXX,XX @@ static void qcow2_close(BlockDriverState *bs) | 87 | - qemu_co_mutex_lock(&s->lock); |
87 | } | 88 | - |
88 | 89 | - qemu_iovec_destroy(&local_qiov); | |
89 | cache_clean_timer_del(bs); | 90 | - |
90 | - qcow2_cache_destroy(bs, s->l2_table_cache); | 91 | - if (ret < 0) { |
91 | - qcow2_cache_destroy(bs, s->refcount_block_cache); | 92 | - goto fail; |
92 | + qcow2_cache_destroy(s->l2_table_cache); | 93 | - } |
93 | + qcow2_cache_destroy(s->refcount_block_cache); | 94 | + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); |
94 | 95 | + qemu_co_mutex_unlock(&s->lock); | |
95 | qcrypto_block_free(s->crypto); | 96 | + ret = bdrv_co_preadv(bs->backing, offset, cur_bytes, |
96 | s->crypto = NULL; | 97 | + &hd_qiov, 0); |
98 | + qemu_co_mutex_lock(&s->lock); | ||
99 | + if (ret < 0) { | ||
100 | + goto fail; | ||
101 | } | ||
102 | } else { | ||
103 | /* Note: in this case, no need to wait */ | ||
97 | -- | 104 | -- |
98 | 2.13.6 | 105 | 2.13.6 |
99 | 106 | ||
100 | 107 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | Removing a quorum child node with x-blockdev-change results in a quorum |
---|---|---|---|
2 | driver state that cannot be recreated with create options because it | ||
3 | would require a list with gaps. This causes trouble in at least | ||
4 | .bdrv_refresh_filename(). | ||
2 | 5 | ||
3 | To maintain load/store disabled bitmap there is new approach: | 6 | Document this problem so that we won't accidentally mark the command |
7 | stable without having addressed it. | ||
4 | 8 | ||
5 | - deprecate @autoload flag of block-dirty-bitmap-add, make it ignored | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | - store enabled bitmaps as "auto" to qcow2 | 10 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
7 | - store disabled bitmaps without "auto" flag to qcow2 | ||
8 | - on qcow2 open load "auto" bitmaps as enabled and others | ||
9 | as disabled (except in_use bitmaps) | ||
10 | |||
11 | Also, adjust iotests 165 and 176 appropriately. | ||
12 | |||
13 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
14 | Message-id: 20180202160752.143796-1-vsementsov@virtuozzo.com | ||
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
16 | --- | 11 | --- |
17 | qapi/block-core.json | 6 +++--- | 12 | qapi/block-core.json | 4 ++++ |
18 | block/qcow2.h | 2 +- | 13 | 1 file changed, 4 insertions(+) |
19 | include/block/dirty-bitmap.h | 1 - | ||
20 | block/dirty-bitmap.c | 18 ------------------ | ||
21 | block/qcow2-bitmap.c | 12 +++++++----- | ||
22 | block/qcow2.c | 2 +- | ||
23 | blockdev.c | 10 ++-------- | ||
24 | qemu-doc.texi | 7 +++++++ | ||
25 | tests/qemu-iotests/165 | 2 +- | ||
26 | tests/qemu-iotests/176 | 2 +- | ||
27 | 10 files changed, 23 insertions(+), 39 deletions(-) | ||
28 | 14 | ||
29 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 15 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
30 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/qapi/block-core.json | 17 | --- a/qapi/block-core.json |
32 | +++ b/qapi/block-core.json | 18 | +++ b/qapi/block-core.json |
33 | @@ -XXX,XX +XXX,XX @@ | 19 | @@ -XXX,XX +XXX,XX @@ |
34 | # Qcow2 disks support persistent bitmaps. Default is false for | 20 | # does not support all kinds of operations, all kinds of children, nor |
35 | # block-dirty-bitmap-add. (Since: 2.10) | 21 | # all block drivers. |
36 | # | 22 | # |
37 | -# @autoload: the bitmap will be automatically loaded when the image it is stored | 23 | +# FIXME Removing children from a quorum node means introducing gaps in the |
38 | -# in is opened. This flag may only be specified for persistent | 24 | +# child indices. This cannot be represented in the 'children' list of |
39 | -# bitmaps. Default is false for block-dirty-bitmap-add. (Since: 2.10) | 25 | +# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename(). |
40 | +# @autoload: ignored and deprecated since 2.12. | 26 | +# |
41 | +# Currently, all dirty tracking bitmaps are loaded from Qcow2 on | 27 | # Warning: The data in a new quorum child MUST be consistent with that of |
42 | +# open. | 28 | # the rest of the array. |
43 | # | 29 | # |
44 | # Since: 2.4 | ||
45 | ## | ||
46 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
47 | index XXXXXXX..XXXXXXX 100644 | ||
48 | --- a/block/qcow2.h | ||
49 | +++ b/block/qcow2.h | ||
50 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table); | ||
51 | int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||
52 | void **refcount_table, | ||
53 | int64_t *refcount_table_size); | ||
54 | -bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||
55 | +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||
56 | int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); | ||
57 | void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||
58 | int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); | ||
59 | diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h | ||
60 | index XXXXXXX..XXXXXXX 100644 | ||
61 | --- a/include/block/dirty-bitmap.h | ||
62 | +++ b/include/block/dirty-bitmap.h | ||
63 | @@ -XXX,XX +XXX,XX @@ void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, | ||
64 | void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); | ||
65 | |||
66 | void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); | ||
67 | -void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload); | ||
68 | void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, | ||
69 | bool persistent); | ||
70 | |||
71 | diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c | ||
72 | index XXXXXXX..XXXXXXX 100644 | ||
73 | --- a/block/dirty-bitmap.c | ||
74 | +++ b/block/dirty-bitmap.c | ||
75 | @@ -XXX,XX +XXX,XX @@ struct BdrvDirtyBitmap { | ||
76 | Such operations must fail and both the image | ||
77 | and this bitmap must remain unchanged while | ||
78 | this flag is set. */ | ||
79 | - bool autoload; /* For persistent bitmaps: bitmap must be | ||
80 | - autoloaded on image opening */ | ||
81 | bool persistent; /* bitmap must be saved to owner disk image */ | ||
82 | QLIST_ENTRY(BdrvDirtyBitmap) list; | ||
83 | }; | ||
84 | @@ -XXX,XX +XXX,XX @@ void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) | ||
85 | g_free(bitmap->name); | ||
86 | bitmap->name = NULL; | ||
87 | bitmap->persistent = false; | ||
88 | - bitmap->autoload = false; | ||
89 | } | ||
90 | |||
91 | /* Called with BQL taken. */ | ||
92 | @@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, | ||
93 | bitmap->successor = NULL; | ||
94 | successor->persistent = bitmap->persistent; | ||
95 | bitmap->persistent = false; | ||
96 | - successor->autoload = bitmap->autoload; | ||
97 | - bitmap->autoload = false; | ||
98 | bdrv_release_dirty_bitmap(bs, bitmap); | ||
99 | |||
100 | return successor; | ||
101 | @@ -XXX,XX +XXX,XX @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) | ||
102 | } | ||
103 | |||
104 | /* Called with BQL taken. */ | ||
105 | -void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload) | ||
106 | -{ | ||
107 | - qemu_mutex_lock(bitmap->mutex); | ||
108 | - bitmap->autoload = autoload; | ||
109 | - qemu_mutex_unlock(bitmap->mutex); | ||
110 | -} | ||
111 | - | ||
112 | -bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap) | ||
113 | -{ | ||
114 | - return bitmap->autoload; | ||
115 | -} | ||
116 | - | ||
117 | -/* Called with BQL taken. */ | ||
118 | void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) | ||
119 | { | ||
120 | qemu_mutex_lock(bitmap->mutex); | ||
121 | diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c | ||
122 | index XXXXXXX..XXXXXXX 100644 | ||
123 | --- a/block/qcow2-bitmap.c | ||
124 | +++ b/block/qcow2-bitmap.c | ||
125 | @@ -XXX,XX +XXX,XX @@ static void set_readonly_helper(gpointer bitmap, gpointer value) | ||
126 | bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); | ||
127 | } | ||
128 | |||
129 | -/* qcow2_load_autoloading_dirty_bitmaps() | ||
130 | +/* qcow2_load_dirty_bitmaps() | ||
131 | * Return value is a hint for caller: true means that the Qcow2 header was | ||
132 | * updated. (false doesn't mean that the header should be updated by the | ||
133 | * caller, it just means that updating was not needed or the image cannot be | ||
134 | * written to). | ||
135 | * On failure the function returns false. | ||
136 | */ | ||
137 | -bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp) | ||
138 | +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) | ||
139 | { | ||
140 | BDRVQcow2State *s = bs->opaque; | ||
141 | Qcow2BitmapList *bm_list; | ||
142 | @@ -XXX,XX +XXX,XX @@ bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp) | ||
143 | } | ||
144 | |||
145 | QSIMPLEQ_FOREACH(bm, bm_list, entry) { | ||
146 | - if ((bm->flags & BME_FLAG_AUTO) && !(bm->flags & BME_FLAG_IN_USE)) { | ||
147 | + if (!(bm->flags & BME_FLAG_IN_USE)) { | ||
148 | BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); | ||
149 | if (bitmap == NULL) { | ||
150 | goto fail; | ||
151 | } | ||
152 | |||
153 | + if (!(bm->flags & BME_FLAG_AUTO)) { | ||
154 | + bdrv_disable_dirty_bitmap(bitmap); | ||
155 | + } | ||
156 | bdrv_dirty_bitmap_set_persistance(bitmap, true); | ||
157 | - bdrv_dirty_bitmap_set_autoload(bitmap, true); | ||
158 | bm->flags |= BME_FLAG_IN_USE; | ||
159 | created_dirty_bitmaps = | ||
160 | g_slist_append(created_dirty_bitmaps, bitmap); | ||
161 | @@ -XXX,XX +XXX,XX @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) | ||
162 | bm->table.size = 0; | ||
163 | QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); | ||
164 | } | ||
165 | - bm->flags = bdrv_dirty_bitmap_get_autoload(bitmap) ? BME_FLAG_AUTO : 0; | ||
166 | + bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0; | ||
167 | bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap)); | ||
168 | bm->dirty_bitmap = bitmap; | ||
169 | } | ||
170 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
171 | index XXXXXXX..XXXXXXX 100644 | ||
172 | --- a/block/qcow2.c | ||
173 | +++ b/block/qcow2.c | ||
174 | @@ -XXX,XX +XXX,XX @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, | ||
175 | s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; | ||
176 | } | ||
177 | |||
178 | - if (qcow2_load_autoloading_dirty_bitmaps(bs, &local_err)) { | ||
179 | + if (qcow2_load_dirty_bitmaps(bs, &local_err)) { | ||
180 | update_header = false; | ||
181 | } | ||
182 | if (local_err != NULL) { | ||
183 | diff --git a/blockdev.c b/blockdev.c | ||
184 | index XXXXXXX..XXXXXXX 100644 | ||
185 | --- a/blockdev.c | ||
186 | +++ b/blockdev.c | ||
187 | @@ -XXX,XX +XXX,XX @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||
188 | if (!has_persistent) { | ||
189 | persistent = false; | ||
190 | } | ||
191 | - if (!has_autoload) { | ||
192 | - autoload = false; | ||
193 | - } | ||
194 | |||
195 | - if (has_autoload && !persistent) { | ||
196 | - error_setg(errp, "Autoload flag must be used only for persistent " | ||
197 | - "bitmaps"); | ||
198 | - return; | ||
199 | + if (has_autoload) { | ||
200 | + warn_report("Autoload option is deprecated and its value is ignored"); | ||
201 | } | ||
202 | |||
203 | if (persistent && | ||
204 | @@ -XXX,XX +XXX,XX @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||
205 | } | ||
206 | |||
207 | bdrv_dirty_bitmap_set_persistance(bitmap, persistent); | ||
208 | - bdrv_dirty_bitmap_set_autoload(bitmap, autoload); | ||
209 | } | ||
210 | |||
211 | void qmp_block_dirty_bitmap_remove(const char *node, const char *name, | ||
212 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
213 | index XXXXXXX..XXXXXXX 100644 | ||
214 | --- a/qemu-doc.texi | ||
215 | +++ b/qemu-doc.texi | ||
216 | @@ -XXX,XX +XXX,XX @@ used and it will be removed with no replacement. | ||
217 | The ``convert -s snapshot_id_or_name'' argument is obsoleted | ||
218 | by the ``convert -l snapshot_param'' argument instead. | ||
219 | |||
220 | +@section QEMU Machine Protocol (QMP) commands | ||
221 | + | ||
222 | +@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) | ||
223 | + | ||
224 | +"autoload" parameter is now ignored. All bitmaps are automatically loaded | ||
225 | +from qcow2 images. | ||
226 | + | ||
227 | @section System emulator human monitor commands | ||
228 | |||
229 | @subsection host_net_add (since 2.10.0) | ||
230 | diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 | ||
231 | index XXXXXXX..XXXXXXX 100755 | ||
232 | --- a/tests/qemu-iotests/165 | ||
233 | +++ b/tests/qemu-iotests/165 | ||
234 | @@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): | ||
235 | |||
236 | def qmpAddBitmap(self): | ||
237 | self.vm.qmp('block-dirty-bitmap-add', node='drive0', | ||
238 | - name='bitmap0', persistent=True, autoload=True) | ||
239 | + name='bitmap0', persistent=True) | ||
240 | |||
241 | def test_persistent(self): | ||
242 | self.vm = self.mkVm() | ||
243 | diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 | ||
244 | index XXXXXXX..XXXXXXX 100755 | ||
245 | --- a/tests/qemu-iotests/176 | ||
246 | +++ b/tests/qemu-iotests/176 | ||
247 | @@ -XXX,XX +XXX,XX @@ case $reason in | ||
248 | "file": { "driver": "file", "filename": "$TEST_IMG" } } } | ||
249 | { "execute": "block-dirty-bitmap-add", | ||
250 | "arguments": { "node": "drive0", "name": "bitmap0", | ||
251 | - "persistent": true, "autoload": true } } | ||
252 | + "persistent": true } } | ||
253 | { "execute": "quit" } | ||
254 | EOF | ||
255 | ;; | ||
256 | -- | 30 | -- |
257 | 2.13.6 | 31 | 2.13.6 |
258 | 32 | ||
259 | 33 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Doug Gale <doug16k@gmail.com> |
---|---|---|---|
2 | 2 | ||
3 | Split options out of the "@table @var" section and create a "@table | 3 | Add trace output for commands, errors, and undefined behavior. |
4 | @option", then use whitespaces and blank lines consistently. | 4 | Add guest error log output for undefined behavior. |
5 | Report invalid undefined accesses to MMIO. | ||
6 | Annotate unlikely error checks with unlikely. | ||
5 | 7 | ||
6 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Doug Gale <doug16k@gmail.com> |
7 | Signed-off-by: Fam Zheng <famz@redhat.com> | 9 | Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> |
8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
9 | Reviewed-by: Kashyap Chamarthy <kchamart@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 12 | --- |
12 | qemu-img.texi | 66 +++++++++++++++++++++++++++++++++++------------------------ | 13 | hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++-------- |
13 | 1 file changed, 39 insertions(+), 27 deletions(-) | 14 | hw/block/trace-events | 93 ++++++++++++++ |
15 | 2 files changed, 390 insertions(+), 52 deletions(-) | ||
14 | 16 | ||
15 | diff --git a/qemu-img.texi b/qemu-img.texi | 17 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c |
16 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/qemu-img.texi | 19 | --- a/hw/block/nvme.c |
18 | +++ b/qemu-img.texi | 20 | +++ b/hw/block/nvme.c |
19 | @@ -XXX,XX +XXX,XX @@ The following commands are supported: | 21 | @@ -XXX,XX +XXX,XX @@ |
20 | 22 | #include "qapi/visitor.h" | |
21 | Command parameters: | 23 | #include "sysemu/block-backend.h" |
22 | @table @var | 24 | |
23 | -@item filename | 25 | +#include "qemu/log.h" |
24 | - is a disk image filename | 26 | +#include "trace.h" |
25 | - | 27 | #include "nvme.h" |
26 | -@item --object @var{objectdef} | 28 | |
27 | - | 29 | +#define NVME_GUEST_ERR(trace, fmt, ...) \ |
28 | -is a QEMU user creatable object definition. See the @code{qemu(1)} manual | 30 | + do { \ |
29 | -page for a description of the object properties. The most common object | 31 | + (trace_##trace)(__VA_ARGS__); \ |
30 | -type is a @code{secret}, which is used to supply passwords and/or encryption | 32 | + qemu_log_mask(LOG_GUEST_ERROR, #trace \ |
31 | -keys. | 33 | + " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \ |
32 | - | 34 | + } while (0) |
33 | -@item --image-opts | 35 | + |
34 | - | 36 | static void nvme_process_sq(void *opaque); |
35 | -Indicates that the source @var{filename} parameter is to be interpreted as a | 37 | |
36 | -full option string, not a plain filename. This parameter is mutually | 38 | static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) |
37 | -exclusive with the @var{-f} parameter. | 39 | @@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) |
38 | - | 40 | { |
39 | -@item --target-image-opts | 41 | if (cq->irq_enabled) { |
40 | 42 | if (msix_enabled(&(n->parent_obj))) { | |
41 | -Indicates that the @var{output_filename} parameter(s) are to be interpreted as | 43 | + trace_nvme_irq_msix(cq->vector); |
42 | -a full option string, not a plain filename. This parameter is mutually | 44 | msix_notify(&(n->parent_obj), cq->vector); |
43 | -exclusive with the @var{-O} parameters. It is currently required to also use | 45 | } else { |
44 | -the @var{-n} parameter to skip image creation. This restriction may be relaxed | 46 | + trace_nvme_irq_pin(); |
45 | -in a future release. | 47 | pci_irq_pulse(&n->parent_obj); |
46 | +@item filename | 48 | } |
47 | +is a disk image filename | 49 | + } else { |
48 | 50 | + trace_nvme_irq_masked(); | |
49 | @item fmt | 51 | } |
50 | is the disk image format. It is guessed automatically in most cases. See below | 52 | } |
51 | for a description of the supported disk formats. | 53 | |
52 | 54 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | |
53 | -@item --backing-chain | 55 | trans_len = MIN(len, trans_len); |
54 | -will enumerate information about backing files in a disk image chain. Refer | 56 | int num_prps = (len >> n->page_bits) + 1; |
55 | -below for further description. | 57 | |
56 | - | 58 | - if (!prp1) { |
57 | @item size | 59 | + if (unlikely(!prp1)) { |
58 | is the disk image size in bytes. Optional suffixes @code{k} or @code{K} | 60 | + trace_nvme_err_invalid_prp(); |
59 | (kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M) | 61 | return NVME_INVALID_FIELD | NVME_DNR; |
60 | @@ -XXX,XX +XXX,XX @@ and T (terabyte, 1024G) are supported. @code{b} is ignored. | 62 | } else if (n->cmbsz && prp1 >= n->ctrl_mem.addr && |
61 | is the destination disk image filename | 63 | prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) { |
62 | 64 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | |
63 | @item output_fmt | 65 | } |
64 | - is the destination format | 66 | len -= trans_len; |
65 | +is the destination format | 67 | if (len) { |
66 | + | 68 | - if (!prp2) { |
67 | @item options | 69 | + if (unlikely(!prp2)) { |
68 | is a comma separated list of format specific options in a | 70 | + trace_nvme_err_invalid_prp2_missing(); |
69 | name=value format. Use @code{-o ?} for an overview of the options supported | 71 | goto unmap; |
70 | by the used format or see the format descriptions below for details. | 72 | } |
71 | + | 73 | if (len > n->page_size) { |
72 | @item snapshot_param | 74 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, |
73 | is param used for internal snapshot, format is | 75 | uint64_t prp_ent = le64_to_cpu(prp_list[i]); |
74 | 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' | 76 | |
75 | + | 77 | if (i == n->max_prp_ents - 1 && len > n->page_size) { |
76 | @item snapshot_id_or_name | 78 | - if (!prp_ent || prp_ent & (n->page_size - 1)) { |
77 | is deprecated, use snapshot_param instead | 79 | + if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) { |
78 | 80 | + trace_nvme_err_invalid_prplist_ent(prp_ent); | |
79 | +@end table | 81 | goto unmap; |
80 | + | 82 | } |
81 | +@table @option | 83 | |
82 | + | 84 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, |
83 | +@item --object @var{objectdef} | 85 | prp_ent = le64_to_cpu(prp_list[i]); |
84 | +is a QEMU user creatable object definition. See the @code{qemu(1)} manual | 86 | } |
85 | +page for a description of the object properties. The most common object | 87 | |
86 | +type is a @code{secret}, which is used to supply passwords and/or encryption | 88 | - if (!prp_ent || prp_ent & (n->page_size - 1)) { |
87 | +keys. | 89 | + if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) { |
88 | + | 90 | + trace_nvme_err_invalid_prplist_ent(prp_ent); |
89 | +@item --image-opts | 91 | goto unmap; |
90 | +Indicates that the source @var{filename} parameter is to be interpreted as a | 92 | } |
91 | +full option string, not a plain filename. This parameter is mutually | 93 | |
92 | +exclusive with the @var{-f} parameter. | 94 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, |
93 | + | 95 | i++; |
94 | +@item --target-image-opts | 96 | } |
95 | +Indicates that the @var{output_filename} parameter(s) are to be interpreted as | 97 | } else { |
96 | +a full option string, not a plain filename. This parameter is mutually | 98 | - if (prp2 & (n->page_size - 1)) { |
97 | +exclusive with the @var{-O} parameters. It is currently required to also use | 99 | + if (unlikely(prp2 & (n->page_size - 1))) { |
98 | +the @var{-n} parameter to skip image creation. This restriction may be relaxed | 100 | + trace_nvme_err_invalid_prp2_align(prp2); |
99 | +in a future release. | 101 | goto unmap; |
100 | + | 102 | } |
101 | +@item --backing-chain | 103 | if (qsg->nsg) { |
102 | +will enumerate information about backing files in a disk image chain. Refer | 104 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len, |
103 | +below for further description. | 105 | QEMUIOVector iov; |
104 | + | 106 | uint16_t status = NVME_SUCCESS; |
105 | @item -c | 107 | |
106 | indicates that target image must be compressed (qcow format only) | 108 | + trace_nvme_dma_read(prp1, prp2); |
107 | + | 109 | + |
108 | @item -h | 110 | if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) { |
109 | with or without a command shows help and lists the supported formats | 111 | return NVME_INVALID_FIELD | NVME_DNR; |
110 | + | 112 | } |
111 | @item -p | 113 | if (qsg.nsg > 0) { |
112 | display progress bar (compare, convert and rebase commands only). | 114 | - if (dma_buf_read(ptr, len, &qsg)) { |
113 | If the @var{-p} option is not used for a command that supports it, the | 115 | + if (unlikely(dma_buf_read(ptr, len, &qsg))) { |
114 | progress is reported when the process receives a @code{SIGUSR1} or | 116 | + trace_nvme_err_invalid_dma(); |
115 | @code{SIGINFO} signal. | 117 | status = NVME_INVALID_FIELD | NVME_DNR; |
116 | + | 118 | } |
117 | @item -q | 119 | qemu_sglist_destroy(&qsg); |
118 | Quiet mode - do not print any output (except errors). There's no progress bar | 120 | } else { |
119 | in case both @var{-q} and @var{-p} options are used. | 121 | - if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) { |
120 | + | 122 | + if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) { |
121 | @item -S @var{size} | 123 | + trace_nvme_err_invalid_dma(); |
122 | indicates the consecutive number of bytes that must contain only zeros | 124 | status = NVME_INVALID_FIELD | NVME_DNR; |
123 | for qemu-img to create a sparse image during conversion. This value is rounded | 125 | } |
124 | down to the nearest 512 bytes. You may use the common size suffixes like | 126 | qemu_iovec_destroy(&iov); |
125 | @code{k} for kilobytes. | 127 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, |
126 | + | 128 | uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); |
127 | @item -t @var{cache} | 129 | uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS); |
128 | specifies the cache mode that should be used with the (destination) file. See | 130 | |
129 | the documentation of the emulator's @code{-drive cache=...} option for allowed | 131 | - if (slba + nlb > ns->id_ns.nsze) { |
130 | values. | 132 | + if (unlikely(slba + nlb > ns->id_ns.nsze)) { |
131 | + | 133 | + trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); |
132 | @item -T @var{src_cache} | 134 | return NVME_LBA_RANGE | NVME_DNR; |
133 | specifies the cache mode that should be used with the source file(s). See | 135 | } |
134 | the documentation of the emulator's @code{-drive cache=...} option for allowed | 136 | |
135 | values. | 137 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, |
136 | + | 138 | int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0; |
137 | @end table | 139 | enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; |
138 | 140 | ||
139 | Parameters to snapshot subcommand: | 141 | - if ((slba + nlb) > ns->id_ns.nsze) { |
142 | + trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba); | ||
143 | + | ||
144 | + if (unlikely((slba + nlb) > ns->id_ns.nsze)) { | ||
145 | block_acct_invalid(blk_get_stats(n->conf.blk), acct); | ||
146 | + trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); | ||
147 | return NVME_LBA_RANGE | NVME_DNR; | ||
148 | } | ||
149 | |||
150 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
151 | NvmeNamespace *ns; | ||
152 | uint32_t nsid = le32_to_cpu(cmd->nsid); | ||
153 | |||
154 | - if (nsid == 0 || nsid > n->num_namespaces) { | ||
155 | + if (unlikely(nsid == 0 || nsid > n->num_namespaces)) { | ||
156 | + trace_nvme_err_invalid_ns(nsid, n->num_namespaces); | ||
157 | return NVME_INVALID_NSID | NVME_DNR; | ||
158 | } | ||
159 | |||
160 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
161 | case NVME_CMD_READ: | ||
162 | return nvme_rw(n, ns, cmd, req); | ||
163 | default: | ||
164 | + trace_nvme_err_invalid_opc(cmd->opcode); | ||
165 | return NVME_INVALID_OPCODE | NVME_DNR; | ||
166 | } | ||
167 | } | ||
168 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd) | ||
169 | NvmeCQueue *cq; | ||
170 | uint16_t qid = le16_to_cpu(c->qid); | ||
171 | |||
172 | - if (!qid || nvme_check_sqid(n, qid)) { | ||
173 | + if (unlikely(!qid || nvme_check_sqid(n, qid))) { | ||
174 | + trace_nvme_err_invalid_del_sq(qid); | ||
175 | return NVME_INVALID_QID | NVME_DNR; | ||
176 | } | ||
177 | |||
178 | + trace_nvme_del_sq(qid); | ||
179 | + | ||
180 | sq = n->sq[qid]; | ||
181 | while (!QTAILQ_EMPTY(&sq->out_req_list)) { | ||
182 | req = QTAILQ_FIRST(&sq->out_req_list); | ||
183 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) | ||
184 | uint16_t qflags = le16_to_cpu(c->sq_flags); | ||
185 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
186 | |||
187 | - if (!cqid || nvme_check_cqid(n, cqid)) { | ||
188 | + trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags); | ||
189 | + | ||
190 | + if (unlikely(!cqid || nvme_check_cqid(n, cqid))) { | ||
191 | + trace_nvme_err_invalid_create_sq_cqid(cqid); | ||
192 | return NVME_INVALID_CQID | NVME_DNR; | ||
193 | } | ||
194 | - if (!sqid || !nvme_check_sqid(n, sqid)) { | ||
195 | + if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) { | ||
196 | + trace_nvme_err_invalid_create_sq_sqid(sqid); | ||
197 | return NVME_INVALID_QID | NVME_DNR; | ||
198 | } | ||
199 | - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { | ||
200 | + if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) { | ||
201 | + trace_nvme_err_invalid_create_sq_size(qsize); | ||
202 | return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; | ||
203 | } | ||
204 | - if (!prp1 || prp1 & (n->page_size - 1)) { | ||
205 | + if (unlikely(!prp1 || prp1 & (n->page_size - 1))) { | ||
206 | + trace_nvme_err_invalid_create_sq_addr(prp1); | ||
207 | return NVME_INVALID_FIELD | NVME_DNR; | ||
208 | } | ||
209 | - if (!(NVME_SQ_FLAGS_PC(qflags))) { | ||
210 | + if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) { | ||
211 | + trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags)); | ||
212 | return NVME_INVALID_FIELD | NVME_DNR; | ||
213 | } | ||
214 | sq = g_malloc0(sizeof(*sq)); | ||
215 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd) | ||
216 | NvmeCQueue *cq; | ||
217 | uint16_t qid = le16_to_cpu(c->qid); | ||
218 | |||
219 | - if (!qid || nvme_check_cqid(n, qid)) { | ||
220 | + if (unlikely(!qid || nvme_check_cqid(n, qid))) { | ||
221 | + trace_nvme_err_invalid_del_cq_cqid(qid); | ||
222 | return NVME_INVALID_CQID | NVME_DNR; | ||
223 | } | ||
224 | |||
225 | cq = n->cq[qid]; | ||
226 | - if (!QTAILQ_EMPTY(&cq->sq_list)) { | ||
227 | + if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) { | ||
228 | + trace_nvme_err_invalid_del_cq_notempty(qid); | ||
229 | return NVME_INVALID_QUEUE_DEL; | ||
230 | } | ||
231 | + trace_nvme_del_cq(qid); | ||
232 | nvme_free_cq(cq, n); | ||
233 | return NVME_SUCCESS; | ||
234 | } | ||
235 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) | ||
236 | uint16_t qflags = le16_to_cpu(c->cq_flags); | ||
237 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
238 | |||
239 | - if (!cqid || !nvme_check_cqid(n, cqid)) { | ||
240 | + trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags, | ||
241 | + NVME_CQ_FLAGS_IEN(qflags) != 0); | ||
242 | + | ||
243 | + if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) { | ||
244 | + trace_nvme_err_invalid_create_cq_cqid(cqid); | ||
245 | return NVME_INVALID_CQID | NVME_DNR; | ||
246 | } | ||
247 | - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { | ||
248 | + if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) { | ||
249 | + trace_nvme_err_invalid_create_cq_size(qsize); | ||
250 | return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; | ||
251 | } | ||
252 | - if (!prp1) { | ||
253 | + if (unlikely(!prp1)) { | ||
254 | + trace_nvme_err_invalid_create_cq_addr(prp1); | ||
255 | return NVME_INVALID_FIELD | NVME_DNR; | ||
256 | } | ||
257 | - if (vector > n->num_queues) { | ||
258 | + if (unlikely(vector > n->num_queues)) { | ||
259 | + trace_nvme_err_invalid_create_cq_vector(vector); | ||
260 | return NVME_INVALID_IRQ_VECTOR | NVME_DNR; | ||
261 | } | ||
262 | - if (!(NVME_CQ_FLAGS_PC(qflags))) { | ||
263 | + if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) { | ||
264 | + trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags)); | ||
265 | return NVME_INVALID_FIELD | NVME_DNR; | ||
266 | } | ||
267 | |||
268 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c) | ||
269 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
270 | uint64_t prp2 = le64_to_cpu(c->prp2); | ||
271 | |||
272 | + trace_nvme_identify_ctrl(); | ||
273 | + | ||
274 | return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), | ||
275 | prp1, prp2); | ||
276 | } | ||
277 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c) | ||
278 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
279 | uint64_t prp2 = le64_to_cpu(c->prp2); | ||
280 | |||
281 | - if (nsid == 0 || nsid > n->num_namespaces) { | ||
282 | + trace_nvme_identify_ns(nsid); | ||
283 | + | ||
284 | + if (unlikely(nsid == 0 || nsid > n->num_namespaces)) { | ||
285 | + trace_nvme_err_invalid_ns(nsid, n->num_namespaces); | ||
286 | return NVME_INVALID_NSID | NVME_DNR; | ||
287 | } | ||
288 | |||
289 | ns = &n->namespaces[nsid - 1]; | ||
290 | + | ||
291 | return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns), | ||
292 | prp1, prp2); | ||
293 | } | ||
294 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c) | ||
295 | uint16_t ret; | ||
296 | int i, j = 0; | ||
297 | |||
298 | + trace_nvme_identify_nslist(min_nsid); | ||
299 | + | ||
300 | list = g_malloc0(data_len); | ||
301 | for (i = 0; i < n->num_namespaces; i++) { | ||
302 | if (i < min_nsid) { | ||
303 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) | ||
304 | case 0x02: | ||
305 | return nvme_identify_nslist(n, c); | ||
306 | default: | ||
307 | + trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns)); | ||
308 | return NVME_INVALID_FIELD | NVME_DNR; | ||
309 | } | ||
310 | } | ||
311 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
312 | switch (dw10) { | ||
313 | case NVME_VOLATILE_WRITE_CACHE: | ||
314 | result = blk_enable_write_cache(n->conf.blk); | ||
315 | + trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled"); | ||
316 | break; | ||
317 | case NVME_NUMBER_OF_QUEUES: | ||
318 | result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); | ||
319 | + trace_nvme_getfeat_numq(result); | ||
320 | break; | ||
321 | default: | ||
322 | + trace_nvme_err_invalid_getfeat(dw10); | ||
323 | return NVME_INVALID_FIELD | NVME_DNR; | ||
324 | } | ||
325 | |||
326 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
327 | blk_set_enable_write_cache(n->conf.blk, dw11 & 1); | ||
328 | break; | ||
329 | case NVME_NUMBER_OF_QUEUES: | ||
330 | + trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1, | ||
331 | + ((dw11 >> 16) & 0xFFFF) + 1, | ||
332 | + n->num_queues - 1, n->num_queues - 1); | ||
333 | req->cqe.result = | ||
334 | cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); | ||
335 | break; | ||
336 | default: | ||
337 | + trace_nvme_err_invalid_setfeat(dw10); | ||
338 | return NVME_INVALID_FIELD | NVME_DNR; | ||
339 | } | ||
340 | return NVME_SUCCESS; | ||
341 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
342 | case NVME_ADM_CMD_GET_FEATURES: | ||
343 | return nvme_get_feature(n, cmd, req); | ||
344 | default: | ||
345 | + trace_nvme_err_invalid_admin_opc(cmd->opcode); | ||
346 | return NVME_INVALID_OPCODE | NVME_DNR; | ||
347 | } | ||
348 | } | ||
349 | @@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n) | ||
350 | uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12; | ||
351 | uint32_t page_size = 1 << page_bits; | ||
352 | |||
353 | - if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq || | ||
354 | - n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) || | ||
355 | - NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) || | ||
356 | - NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) || | ||
357 | - NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) || | ||
358 | - NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) || | ||
359 | - NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) || | ||
360 | - NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) || | ||
361 | - !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) { | ||
362 | + if (unlikely(n->cq[0])) { | ||
363 | + trace_nvme_err_startfail_cq(); | ||
364 | + return -1; | ||
365 | + } | ||
366 | + if (unlikely(n->sq[0])) { | ||
367 | + trace_nvme_err_startfail_sq(); | ||
368 | + return -1; | ||
369 | + } | ||
370 | + if (unlikely(!n->bar.asq)) { | ||
371 | + trace_nvme_err_startfail_nbarasq(); | ||
372 | + return -1; | ||
373 | + } | ||
374 | + if (unlikely(!n->bar.acq)) { | ||
375 | + trace_nvme_err_startfail_nbaracq(); | ||
376 | + return -1; | ||
377 | + } | ||
378 | + if (unlikely(n->bar.asq & (page_size - 1))) { | ||
379 | + trace_nvme_err_startfail_asq_misaligned(n->bar.asq); | ||
380 | + return -1; | ||
381 | + } | ||
382 | + if (unlikely(n->bar.acq & (page_size - 1))) { | ||
383 | + trace_nvme_err_startfail_acq_misaligned(n->bar.acq); | ||
384 | + return -1; | ||
385 | + } | ||
386 | + if (unlikely(NVME_CC_MPS(n->bar.cc) < | ||
387 | + NVME_CAP_MPSMIN(n->bar.cap))) { | ||
388 | + trace_nvme_err_startfail_page_too_small( | ||
389 | + NVME_CC_MPS(n->bar.cc), | ||
390 | + NVME_CAP_MPSMIN(n->bar.cap)); | ||
391 | + return -1; | ||
392 | + } | ||
393 | + if (unlikely(NVME_CC_MPS(n->bar.cc) > | ||
394 | + NVME_CAP_MPSMAX(n->bar.cap))) { | ||
395 | + trace_nvme_err_startfail_page_too_large( | ||
396 | + NVME_CC_MPS(n->bar.cc), | ||
397 | + NVME_CAP_MPSMAX(n->bar.cap)); | ||
398 | + return -1; | ||
399 | + } | ||
400 | + if (unlikely(NVME_CC_IOCQES(n->bar.cc) < | ||
401 | + NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) { | ||
402 | + trace_nvme_err_startfail_cqent_too_small( | ||
403 | + NVME_CC_IOCQES(n->bar.cc), | ||
404 | + NVME_CTRL_CQES_MIN(n->bar.cap)); | ||
405 | + return -1; | ||
406 | + } | ||
407 | + if (unlikely(NVME_CC_IOCQES(n->bar.cc) > | ||
408 | + NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) { | ||
409 | + trace_nvme_err_startfail_cqent_too_large( | ||
410 | + NVME_CC_IOCQES(n->bar.cc), | ||
411 | + NVME_CTRL_CQES_MAX(n->bar.cap)); | ||
412 | + return -1; | ||
413 | + } | ||
414 | + if (unlikely(NVME_CC_IOSQES(n->bar.cc) < | ||
415 | + NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) { | ||
416 | + trace_nvme_err_startfail_sqent_too_small( | ||
417 | + NVME_CC_IOSQES(n->bar.cc), | ||
418 | + NVME_CTRL_SQES_MIN(n->bar.cap)); | ||
419 | + return -1; | ||
420 | + } | ||
421 | + if (unlikely(NVME_CC_IOSQES(n->bar.cc) > | ||
422 | + NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) { | ||
423 | + trace_nvme_err_startfail_sqent_too_large( | ||
424 | + NVME_CC_IOSQES(n->bar.cc), | ||
425 | + NVME_CTRL_SQES_MAX(n->bar.cap)); | ||
426 | + return -1; | ||
427 | + } | ||
428 | + if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) { | ||
429 | + trace_nvme_err_startfail_asqent_sz_zero(); | ||
430 | + return -1; | ||
431 | + } | ||
432 | + if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) { | ||
433 | + trace_nvme_err_startfail_acqent_sz_zero(); | ||
434 | return -1; | ||
435 | } | ||
436 | |||
437 | @@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n) | ||
438 | static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, | ||
439 | unsigned size) | ||
440 | { | ||
441 | + if (unlikely(offset & (sizeof(uint32_t) - 1))) { | ||
442 | + NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32, | ||
443 | + "MMIO write not 32-bit aligned," | ||
444 | + " offset=0x%"PRIx64"", offset); | ||
445 | + /* should be ignored, fall through for now */ | ||
446 | + } | ||
447 | + | ||
448 | + if (unlikely(size < sizeof(uint32_t))) { | ||
449 | + NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall, | ||
450 | + "MMIO write smaller than 32-bits," | ||
451 | + " offset=0x%"PRIx64", size=%u", | ||
452 | + offset, size); | ||
453 | + /* should be ignored, fall through for now */ | ||
454 | + } | ||
455 | + | ||
456 | switch (offset) { | ||
457 | - case 0xc: | ||
458 | + case 0xc: /* INTMS */ | ||
459 | + if (unlikely(msix_enabled(&(n->parent_obj)))) { | ||
460 | + NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix, | ||
461 | + "undefined access to interrupt mask set" | ||
462 | + " when MSI-X is enabled"); | ||
463 | + /* should be ignored, fall through for now */ | ||
464 | + } | ||
465 | n->bar.intms |= data & 0xffffffff; | ||
466 | n->bar.intmc = n->bar.intms; | ||
467 | + trace_nvme_mmio_intm_set(data & 0xffffffff, | ||
468 | + n->bar.intmc); | ||
469 | break; | ||
470 | - case 0x10: | ||
471 | + case 0x10: /* INTMC */ | ||
472 | + if (unlikely(msix_enabled(&(n->parent_obj)))) { | ||
473 | + NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix, | ||
474 | + "undefined access to interrupt mask clr" | ||
475 | + " when MSI-X is enabled"); | ||
476 | + /* should be ignored, fall through for now */ | ||
477 | + } | ||
478 | n->bar.intms &= ~(data & 0xffffffff); | ||
479 | n->bar.intmc = n->bar.intms; | ||
480 | + trace_nvme_mmio_intm_clr(data & 0xffffffff, | ||
481 | + n->bar.intmc); | ||
482 | break; | ||
483 | - case 0x14: | ||
484 | + case 0x14: /* CC */ | ||
485 | + trace_nvme_mmio_cfg(data & 0xffffffff); | ||
486 | /* Windows first sends data, then sends enable bit */ | ||
487 | if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) && | ||
488 | !NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc)) | ||
489 | @@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, | ||
490 | |||
491 | if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) { | ||
492 | n->bar.cc = data; | ||
493 | - if (nvme_start_ctrl(n)) { | ||
494 | + if (unlikely(nvme_start_ctrl(n))) { | ||
495 | + trace_nvme_err_startfail(); | ||
496 | n->bar.csts = NVME_CSTS_FAILED; | ||
497 | } else { | ||
498 | + trace_nvme_mmio_start_success(); | ||
499 | n->bar.csts = NVME_CSTS_READY; | ||
500 | } | ||
501 | } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) { | ||
502 | + trace_nvme_mmio_stopped(); | ||
503 | nvme_clear_ctrl(n); | ||
504 | n->bar.csts &= ~NVME_CSTS_READY; | ||
505 | } | ||
506 | if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) { | ||
507 | - nvme_clear_ctrl(n); | ||
508 | - n->bar.cc = data; | ||
509 | - n->bar.csts |= NVME_CSTS_SHST_COMPLETE; | ||
510 | + trace_nvme_mmio_shutdown_set(); | ||
511 | + nvme_clear_ctrl(n); | ||
512 | + n->bar.cc = data; | ||
513 | + n->bar.csts |= NVME_CSTS_SHST_COMPLETE; | ||
514 | } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) { | ||
515 | - n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; | ||
516 | - n->bar.cc = data; | ||
517 | + trace_nvme_mmio_shutdown_cleared(); | ||
518 | + n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; | ||
519 | + n->bar.cc = data; | ||
520 | + } | ||
521 | + break; | ||
522 | + case 0x1C: /* CSTS */ | ||
523 | + if (data & (1 << 4)) { | ||
524 | + NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported, | ||
525 | + "attempted to W1C CSTS.NSSRO" | ||
526 | + " but CAP.NSSRS is zero (not supported)"); | ||
527 | + } else if (data != 0) { | ||
528 | + NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts, | ||
529 | + "attempted to set a read only bit" | ||
530 | + " of controller status"); | ||
531 | + } | ||
532 | + break; | ||
533 | + case 0x20: /* NSSR */ | ||
534 | + if (data == 0x4E564D65) { | ||
535 | + trace_nvme_ub_mmiowr_ssreset_unsupported(); | ||
536 | + } else { | ||
537 | + /* The spec says that writes of other values have no effect */ | ||
538 | + return; | ||
539 | } | ||
540 | break; | ||
541 | - case 0x24: | ||
542 | + case 0x24: /* AQA */ | ||
543 | n->bar.aqa = data & 0xffffffff; | ||
544 | + trace_nvme_mmio_aqattr(data & 0xffffffff); | ||
545 | break; | ||
546 | - case 0x28: | ||
547 | + case 0x28: /* ASQ */ | ||
548 | n->bar.asq = data; | ||
549 | + trace_nvme_mmio_asqaddr(data); | ||
550 | break; | ||
551 | - case 0x2c: | ||
552 | + case 0x2c: /* ASQ hi */ | ||
553 | n->bar.asq |= data << 32; | ||
554 | + trace_nvme_mmio_asqaddr_hi(data, n->bar.asq); | ||
555 | break; | ||
556 | - case 0x30: | ||
557 | + case 0x30: /* ACQ */ | ||
558 | + trace_nvme_mmio_acqaddr(data); | ||
559 | n->bar.acq = data; | ||
560 | break; | ||
561 | - case 0x34: | ||
562 | + case 0x34: /* ACQ hi */ | ||
563 | n->bar.acq |= data << 32; | ||
564 | + trace_nvme_mmio_acqaddr_hi(data, n->bar.acq); | ||
565 | break; | ||
566 | + case 0x38: /* CMBLOC */ | ||
567 | + NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved, | ||
568 | + "invalid write to reserved CMBLOC" | ||
569 | + " when CMBSZ is zero, ignored"); | ||
570 | + return; | ||
571 | + case 0x3C: /* CMBSZ */ | ||
572 | + NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly, | ||
573 | + "invalid write to read only CMBSZ, ignored"); | ||
574 | + return; | ||
575 | default: | ||
576 | + NVME_GUEST_ERR(nvme_ub_mmiowr_invalid, | ||
577 | + "invalid MMIO write," | ||
578 | + " offset=0x%"PRIx64", data=%"PRIx64"", | ||
579 | + offset, data); | ||
580 | break; | ||
581 | } | ||
582 | } | ||
583 | @@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) | ||
584 | uint8_t *ptr = (uint8_t *)&n->bar; | ||
585 | uint64_t val = 0; | ||
586 | |||
587 | + if (unlikely(addr & (sizeof(uint32_t) - 1))) { | ||
588 | + NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32, | ||
589 | + "MMIO read not 32-bit aligned," | ||
590 | + " offset=0x%"PRIx64"", addr); | ||
591 | + /* should RAZ, fall through for now */ | ||
592 | + } else if (unlikely(size < sizeof(uint32_t))) { | ||
593 | + NVME_GUEST_ERR(nvme_ub_mmiord_toosmall, | ||
594 | + "MMIO read smaller than 32-bits," | ||
595 | + " offset=0x%"PRIx64"", addr); | ||
596 | + /* should RAZ, fall through for now */ | ||
597 | + } | ||
598 | + | ||
599 | if (addr < sizeof(n->bar)) { | ||
600 | memcpy(&val, ptr + addr, size); | ||
601 | + } else { | ||
602 | + NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs, | ||
603 | + "MMIO read beyond last register," | ||
604 | + " offset=0x%"PRIx64", returning 0", addr); | ||
605 | } | ||
606 | + | ||
607 | return val; | ||
608 | } | ||
609 | |||
610 | @@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) | ||
611 | { | ||
612 | uint32_t qid; | ||
613 | |||
614 | - if (addr & ((1 << 2) - 1)) { | ||
615 | + if (unlikely(addr & ((1 << 2) - 1))) { | ||
616 | + NVME_GUEST_ERR(nvme_ub_db_wr_misaligned, | ||
617 | + "doorbell write not 32-bit aligned," | ||
618 | + " offset=0x%"PRIx64", ignoring", addr); | ||
619 | return; | ||
620 | } | ||
621 | |||
622 | if (((addr - 0x1000) >> 2) & 1) { | ||
623 | + /* Completion queue doorbell write */ | ||
624 | + | ||
625 | uint16_t new_head = val & 0xffff; | ||
626 | int start_sqs; | ||
627 | NvmeCQueue *cq; | ||
628 | |||
629 | qid = (addr - (0x1000 + (1 << 2))) >> 3; | ||
630 | - if (nvme_check_cqid(n, qid)) { | ||
631 | + if (unlikely(nvme_check_cqid(n, qid))) { | ||
632 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq, | ||
633 | + "completion queue doorbell write" | ||
634 | + " for nonexistent queue," | ||
635 | + " sqid=%"PRIu32", ignoring", qid); | ||
636 | return; | ||
637 | } | ||
638 | |||
639 | cq = n->cq[qid]; | ||
640 | - if (new_head >= cq->size) { | ||
641 | + if (unlikely(new_head >= cq->size)) { | ||
642 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead, | ||
643 | + "completion queue doorbell write value" | ||
644 | + " beyond queue size, sqid=%"PRIu32"," | ||
645 | + " new_head=%"PRIu16", ignoring", | ||
646 | + qid, new_head); | ||
647 | return; | ||
648 | } | ||
649 | |||
650 | @@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) | ||
651 | nvme_isr_notify(n, cq); | ||
652 | } | ||
653 | } else { | ||
654 | + /* Submission queue doorbell write */ | ||
655 | + | ||
656 | uint16_t new_tail = val & 0xffff; | ||
657 | NvmeSQueue *sq; | ||
658 | |||
659 | qid = (addr - 0x1000) >> 3; | ||
660 | - if (nvme_check_sqid(n, qid)) { | ||
661 | + if (unlikely(nvme_check_sqid(n, qid))) { | ||
662 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq, | ||
663 | + "submission queue doorbell write" | ||
664 | + " for nonexistent queue," | ||
665 | + " sqid=%"PRIu32", ignoring", qid); | ||
666 | return; | ||
667 | } | ||
668 | |||
669 | sq = n->sq[qid]; | ||
670 | - if (new_tail >= sq->size) { | ||
671 | + if (unlikely(new_tail >= sq->size)) { | ||
672 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail, | ||
673 | + "submission queue doorbell write value" | ||
674 | + " beyond queue size, sqid=%"PRIu32"," | ||
675 | + " new_tail=%"PRIu16", ignoring", | ||
676 | + qid, new_tail); | ||
677 | return; | ||
678 | } | ||
679 | |||
680 | diff --git a/hw/block/trace-events b/hw/block/trace-events | ||
681 | index XXXXXXX..XXXXXXX 100644 | ||
682 | --- a/hw/block/trace-events | ||
683 | +++ b/hw/block/trace-events | ||
684 | @@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6 | ||
685 | hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" | ||
686 | hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d" | ||
687 | |||
688 | +# hw/block/nvme.c | ||
689 | +# nvme traces for successful events | ||
690 | +nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" | ||
691 | +nvme_irq_pin(void) "pulsing IRQ pin" | ||
692 | +nvme_irq_masked(void) "IRQ is masked" | ||
693 | +nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64"" | ||
694 | +nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64"" | ||
695 | +nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16"" | ||
696 | +nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d" | ||
697 | +nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16"" | ||
698 | +nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16"" | ||
699 | +nvme_identify_ctrl(void) "identify controller" | ||
700 | +nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16"" | ||
701 | +nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16"" | ||
702 | +nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s" | ||
703 | +nvme_getfeat_numq(int result) "get feature number of queues, result=%d" | ||
704 | +nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d" | ||
705 | +nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64"" | ||
706 | +nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64"" | ||
707 | +nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64"" | ||
708 | +nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64"" | ||
709 | +nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64"" | ||
710 | +nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64"" | ||
711 | +nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64"" | ||
712 | +nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64"" | ||
713 | +nvme_mmio_start_success(void) "setting controller enable bit succeeded" | ||
714 | +nvme_mmio_stopped(void) "cleared controller enable bit" | ||
715 | +nvme_mmio_shutdown_set(void) "shutdown bit set" | ||
716 | +nvme_mmio_shutdown_cleared(void) "shutdown bit cleared" | ||
717 | + | ||
718 | +# nvme traces for error conditions | ||
719 | +nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size" | ||
720 | +nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64"" | ||
721 | +nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64"" | ||
722 | +nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred" | ||
723 | +nvme_err_invalid_field(void) "invalid field" | ||
724 | +nvme_err_invalid_prp(void) "invalid PRP" | ||
725 | +nvme_err_invalid_sgl(void) "invalid SGL" | ||
726 | +nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u" | ||
727 | +nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8"" | ||
728 | +nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8"" | ||
729 | +nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64"" | ||
730 | +nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16"" | ||
731 | +nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16"" | ||
732 | +nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16"" | ||
733 | +nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16"" | ||
734 | +nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64"" | ||
735 | +nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16"" | ||
736 | +nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16"" | ||
737 | +nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16"" | ||
738 | +nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16"" | ||
739 | +nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16"" | ||
740 | +nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64"" | ||
741 | +nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16"" | ||
742 | +nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16"" | ||
743 | +nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16"" | ||
744 | +nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32"" | ||
745 | +nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32"" | ||
746 | +nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues" | ||
747 | +nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues" | ||
748 | +nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null" | ||
749 | +nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null" | ||
750 | +nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64"" | ||
751 | +nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64"" | ||
752 | +nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u" | ||
753 | +nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u" | ||
754 | +nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u" | ||
755 | +nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u" | ||
756 | +nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u" | ||
757 | +nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u" | ||
758 | +nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero" | ||
759 | +nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero" | ||
760 | +nvme_err_startfail(void) "setting controller enable bit failed" | ||
761 | + | ||
762 | +# Traces for undefined behavior | ||
763 | +nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64"" | ||
764 | +nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u" | ||
765 | +nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled" | ||
766 | +nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status" | ||
767 | +nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)" | ||
768 | +nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)" | ||
769 | +nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored" | ||
770 | +nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored" | ||
771 | +nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64"" | ||
772 | +nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64"" | ||
773 | +nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64"" | ||
774 | +nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0" | ||
775 | +nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring" | ||
776 | +nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring" | ||
777 | +nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring" | ||
778 | +nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring" | ||
779 | +nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring" | ||
780 | + | ||
781 | # hw/block/xen_disk.c | ||
782 | xen_disk_alloc(char *name) "%s" | ||
783 | xen_disk_init(char *name) "%s" | ||
140 | -- | 784 | -- |
141 | 2.13.6 | 785 | 2.13.6 |
142 | 786 | ||
143 | 787 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | This reverts commit 76bf133c4 which updated the reference output, and | 3 | Management tools create overlays of running guests with qemu-img: |
4 | fixed the reference image, because the code path we want to exercise is | ||
5 | actually the invalid image size. | ||
6 | 4 | ||
7 | The descriptor block in the image, which includes the CID to verify, has been | 5 | $ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2 |
8 | invalid since the reference image was added. Since commit 9877860e7bd we report | ||
9 | this error earlier than the "file too large", so 059.out mismatches. | ||
10 | 6 | ||
11 | The binary change is generated along the operations of: | 7 | but this doesn't work anymore due to image locking: |
12 | 8 | ||
13 | $ bunzip2 afl9.vmdk.bz2 | 9 | qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock |
14 | $ qemu-img create -f vmdk fix.vmdk 1G | 10 | Is another process using the image? |
15 | $ dd if=afl9.vmdk of=fix.vmdk bs=512 count=1 conv=notrunc | 11 | Could not open backing image to determine size. |
16 | $ mv fix.vmdk afl9.vmdk | 12 | Use the force share option to allow this use case again. |
17 | $ bzip2 afl9.vmdk | ||
18 | 13 | ||
14 | Cc: qemu-stable@nongnu.org | ||
19 | Signed-off-by: Fam Zheng <famz@redhat.com> | 15 | Signed-off-by: Fam Zheng <famz@redhat.com> |
20 | Reviewed-by: Eric Blake <eblake@redhat.com> | 16 | Reviewed-by: Eric Blake <eblake@redhat.com> |
21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
22 | --- | 18 | --- |
23 | tests/qemu-iotests/059.out | 2 +- | 19 | block.c | 3 ++- |
24 | tests/qemu-iotests/sample_images/afl9.vmdk.bz2 | Bin 178 -> 618 bytes | 20 | 1 file changed, 2 insertions(+), 1 deletion(-) |
25 | 2 files changed, 1 insertion(+), 1 deletion(-) | ||
26 | 21 | ||
27 | diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out | 22 | diff --git a/block.c b/block.c |
28 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/tests/qemu-iotests/059.out | 24 | --- a/block.c |
30 | +++ b/tests/qemu-iotests/059.out | 25 | +++ b/block.c |
31 | @@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File | 26 | @@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt, |
32 | 0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk | 27 | back_flags = flags; |
33 | 28 | back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); | |
34 | === Testing afl image with a very large capacity === | 29 | |
35 | -qemu-img: Could not open 'TEST_DIR/afl9.IMGFMT': Could not open 'TEST_DIR/afl9.IMGFMT': Invalid argument | 30 | + backing_options = qdict_new(); |
36 | +qemu-img: Can't get image size 'TEST_DIR/afl9.IMGFMT': File too large | 31 | if (backing_fmt) { |
37 | *** done | 32 | - backing_options = qdict_new(); |
38 | diff --git a/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 b/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 | 33 | qdict_put_str(backing_options, "driver", backing_fmt); |
39 | index XXXXXXX..XXXXXXX 100644 | 34 | } |
40 | GIT binary patch | 35 | + qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true); |
41 | literal 618 | 36 | |
42 | zcmV-w0+szjT4*^jL0KkKSvgW7ssIN3|NsBH-Q9UpfAhclU70`s-*NE~5QvC~h=_=Y | 37 | bs = bdrv_open(full_backing, NULL, backing_options, back_flags, |
43 | zh>D2n*q*=vygR634445h35k;?00h9835kMW00004$iPepVE{Bqk)uhJ^wfGLr=)3s | 38 | &local_err); |
44 | zhM5CR88jLh7)B;cA*K)*6GmuECPU3o4NWG5O#pg>Ak#xY8Z^<M8Z>CrMt}oD38Ns$ | ||
45 | z02n}M0LdjZ&}cLPqd+nPKmn$j0iXe(02%-d27nnJriN-uE+X&cz@Bj4BBfd|yV!NB | ||
46 | zwqkL}nW3AI5x^jp=t%^F1pxqp)v#n#)j$zcm1xqv(!$2d*5%vF{5RPWnOV8-^tE<( | ||
47 | zU~%&}Y0uNu*9Wt=yS^8PkC&gPueZO%IG;aD{l#sG`<Af;l1Pnwpi9I75FkQ`LLhd8 | ||
48 | z6(9f*2s+N5=%bwp80ddrD6>m4Ho*fsHXdM<jtl*zKvRiTx7Ugy1|Nl<Ns!z;1dvhy | ||
49 | z=`SDHh~{u|1ZodC(_lzezQ)I*Kv2z|PZ@!SJjlVzwGdx2iu#W}dI{t+T&dDWT^LPy | ||
50 | zg3NouEM=V~7GvZQS1CXy676F6mJXWGgW!KTr+E$OspGYCjWmuwa^<Bc>_(-i7fPIW | ||
51 | zA+~n9iy_f)g8B2RILhd%F)dZ5f?7pFLw)@;Ncl<JE}gvMrfh{elT#3gLjY6r8xY4O | ||
52 | z)UO#pv=WYptukn<DuoMH2ip%k?V^k!rjQirK^RC<Brw>3Bz9<|!xm0F{45K+gg8#n | ||
53 | z4FNAJ!<X|3Vq+lyV4=xZ;>AN0<K=%c4A2ruB!4rGvWm!KFrvd4PyfZ-kxmpO4pfM$ | ||
54 | EfLnqQYXATM | ||
55 | |||
56 | literal 178 | ||
57 | zcmV;j08RfwT4*^jL0KkKS>A08g#Z9x|HJ$H)ZJi0004xF0SE*D03g5s00IDLSQelF | ||
58 | ziVX^$pfWNUJrmRhn2k52pQ;Rs0EQC;(S%|!m`2~BZ@b++;etskRJUVl!Kt)wu7?VN | ||
59 | zl;%JdqX2?TgsNVJP?87M*MvL1qQnBkCES&?0@MeaN-bL4;bDzxmMm|da4fuh!=#fu | ||
60 | g@i9R@5z!av{9tA<GGr!3hi~HUNT&)C8_l7xpl%OKQ2+n{ | ||
61 | |||
62 | -- | 39 | -- |
63 | 2.13.6 | 40 | 2.13.6 |
64 | 41 | ||
65 | 42 | diff view generated by jsdifflib |
1 | From: Paolo Bonzini <pbonzini@redhat.com> | 1 | From: Thomas Huth <thuth@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Even if an op blocker is present for BLOCK_OP_TYPE_MIRROR_SOURCE, | 3 | It's not working anymore since QEMU v1.3.0 - time to remove it now. |
4 | it is checked a bit late and the result is that the target is | ||
5 | created even if drive-mirror subsequently fails. Add an early | ||
6 | check to avoid this. | ||
7 | 4 | ||
8 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | 5 | Signed-off-by: Thomas Huth <thuth@redhat.com> |
9 | Reviewed-by: Fam Zheng <famz@redhat.com> | 6 | Reviewed-by: John Snow <jsnow@redhat.com> |
10 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 7 | Reviewed-by: Markus Armbruster <armbru@redhat.com> |
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 9 | --- |
13 | blockdev.c | 5 +++++ | 10 | blockdev.c | 11 ----------- |
14 | 1 file changed, 5 insertions(+) | 11 | qemu-doc.texi | 6 ------ |
12 | 2 files changed, 17 deletions(-) | ||
15 | 13 | ||
16 | diff --git a/blockdev.c b/blockdev.c | 14 | diff --git a/blockdev.c b/blockdev.c |
17 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/blockdev.c | 16 | --- a/blockdev.c |
19 | +++ b/blockdev.c | 17 | +++ b/blockdev.c |
20 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | 18 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { |
21 | return; | 19 | .type = QEMU_OPT_STRING, |
20 | .help = "chs translation (auto, lba, none)", | ||
21 | },{ | ||
22 | - .name = "boot", | ||
23 | - .type = QEMU_OPT_BOOL, | ||
24 | - .help = "(deprecated, ignored)", | ||
25 | - },{ | ||
26 | .name = "addr", | ||
27 | .type = QEMU_OPT_STRING, | ||
28 | .help = "pci address (virtio only)", | ||
29 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
30 | goto fail; | ||
22 | } | 31 | } |
23 | 32 | ||
24 | + /* Early check to avoid creating target */ | 33 | - /* Deprecated option boot=[on|off] */ |
25 | + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { | 34 | - if (qemu_opt_get(legacy_opts, "boot") != NULL) { |
26 | + return; | 35 | - fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be " |
27 | + } | 36 | - "ignored. Future versions will reject this parameter. Please " |
28 | + | 37 | - "update your scripts.\n"); |
29 | aio_context = bdrv_get_aio_context(bs); | 38 | - } |
30 | aio_context_acquire(aio_context); | 39 | - |
31 | 40 | /* Other deprecated options */ | |
41 | if (!qtest_enabled()) { | ||
42 | for (i = 0; i < ARRAY_SIZE(deprecated); i++) { | ||
43 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
44 | index XXXXXXX..XXXXXXX 100644 | ||
45 | --- a/qemu-doc.texi | ||
46 | +++ b/qemu-doc.texi | ||
47 | @@ -XXX,XX +XXX,XX @@ deprecated. | ||
48 | |||
49 | @section System emulator command line arguments | ||
50 | |||
51 | -@subsection -drive boot=on|off (since 1.3.0) | ||
52 | - | ||
53 | -The ``boot=on|off'' option to the ``-drive'' argument is | ||
54 | -ignored. Applications should use the ``bootindex=N'' parameter | ||
55 | -to set an absolute ordering between devices instead. | ||
56 | - | ||
57 | @subsection -tdf (since 1.3.0) | ||
58 | |||
59 | The ``-tdf'' argument is ignored. The behaviour implemented | ||
32 | -- | 60 | -- |
33 | 2.13.6 | 61 | 2.13.6 |
34 | 62 | ||
35 | 63 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | From: Thomas Huth <thuth@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | g_realloc() aborts the program if it fails to allocate the required | 3 | It's been marked as deprecated since QEMU v2.10.0, and so far nobody |
4 | amount of memory. We want to detect that scenario and return an error | 4 | complained that we should keep it, so let's remove this legacy option |
5 | instead, so let's use g_try_realloc(). | 5 | now to simplify the code quite a bit. |
6 | 6 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 7 | Signed-off-by: Thomas Huth <thuth@redhat.com> |
8 | Reviewed-by: John Snow <jsnow@redhat.com> | ||
9 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 11 | --- |
10 | block/qcow2-cluster.c | 10 +++++++++- | 12 | vl.c | 86 ++------------------------------------------------------- |
11 | 1 file changed, 9 insertions(+), 1 deletion(-) | 13 | qemu-doc.texi | 8 ------ |
12 | 14 | qemu-options.hx | 19 ++----------- | |
13 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | 15 | 3 files changed, 4 insertions(+), 109 deletions(-) |
16 | |||
17 | diff --git a/vl.c b/vl.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/qcow2-cluster.c | 19 | --- a/vl.c |
16 | +++ b/block/qcow2-cluster.c | 20 | +++ b/vl.c |
17 | @@ -XXX,XX +XXX,XX @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, | 21 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) |
18 | int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size * | 22 | const char *boot_order = NULL; |
19 | sizeof(uint64_t), BDRV_SECTOR_SIZE); | 23 | const char *boot_once = NULL; |
20 | 24 | DisplayState *ds; | |
21 | - l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); | 25 | - int cyls, heads, secs, translation; |
22 | + uint64_t *new_l1_table = | 26 | QemuOpts *opts, *machine_opts; |
23 | + g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); | 27 | - QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL; |
24 | + | 28 | + QemuOpts *icount_opts = NULL, *accel_opts = NULL; |
25 | + if (!new_l1_table) { | 29 | QemuOptsList *olist; |
26 | + ret = -ENOMEM; | 30 | int optind; |
27 | + goto fail; | 31 | const char *optarg; |
28 | + } | 32 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) |
29 | + | 33 | |
30 | + l1_table = new_l1_table; | 34 | cpu_model = NULL; |
31 | 35 | snapshot = 0; | |
32 | ret = bdrv_read(bs->file, | 36 | - cyls = heads = secs = 0; |
33 | s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE, | 37 | - translation = BIOS_ATA_TRANSLATION_AUTO; |
38 | |||
39 | nb_nics = 0; | ||
40 | |||
41 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) | ||
42 | if (optind >= argc) | ||
43 | break; | ||
44 | if (argv[optind][0] != '-') { | ||
45 | - hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); | ||
46 | + drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); | ||
47 | } else { | ||
48 | const QEMUOption *popt; | ||
49 | |||
50 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) | ||
51 | cpu_model = optarg; | ||
52 | break; | ||
53 | case QEMU_OPTION_hda: | ||
54 | - { | ||
55 | - char buf[256]; | ||
56 | - if (cyls == 0) | ||
57 | - snprintf(buf, sizeof(buf), "%s", HD_OPTS); | ||
58 | - else | ||
59 | - snprintf(buf, sizeof(buf), | ||
60 | - "%s,cyls=%d,heads=%d,secs=%d%s", | ||
61 | - HD_OPTS , cyls, heads, secs, | ||
62 | - translation == BIOS_ATA_TRANSLATION_LBA ? | ||
63 | - ",trans=lba" : | ||
64 | - translation == BIOS_ATA_TRANSLATION_NONE ? | ||
65 | - ",trans=none" : ""); | ||
66 | - drive_add(IF_DEFAULT, 0, optarg, buf); | ||
67 | - break; | ||
68 | - } | ||
69 | case QEMU_OPTION_hdb: | ||
70 | case QEMU_OPTION_hdc: | ||
71 | case QEMU_OPTION_hdd: | ||
72 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) | ||
73 | case QEMU_OPTION_snapshot: | ||
74 | snapshot = 1; | ||
75 | break; | ||
76 | - case QEMU_OPTION_hdachs: | ||
77 | - { | ||
78 | - const char *p; | ||
79 | - p = optarg; | ||
80 | - cyls = strtol(p, (char **)&p, 0); | ||
81 | - if (cyls < 1 || cyls > 16383) | ||
82 | - goto chs_fail; | ||
83 | - if (*p != ',') | ||
84 | - goto chs_fail; | ||
85 | - p++; | ||
86 | - heads = strtol(p, (char **)&p, 0); | ||
87 | - if (heads < 1 || heads > 16) | ||
88 | - goto chs_fail; | ||
89 | - if (*p != ',') | ||
90 | - goto chs_fail; | ||
91 | - p++; | ||
92 | - secs = strtol(p, (char **)&p, 0); | ||
93 | - if (secs < 1 || secs > 63) | ||
94 | - goto chs_fail; | ||
95 | - if (*p == ',') { | ||
96 | - p++; | ||
97 | - if (!strcmp(p, "large")) { | ||
98 | - translation = BIOS_ATA_TRANSLATION_LARGE; | ||
99 | - } else if (!strcmp(p, "rechs")) { | ||
100 | - translation = BIOS_ATA_TRANSLATION_RECHS; | ||
101 | - } else if (!strcmp(p, "none")) { | ||
102 | - translation = BIOS_ATA_TRANSLATION_NONE; | ||
103 | - } else if (!strcmp(p, "lba")) { | ||
104 | - translation = BIOS_ATA_TRANSLATION_LBA; | ||
105 | - } else if (!strcmp(p, "auto")) { | ||
106 | - translation = BIOS_ATA_TRANSLATION_AUTO; | ||
107 | - } else { | ||
108 | - goto chs_fail; | ||
109 | - } | ||
110 | - } else if (*p != '\0') { | ||
111 | - chs_fail: | ||
112 | - error_report("invalid physical CHS format"); | ||
113 | - exit(1); | ||
114 | - } | ||
115 | - if (hda_opts != NULL) { | ||
116 | - qemu_opt_set_number(hda_opts, "cyls", cyls, | ||
117 | - &error_abort); | ||
118 | - qemu_opt_set_number(hda_opts, "heads", heads, | ||
119 | - &error_abort); | ||
120 | - qemu_opt_set_number(hda_opts, "secs", secs, | ||
121 | - &error_abort); | ||
122 | - if (translation == BIOS_ATA_TRANSLATION_LARGE) { | ||
123 | - qemu_opt_set(hda_opts, "trans", "large", | ||
124 | - &error_abort); | ||
125 | - } else if (translation == BIOS_ATA_TRANSLATION_RECHS) { | ||
126 | - qemu_opt_set(hda_opts, "trans", "rechs", | ||
127 | - &error_abort); | ||
128 | - } else if (translation == BIOS_ATA_TRANSLATION_LBA) { | ||
129 | - qemu_opt_set(hda_opts, "trans", "lba", | ||
130 | - &error_abort); | ||
131 | - } else if (translation == BIOS_ATA_TRANSLATION_NONE) { | ||
132 | - qemu_opt_set(hda_opts, "trans", "none", | ||
133 | - &error_abort); | ||
134 | - } | ||
135 | - } | ||
136 | - } | ||
137 | - error_report("'-hdachs' is deprecated, please use '-device" | ||
138 | - " ide-hd,cyls=c,heads=h,secs=s,...' instead"); | ||
139 | - break; | ||
140 | case QEMU_OPTION_numa: | ||
141 | opts = qemu_opts_parse_noisily(qemu_find_opts("numa"), | ||
142 | optarg, true); | ||
143 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
144 | index XXXXXXX..XXXXXXX 100644 | ||
145 | --- a/qemu-doc.texi | ||
146 | +++ b/qemu-doc.texi | ||
147 | @@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the | ||
148 | ``-object filter-dump'' argument which works in combination | ||
149 | with the modern ``-netdev`` backends instead. | ||
150 | |||
151 | -@subsection -hdachs (since 2.10.0) | ||
152 | - | ||
153 | -The ``-hdachs'' argument is now a synonym for setting | ||
154 | -the ``cyls'', ``heads'', ``secs'', and ``trans'' properties | ||
155 | -on the ``ide-hd'' device using the ``-device'' argument. | ||
156 | -The new syntax allows different settings to be provided | ||
157 | -per disk. | ||
158 | - | ||
159 | @subsection -usbdevice (since 2.10.0) | ||
160 | |||
161 | The ``-usbdevice DEV'' argument is now a synonym for setting | ||
162 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
163 | index XXXXXXX..XXXXXXX 100644 | ||
164 | --- a/qemu-options.hx | ||
165 | +++ b/qemu-options.hx | ||
166 | @@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type. | ||
167 | @item media=@var{media} | ||
168 | This option defines the type of the media: disk or cdrom. | ||
169 | @item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] | ||
170 | -These options have the same definition as they have in @option{-hdachs}. | ||
171 | -These parameters are deprecated, use the corresponding parameters | ||
172 | +Force disk physical geometry and the optional BIOS translation (trans=none or | ||
173 | +lba). These parameters are deprecated, use the corresponding parameters | ||
174 | of @code{-device} instead. | ||
175 | @item snapshot=@var{snapshot} | ||
176 | @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive | ||
177 | @@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force | ||
178 | the write back by pressing @key{C-a s} (@pxref{disk_images}). | ||
179 | ETEXI | ||
180 | |||
181 | -DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \ | ||
182 | - "-hdachs c,h,s[,t]\n" \ | ||
183 | - " force hard disk 0 physical geometry and the optional BIOS\n" \ | ||
184 | - " translation (t=none or lba) (usually QEMU can guess them)\n", | ||
185 | - QEMU_ARCH_ALL) | ||
186 | -STEXI | ||
187 | -@item -hdachs @var{c},@var{h},@var{s},[,@var{t}] | ||
188 | -@findex -hdachs | ||
189 | -Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= | ||
190 | -@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS | ||
191 | -translation mode (@var{t}=none, lba or auto). Usually QEMU can guess | ||
192 | -all those parameters. This option is deprecated, please use | ||
193 | -@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead. | ||
194 | -ETEXI | ||
195 | - | ||
196 | DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, | ||
197 | "-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n" | ||
198 | " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n" | ||
34 | -- | 199 | -- |
35 | 2.13.6 | 200 | 2.13.6 |
36 | 201 | ||
37 | 202 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | From: Thomas Huth <thuth@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Only a few select machine types support floppy drives and there is | 3 | Looks like we forgot to announce the deprecation of these options in |
4 | actually nothing preventing us from using virtio here, so let's do it. | 4 | the corresponding chapter of the qemu-doc text, so let's do that now. |
5 | 5 | ||
6 | Reported-by: Christian Borntraeger <borntraeger@de.ibm.com> | 6 | Signed-off-by: Thomas Huth <thuth@redhat.com> |
7 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 7 | Reviewed-by: John Snow <jsnow@redhat.com> |
8 | Tested-by: Christian Borntraeger <borntraeger@de.ibm.com> | 8 | Reviewed-by: Markus Armbruster <armbru@redhat.com> |
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 10 | --- |
11 | tests/qemu-iotests/155 | 14 +++++++++----- | 11 | qemu-doc.texi | 15 +++++++++++++++ |
12 | 1 file changed, 9 insertions(+), 5 deletions(-) | 12 | 1 file changed, 15 insertions(+) |
13 | 13 | ||
14 | diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 | 14 | diff --git a/qemu-doc.texi b/qemu-doc.texi |
15 | index XXXXXXX..XXXXXXX 100755 | 15 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/tests/qemu-iotests/155 | 16 | --- a/qemu-doc.texi |
17 | +++ b/tests/qemu-iotests/155 | 17 | +++ b/qemu-doc.texi |
18 | @@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase): | 18 | @@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU. |
19 | 'file': {'driver': 'file', | 19 | The ``-drive if=scsi'' argument is replaced by the the |
20 | 'filename': source_img}} | 20 | ``-device BUS-TYPE'' argument combined with ``-drive if=none''. |
21 | self.vm.add_blockdev(self.qmp_to_opts(blockdev)) | 21 | |
22 | - self.vm.add_device('floppy,id=qdev0,drive=source') | 22 | +@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) |
23 | + self.vm.add_device('virtio-blk,id=qdev0,drive=source') | 23 | + |
24 | self.vm.launch() | 24 | +The drive geometry arguments are replaced by the the geometry arguments |
25 | 25 | +that can be specified with the ``-device'' parameter. | |
26 | self.assertIntactSourceBackingChain() | 26 | + |
27 | @@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass): | 27 | +@subsection -drive serial=... (since 2.10.0) |
28 | def testFull(self): | 28 | + |
29 | self.runMirror('full') | 29 | +The drive serial argument is replaced by the the serial argument |
30 | 30 | +that can be specified with the ``-device'' parameter. | |
31 | - node = self.findBlockNode('target', 'qdev0') | 31 | + |
32 | + node = self.findBlockNode('target', | 32 | +@subsection -drive addr=... (since 2.10.0) |
33 | + '/machine/peripheral/qdev0/virtio-backend') | 33 | + |
34 | self.assertCorrectBackingImage(node, None) | 34 | +The drive addr argument is replaced by the the addr argument |
35 | self.assertIntactSourceBackingChain() | 35 | +that can be specified with the ``-device'' parameter. |
36 | 36 | + | |
37 | def testTop(self): | 37 | @subsection -net dump (since 2.10.0) |
38 | self.runMirror('top') | 38 | |
39 | 39 | The ``--net dump'' argument is now replaced with the | |
40 | - node = self.findBlockNode('target', 'qdev0') | ||
41 | + node = self.findBlockNode('target', | ||
42 | + '/machine/peripheral/qdev0/virtio-backend') | ||
43 | self.assertCorrectBackingImage(node, back2_img) | ||
44 | self.assertIntactSourceBackingChain() | ||
45 | |||
46 | def testNone(self): | ||
47 | self.runMirror('none') | ||
48 | |||
49 | - node = self.findBlockNode('target', 'qdev0') | ||
50 | + node = self.findBlockNode('target', | ||
51 | + '/machine/peripheral/qdev0/virtio-backend') | ||
52 | self.assertCorrectBackingImage(node, source_img) | ||
53 | self.assertIntactSourceBackingChain() | ||
54 | |||
55 | @@ -XXX,XX +XXX,XX @@ class TestCommit(BaseClass): | ||
56 | |||
57 | self.vm.event_wait('BLOCK_JOB_COMPLETED') | ||
58 | |||
59 | - node = self.findBlockNode(None, 'qdev0') | ||
60 | + node = self.findBlockNode(None, | ||
61 | + '/machine/peripheral/qdev0/virtio-backend') | ||
62 | self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', | ||
63 | back1_img) | ||
64 | self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', | ||
65 | -- | 40 | -- |
66 | 2.13.6 | 41 | 2.13.6 |
67 | 42 | ||
68 | 43 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Fam Zheng <famz@redhat.com> | 3 | Signed-off-by: Fam Zheng <famz@redhat.com> |
4 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
5 | Reviewed-by: Kashyap Chamarthy <kchamart@redhat.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | --- | 5 | --- |
8 | qemu-img.texi | 8 ++++++++ | 6 | include/block/block_int.h | 1 - |
9 | 1 file changed, 8 insertions(+) | 7 | block/io.c | 18 ------------------ |
8 | 2 files changed, 19 deletions(-) | ||
10 | 9 | ||
11 | diff --git a/qemu-img.texi b/qemu-img.texi | 10 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
12 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/qemu-img.texi | 12 | --- a/include/block/block_int.h |
14 | +++ b/qemu-img.texi | 13 | +++ b/include/block/block_int.h |
15 | @@ -XXX,XX +XXX,XX @@ exclusive with the @var{-O} parameters. It is currently required to also use | 14 | @@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk); |
16 | the @var{-n} parameter to skip image creation. This restriction may be relaxed | 15 | bool blk_dev_is_medium_locked(BlockBackend *blk); |
17 | in a future release. | 16 | |
18 | 17 | void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); | |
19 | +@item --force-share (-U) | 18 | -bool bdrv_requests_pending(BlockDriverState *bs); |
20 | +If specified, @code{qemu-img} will open the image in shared mode, allowing | 19 | |
21 | +other QEMU processes to open it in write mode. For example, this can be used to | 20 | void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); |
22 | +get the image information (with 'info' subcommand) when the image is used by a | 21 | void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); |
23 | +running guest. Note that this could produce inconsistent results because of | 22 | diff --git a/block/io.c b/block/io.c |
24 | +concurrent metadata changes, etc. This option is only allowed when opening | 23 | index XXXXXXX..XXXXXXX 100644 |
25 | +images in read-only mode. | 24 | --- a/block/io.c |
26 | + | 25 | +++ b/block/io.c |
27 | @item --backing-chain | 26 | @@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs) |
28 | will enumerate information about backing files in a disk image chain. Refer | 27 | assert(old >= 1); |
29 | below for further description. | 28 | } |
29 | |||
30 | -/* Check if any requests are in-flight (including throttled requests) */ | ||
31 | -bool bdrv_requests_pending(BlockDriverState *bs) | ||
32 | -{ | ||
33 | - BdrvChild *child; | ||
34 | - | ||
35 | - if (atomic_read(&bs->in_flight)) { | ||
36 | - return true; | ||
37 | - } | ||
38 | - | ||
39 | - QLIST_FOREACH(child, &bs->children, next) { | ||
40 | - if (bdrv_requests_pending(child->bs)) { | ||
41 | - return true; | ||
42 | - } | ||
43 | - } | ||
44 | - | ||
45 | - return false; | ||
46 | -} | ||
47 | - | ||
48 | typedef struct { | ||
49 | Coroutine *co; | ||
50 | BlockDriverState *bs; | ||
30 | -- | 51 | -- |
31 | 2.13.6 | 52 | 2.13.6 |
32 | 53 | ||
33 | 54 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
---|---|---|---|
2 | Reviewed-by: Fam Zheng <famz@redhat.com> | ||
3 | --- | ||
4 | block/io.c | 6 ++++++ | ||
5 | 1 file changed, 6 insertions(+) | ||
2 | 6 | ||
3 | This test tries reopening a qcow2 image with valid and invalid | 7 | diff --git a/block/io.c b/block/io.c |
4 | options. This patch adds l2-cache-entry-size to the set. | ||
5 | |||
6 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
9 | Message-id: 3d3b7d2dbfc020deaef60fb58739b0801eb9517c.1517840877.git.berto@igalia.com | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
11 | --- | ||
12 | tests/qemu-iotests/137 | 5 +++++ | ||
13 | tests/qemu-iotests/137.out | 2 ++ | ||
14 | 2 files changed, 7 insertions(+) | ||
15 | |||
16 | diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 | ||
17 | index XXXXXXX..XXXXXXX 100755 | ||
18 | --- a/tests/qemu-iotests/137 | ||
19 | +++ b/tests/qemu-iotests/137 | ||
20 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO \ | ||
21 | -c "reopen -o overlap-check.inactive-l2=off" \ | ||
22 | -c "reopen -o cache-size=1M" \ | ||
23 | -c "reopen -o l2-cache-size=512k" \ | ||
24 | + -c "reopen -o l2-cache-entry-size=512" \ | ||
25 | + -c "reopen -o l2-cache-entry-size=4k" \ | ||
26 | + -c "reopen -o l2-cache-entry-size=64k" \ | ||
27 | -c "reopen -o refcount-cache-size=128k" \ | ||
28 | -c "reopen -o cache-clean-interval=5" \ | ||
29 | -c "reopen -o cache-clean-interval=0" \ | ||
30 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO \ | ||
31 | -c "reopen -o cache-size=1M,l2-cache-size=2M" \ | ||
32 | -c "reopen -o cache-size=1M,refcount-cache-size=2M" \ | ||
33 | -c "reopen -o l2-cache-size=256T" \ | ||
34 | + -c "reopen -o l2-cache-entry-size=33k" \ | ||
35 | + -c "reopen -o l2-cache-entry-size=128k" \ | ||
36 | -c "reopen -o refcount-cache-size=256T" \ | ||
37 | -c "reopen -o overlap-check=constant,overlap-check.template=all" \ | ||
38 | -c "reopen -o overlap-check=blubb" \ | ||
39 | diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out | ||
40 | index XXXXXXX..XXXXXXX 100644 | 8 | index XXXXXXX..XXXXXXX 100644 |
41 | --- a/tests/qemu-iotests/137.out | 9 | --- a/block/io.c |
42 | +++ b/tests/qemu-iotests/137.out | 10 | +++ b/block/io.c |
43 | @@ -XXX,XX +XXX,XX @@ cache-size, l2-cache-size and refcount-cache-size may not be set the same time | 11 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) |
44 | l2-cache-size may not exceed cache-size | 12 | BdrvNextIterator it; |
45 | refcount-cache-size may not exceed cache-size | 13 | GSList *aio_ctxs = NULL, *ctx; |
46 | L2 cache size too big | 14 | |
47 | +L2 cache entry size must be a power of two between 512 and the cluster size (65536) | 15 | + /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread |
48 | +L2 cache entry size must be a power of two between 512 and the cluster size (65536) | 16 | + * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on |
49 | L2 cache size too big | 17 | + * nodes in several different AioContexts, so make sure we're in the main |
50 | Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') | 18 | + * context. */ |
51 | Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all | 19 | + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); |
20 | + | ||
21 | block_job_pause_all(); | ||
22 | |||
23 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
52 | -- | 24 | -- |
53 | 2.13.6 | 25 | 2.13.6 |
54 | 26 | ||
55 | 27 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively |
---|---|---|---|
2 | and also doesn't notify other parent nodes of children, which both means | ||
3 | that the child nodes are not actually drained, and bdrv_drained_begin() | ||
4 | is providing useful functionality only on a single node. | ||
2 | 5 | ||
3 | This function was only using the BlockDriverState parameter to get the | 6 | To keep things consistent, we also shouldn't call the block driver |
4 | cache table size (since it was equal to the cluster size). This is no | 7 | callbacks recursively. |
5 | longer necessary so this parameter can be removed. | ||
6 | 8 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 9 | A proper recursive drain version that provides an actually working |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 10 | drained section for child nodes will be introduced later. |
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 11 | |
10 | Message-id: 7c1b262344375d52544525f85bbbf0548d5ba575.1517840876.git.berto@igalia.com | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 13 | Reviewed-by: Fam Zheng <famz@redhat.com> |
12 | --- | 14 | --- |
13 | block/qcow2-cache.c | 9 ++++----- | 15 | block/io.c | 16 +++++++++------- |
14 | 1 file changed, 4 insertions(+), 5 deletions(-) | 16 | 1 file changed, 9 insertions(+), 7 deletions(-) |
15 | 17 | ||
16 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | 18 | diff --git a/block/io.c b/block/io.c |
17 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/qcow2-cache.c | 20 | --- a/block/io.c |
19 | +++ b/block/qcow2-cache.c | 21 | +++ b/block/io.c |
20 | @@ -XXX,XX +XXX,XX @@ static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c) | 22 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) |
23 | } | ||
24 | |||
25 | /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ | ||
26 | -static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) | ||
27 | +static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) | ||
28 | { | ||
29 | BdrvChild *child, *tmp; | ||
30 | BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; | ||
31 | @@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) | ||
32 | bdrv_coroutine_enter(bs, data.co); | ||
33 | BDRV_POLL_WHILE(bs, !data.done); | ||
34 | |||
35 | - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { | ||
36 | - bdrv_drain_invoke(child->bs, begin); | ||
37 | + if (recursive) { | ||
38 | + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { | ||
39 | + bdrv_drain_invoke(child->bs, begin, true); | ||
40 | + } | ||
21 | } | 41 | } |
22 | } | 42 | } |
23 | 43 | ||
24 | -static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, | 44 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) |
25 | - int i, int num_tables) | 45 | bdrv_parent_drained_begin(bs); |
26 | +static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables) | ||
27 | { | ||
28 | /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ | ||
29 | #ifdef CONFIG_LINUX | ||
30 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) | ||
31 | } | ||
32 | |||
33 | if (to_clean > 0) { | ||
34 | - qcow2_cache_table_release(bs, c, i - to_clean, to_clean); | ||
35 | + qcow2_cache_table_release(c, i - to_clean, to_clean); | ||
36 | } | ||
37 | } | 46 | } |
38 | 47 | ||
39 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) | 48 | - bdrv_drain_invoke(bs, true); |
40 | c->entries[i].lru_counter = 0; | 49 | + bdrv_drain_invoke(bs, true, false); |
50 | bdrv_drain_recurse(bs); | ||
51 | } | ||
52 | |||
53 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | ||
41 | } | 54 | } |
42 | 55 | ||
43 | - qcow2_cache_table_release(bs, c, 0, c->size); | 56 | /* Re-enable things in child-to-parent order */ |
44 | + qcow2_cache_table_release(c, 0, c->size); | 57 | - bdrv_drain_invoke(bs, false); |
45 | 58 | + bdrv_drain_invoke(bs, false, false); | |
46 | c->lru_counter = 0; | 59 | bdrv_parent_drained_end(bs); |
47 | 60 | aio_enable_external(bdrv_get_aio_context(bs)); | |
48 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table) | ||
49 | c->entries[i].lru_counter = 0; | ||
50 | c->entries[i].dirty = false; | ||
51 | |||
52 | - qcow2_cache_table_release(bs, c, i, 1); | ||
53 | + qcow2_cache_table_release(c, i, 1); | ||
54 | } | 61 | } |
62 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
63 | aio_context_acquire(aio_context); | ||
64 | aio_disable_external(aio_context); | ||
65 | bdrv_parent_drained_begin(bs); | ||
66 | - bdrv_drain_invoke(bs, true); | ||
67 | + bdrv_drain_invoke(bs, true, true); | ||
68 | aio_context_release(aio_context); | ||
69 | |||
70 | if (!g_slist_find(aio_ctxs, aio_context)) { | ||
71 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
72 | |||
73 | /* Re-enable things in child-to-parent order */ | ||
74 | aio_context_acquire(aio_context); | ||
75 | - bdrv_drain_invoke(bs, false); | ||
76 | + bdrv_drain_invoke(bs, false, true); | ||
77 | bdrv_parent_drained_end(bs); | ||
78 | aio_enable_external(aio_context); | ||
79 | aio_context_release(aio_context); | ||
55 | -- | 80 | -- |
56 | 2.13.6 | 81 | 2.13.6 |
57 | 82 | ||
58 | 83 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | The existing test is for bdrv_drain_all_begin/end() only. Generalise the |
---|---|---|---|
2 | test case so that it can be run for the other variants as well. At the | ||
3 | moment this is only bdrv_drain_begin/end(), but in a while, we'll add | ||
4 | another one. | ||
2 | 5 | ||
3 | Similar to offset_to_l2_index(), this function takes a guest offset | 6 | Also, add a backing file to the test node to test whether the operations |
4 | and returns the index in the L2 slice that contains its L2 entry. | 7 | work recursively. |
5 | 8 | ||
6 | An L2 slice has currently the same size as an L2 table (one cluster), | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | so both functions return the same value for now. | 10 | --- |
11 | tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++----- | ||
12 | 1 file changed, 62 insertions(+), 7 deletions(-) | ||
8 | 13 | ||
9 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 14 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
12 | Message-id: a1c45c5c5a76146dd1712d8d1e7b409ad539c718.1517840877.git.berto@igalia.com | ||
13 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
14 | --- | ||
15 | block/qcow2.h | 5 +++++ | ||
16 | 1 file changed, 5 insertions(+) | ||
17 | |||
18 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
19 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/block/qcow2.h | 16 | --- a/tests/test-bdrv-drain.c |
21 | +++ b/block/qcow2.h | 17 | +++ b/tests/test-bdrv-drain.c |
22 | @@ -XXX,XX +XXX,XX @@ static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) | 18 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = { |
23 | return (offset >> s->cluster_bits) & (s->l2_size - 1); | 19 | |
20 | .bdrv_co_drain_begin = bdrv_test_co_drain_begin, | ||
21 | .bdrv_co_drain_end = bdrv_test_co_drain_end, | ||
22 | + | ||
23 | + .bdrv_child_perm = bdrv_format_default_perms, | ||
24 | }; | ||
25 | |||
26 | static void aio_ret_cb(void *opaque, int ret) | ||
27 | @@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret) | ||
28 | *aio_ret = ret; | ||
24 | } | 29 | } |
25 | 30 | ||
26 | +static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset) | 31 | -static void test_drv_cb_drain_all(void) |
32 | +enum drain_type { | ||
33 | + BDRV_DRAIN_ALL, | ||
34 | + BDRV_DRAIN, | ||
35 | +}; | ||
36 | + | ||
37 | +static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) | ||
27 | +{ | 38 | +{ |
28 | + return (offset >> s->cluster_bits) & (s->l2_slice_size - 1); | 39 | + switch (drain_type) { |
40 | + case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; | ||
41 | + case BDRV_DRAIN: bdrv_drained_begin(bs); break; | ||
42 | + default: g_assert_not_reached(); | ||
43 | + } | ||
29 | +} | 44 | +} |
30 | + | 45 | + |
31 | static inline int64_t align_offset(int64_t offset, int n) | 46 | +static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) |
47 | +{ | ||
48 | + switch (drain_type) { | ||
49 | + case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; | ||
50 | + case BDRV_DRAIN: bdrv_drained_end(bs); break; | ||
51 | + default: g_assert_not_reached(); | ||
52 | + } | ||
53 | +} | ||
54 | + | ||
55 | +static void test_drv_cb_common(enum drain_type drain_type, bool recursive) | ||
32 | { | 56 | { |
33 | offset = (offset + n - 1) & ~(n - 1); | 57 | BlockBackend *blk; |
58 | - BlockDriverState *bs; | ||
59 | - BDRVTestState *s; | ||
60 | + BlockDriverState *bs, *backing; | ||
61 | + BDRVTestState *s, *backing_s; | ||
62 | BlockAIOCB *acb; | ||
63 | int aio_ret; | ||
64 | |||
65 | @@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void) | ||
66 | s = bs->opaque; | ||
67 | blk_insert_bs(blk, bs, &error_abort); | ||
68 | |||
69 | + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); | ||
70 | + backing_s = backing->opaque; | ||
71 | + bdrv_set_backing_hd(bs, backing, &error_abort); | ||
72 | + | ||
73 | /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ | ||
74 | g_assert_cmpint(s->drain_count, ==, 0); | ||
75 | - bdrv_drain_all_begin(); | ||
76 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
77 | + | ||
78 | + do_drain_begin(drain_type, bs); | ||
79 | + | ||
80 | g_assert_cmpint(s->drain_count, ==, 1); | ||
81 | - bdrv_drain_all_end(); | ||
82 | + g_assert_cmpint(backing_s->drain_count, ==, !!recursive); | ||
83 | + | ||
84 | + do_drain_end(drain_type, bs); | ||
85 | + | ||
86 | g_assert_cmpint(s->drain_count, ==, 0); | ||
87 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
88 | |||
89 | /* Now do the same while a request is pending */ | ||
90 | aio_ret = -EINPROGRESS; | ||
91 | @@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void) | ||
92 | g_assert_cmpint(aio_ret, ==, -EINPROGRESS); | ||
93 | |||
94 | g_assert_cmpint(s->drain_count, ==, 0); | ||
95 | - bdrv_drain_all_begin(); | ||
96 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
97 | + | ||
98 | + do_drain_begin(drain_type, bs); | ||
99 | + | ||
100 | g_assert_cmpint(aio_ret, ==, 0); | ||
101 | g_assert_cmpint(s->drain_count, ==, 1); | ||
102 | - bdrv_drain_all_end(); | ||
103 | + g_assert_cmpint(backing_s->drain_count, ==, !!recursive); | ||
104 | + | ||
105 | + do_drain_end(drain_type, bs); | ||
106 | + | ||
107 | g_assert_cmpint(s->drain_count, ==, 0); | ||
108 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
109 | |||
110 | + bdrv_unref(backing); | ||
111 | bdrv_unref(bs); | ||
112 | blk_unref(blk); | ||
113 | } | ||
114 | |||
115 | +static void test_drv_cb_drain_all(void) | ||
116 | +{ | ||
117 | + test_drv_cb_common(BDRV_DRAIN_ALL, true); | ||
118 | +} | ||
119 | + | ||
120 | +static void test_drv_cb_drain(void) | ||
121 | +{ | ||
122 | + test_drv_cb_common(BDRV_DRAIN, false); | ||
123 | +} | ||
124 | + | ||
125 | int main(int argc, char **argv) | ||
126 | { | ||
127 | bdrv_init(); | ||
128 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
129 | g_test_init(&argc, &argv, NULL); | ||
130 | |||
131 | g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); | ||
132 | + g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); | ||
133 | |||
134 | return g_test_run(); | ||
135 | } | ||
34 | -- | 136 | -- |
35 | 2.13.6 | 137 | 2.13.6 |
36 | 138 | ||
37 | 139 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | This is currently only working correctly for bdrv_drain(), not for |
---|---|---|---|
2 | bdrv_drain_all(). Leave a comment for the drain_all case, we'll address | ||
3 | it later. | ||
2 | 4 | ||
3 | This function was only using the BlockDriverState parameter to pass it | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | to qcow2_cache_table_release(). This is no longer necessary so this | 6 | --- |
5 | parameter can be removed. | 7 | tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ |
8 | 1 file changed, 45 insertions(+) | ||
6 | 9 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 10 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: b74f17591af52f201de0ea3a3b2dd0a81932334d.1517840876.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2.h | 2 +- | ||
14 | block/qcow2-cache.c | 2 +- | ||
15 | block/qcow2.c | 4 ++-- | ||
16 | 3 files changed, 4 insertions(+), 4 deletions(-) | ||
17 | |||
18 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
19 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/block/qcow2.h | 12 | --- a/tests/test-bdrv-drain.c |
21 | +++ b/block/qcow2.h | 13 | +++ b/tests/test-bdrv-drain.c |
22 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, | 14 | @@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void) |
23 | Qcow2Cache *dependency); | 15 | test_drv_cb_common(BDRV_DRAIN, false); |
24 | void qcow2_cache_depends_on_flush(Qcow2Cache *c); | ||
25 | |||
26 | -void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c); | ||
27 | +void qcow2_cache_clean_unused(Qcow2Cache *c); | ||
28 | int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); | ||
29 | |||
30 | int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||
31 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block/qcow2-cache.c | ||
34 | +++ b/block/qcow2-cache.c | ||
35 | @@ -XXX,XX +XXX,XX @@ static inline bool can_clean_entry(Qcow2Cache *c, int i) | ||
36 | t->lru_counter <= c->cache_clean_lru_counter; | ||
37 | } | 16 | } |
38 | 17 | ||
39 | -void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) | 18 | +static void test_quiesce_common(enum drain_type drain_type, bool recursive) |
40 | +void qcow2_cache_clean_unused(Qcow2Cache *c) | 19 | +{ |
20 | + BlockBackend *blk; | ||
21 | + BlockDriverState *bs, *backing; | ||
22 | + | ||
23 | + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||
24 | + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, | ||
25 | + &error_abort); | ||
26 | + blk_insert_bs(blk, bs, &error_abort); | ||
27 | + | ||
28 | + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); | ||
29 | + bdrv_set_backing_hd(bs, backing, &error_abort); | ||
30 | + | ||
31 | + g_assert_cmpint(bs->quiesce_counter, ==, 0); | ||
32 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
33 | + | ||
34 | + do_drain_begin(drain_type, bs); | ||
35 | + | ||
36 | + g_assert_cmpint(bs->quiesce_counter, ==, 1); | ||
37 | + g_assert_cmpint(backing->quiesce_counter, ==, !!recursive); | ||
38 | + | ||
39 | + do_drain_end(drain_type, bs); | ||
40 | + | ||
41 | + g_assert_cmpint(bs->quiesce_counter, ==, 0); | ||
42 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
43 | + | ||
44 | + bdrv_unref(backing); | ||
45 | + bdrv_unref(bs); | ||
46 | + blk_unref(blk); | ||
47 | +} | ||
48 | + | ||
49 | +static void test_quiesce_drain_all(void) | ||
50 | +{ | ||
51 | + // XXX drain_all doesn't quiesce | ||
52 | + //test_quiesce_common(BDRV_DRAIN_ALL, true); | ||
53 | +} | ||
54 | + | ||
55 | +static void test_quiesce_drain(void) | ||
56 | +{ | ||
57 | + test_quiesce_common(BDRV_DRAIN, false); | ||
58 | +} | ||
59 | + | ||
60 | int main(int argc, char **argv) | ||
41 | { | 61 | { |
42 | int i = 0; | 62 | bdrv_init(); |
43 | while (i < c->size) { | 63 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) |
44 | diff --git a/block/qcow2.c b/block/qcow2.c | 64 | g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); |
45 | index XXXXXXX..XXXXXXX 100644 | 65 | g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); |
46 | --- a/block/qcow2.c | 66 | |
47 | +++ b/block/qcow2.c | 67 | + g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); |
48 | @@ -XXX,XX +XXX,XX @@ static void cache_clean_timer_cb(void *opaque) | 68 | + g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); |
49 | { | 69 | + |
50 | BlockDriverState *bs = opaque; | 70 | return g_test_run(); |
51 | BDRVQcow2State *s = bs->opaque; | ||
52 | - qcow2_cache_clean_unused(bs, s->l2_table_cache); | ||
53 | - qcow2_cache_clean_unused(bs, s->refcount_block_cache); | ||
54 | + qcow2_cache_clean_unused(s->l2_table_cache); | ||
55 | + qcow2_cache_clean_unused(s->refcount_block_cache); | ||
56 | timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + | ||
57 | (int64_t) s->cache_clean_interval * 1000); | ||
58 | } | 71 | } |
59 | -- | 72 | -- |
60 | 2.13.6 | 73 | 2.13.6 |
61 | 74 | ||
62 | 75 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Block jobs already paused themselves when their main BlockBackend |
---|---|---|---|
2 | entered a drained section. This is not good enough: We also want to | ||
3 | pause a block job and may not submit new requests if, for example, the | ||
4 | mirror target node should be drained. | ||
2 | 5 | ||
3 | The table size in the qcow2 cache is currently equal to the cluster | 6 | This implements .drained_begin/end callbacks in child_job in order to |
4 | size. This doesn't allow us to use the cache memory efficiently, | 7 | consider all block nodes related to the job, and removes the |
5 | particularly with large cluster sizes, so we need to be able to have | 8 | BlockBackend callbacks which are unnecessary now because the root of the |
6 | smaller cache tables that are independent from the cluster size. This | 9 | job main BlockBackend is always referenced with a child_job, too. |
7 | patch adds a new field to Qcow2Cache that we can use instead of the | ||
8 | cluster size. | ||
9 | 10 | ||
10 | The current table size is still being initialized to the cluster size, | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | so there are no semantic changes yet, but this patch will allow us to | 12 | --- |
12 | prepare the rest of the code and simplify a few function calls. | 13 | blockjob.c | 22 +++++++++------------- |
14 | 1 file changed, 9 insertions(+), 13 deletions(-) | ||
13 | 15 | ||
14 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 16 | diff --git a/blockjob.c b/blockjob.c |
15 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
16 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
17 | Message-id: 67a1bf9e55f417005c567bead95a018dc34bc687.1517840876.git.berto@igalia.com | ||
18 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
19 | --- | ||
20 | block/qcow2-cache.c | 29 ++++++++++++++--------------- | ||
21 | 1 file changed, 14 insertions(+), 15 deletions(-) | ||
22 | |||
23 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
24 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/qcow2-cache.c | 18 | --- a/blockjob.c |
26 | +++ b/block/qcow2-cache.c | 19 | +++ b/blockjob.c |
27 | @@ -XXX,XX +XXX,XX @@ struct Qcow2Cache { | 20 | @@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c) |
28 | Qcow2CachedTable *entries; | 21 | job->id); |
29 | struct Qcow2Cache *depends; | 22 | } |
30 | int size; | 23 | |
31 | + int table_size; | 24 | -static const BdrvChildRole child_job = { |
32 | bool depends_on_flush; | 25 | - .get_parent_desc = child_job_get_parent_desc, |
33 | void *table_array; | 26 | - .stay_at_node = true, |
34 | uint64_t lru_counter; | 27 | -}; |
35 | @@ -XXX,XX +XXX,XX @@ struct Qcow2Cache { | 28 | - |
36 | static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, | 29 | -static void block_job_drained_begin(void *opaque) |
37 | Qcow2Cache *c, int table) | 30 | +static void child_job_drained_begin(BdrvChild *c) |
38 | { | 31 | { |
39 | - BDRVQcow2State *s = bs->opaque; | 32 | - BlockJob *job = opaque; |
40 | - return (uint8_t *) c->table_array + (size_t) table * s->cluster_size; | 33 | + BlockJob *job = c->opaque; |
41 | + return (uint8_t *) c->table_array + (size_t) table * c->table_size; | 34 | block_job_pause(job); |
42 | } | 35 | } |
43 | 36 | ||
44 | static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, | 37 | -static void block_job_drained_end(void *opaque) |
45 | Qcow2Cache *c, void *table) | 38 | +static void child_job_drained_end(BdrvChild *c) |
46 | { | 39 | { |
47 | - BDRVQcow2State *s = bs->opaque; | 40 | - BlockJob *job = opaque; |
48 | ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; | 41 | + BlockJob *job = c->opaque; |
49 | - int idx = table_offset / s->cluster_size; | 42 | block_job_resume(job); |
50 | - assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); | ||
51 | + int idx = table_offset / c->table_size; | ||
52 | + assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0); | ||
53 | return idx; | ||
54 | } | 43 | } |
55 | 44 | ||
56 | @@ -XXX,XX +XXX,XX @@ static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, | 45 | -static const BlockDevOps block_job_dev_ops = { |
57 | { | 46 | - .drained_begin = block_job_drained_begin, |
58 | /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ | 47 | - .drained_end = block_job_drained_end, |
59 | #ifdef CONFIG_LINUX | 48 | +static const BdrvChildRole child_job = { |
60 | - BDRVQcow2State *s = bs->opaque; | 49 | + .get_parent_desc = child_job_get_parent_desc, |
61 | void *t = qcow2_cache_get_table_addr(bs, c, i); | 50 | + .drained_begin = child_job_drained_begin, |
62 | int align = getpagesize(); | 51 | + .drained_end = child_job_drained_end, |
63 | - size_t mem_size = (size_t) s->cluster_size * num_tables; | 52 | + .stay_at_node = true, |
64 | + size_t mem_size = (size_t) c->table_size * num_tables; | 53 | }; |
65 | size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; | 54 | |
66 | size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); | 55 | void block_job_remove_all_bdrv(BlockJob *job) |
67 | if (mem_size > offset && length > 0) { | 56 | @@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, |
68 | @@ -XXX,XX +XXX,XX @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) | 57 | block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); |
69 | 58 | bs->job = job; | |
70 | c = g_new0(Qcow2Cache, 1); | 59 | |
71 | c->size = num_tables; | 60 | - blk_set_dev_ops(blk, &block_job_dev_ops, job); |
72 | + c->table_size = s->cluster_size; | 61 | bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); |
73 | c->entries = g_try_new0(Qcow2CachedTable, num_tables); | 62 | |
74 | c->table_array = qemu_try_blockalign(bs->file->bs, | 63 | QLIST_INSERT_HEAD(&block_jobs, job, job_list); |
75 | - (size_t) num_tables * s->cluster_size); | ||
76 | + (size_t) num_tables * c->table_size); | ||
77 | |||
78 | if (!c->entries || !c->table_array) { | ||
79 | qemu_vfree(c->table_array); | ||
80 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||
81 | |||
82 | if (c == s->refcount_block_cache) { | ||
83 | ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, | ||
84 | - c->entries[i].offset, s->cluster_size); | ||
85 | + c->entries[i].offset, c->table_size); | ||
86 | } else if (c == s->l2_table_cache) { | ||
87 | ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, | ||
88 | - c->entries[i].offset, s->cluster_size); | ||
89 | + c->entries[i].offset, c->table_size); | ||
90 | } else { | ||
91 | ret = qcow2_pre_write_overlap_check(bs, 0, | ||
92 | - c->entries[i].offset, s->cluster_size); | ||
93 | + c->entries[i].offset, c->table_size); | ||
94 | } | ||
95 | |||
96 | if (ret < 0) { | ||
97 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||
98 | } | ||
99 | |||
100 | ret = bdrv_pwrite(bs->file, c->entries[i].offset, | ||
101 | - qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); | ||
102 | + qcow2_cache_get_table_addr(bs, c, i), c->table_size); | ||
103 | if (ret < 0) { | ||
104 | return ret; | ||
105 | } | ||
106 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||
107 | trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, | ||
108 | offset, read_from_disk); | ||
109 | |||
110 | - if (offset_into_cluster(s, offset)) { | ||
111 | + if (!QEMU_IS_ALIGNED(offset, c->table_size)) { | ||
112 | qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " | ||
113 | "cache: Offset %#" PRIx64 " is unaligned", | ||
114 | qcow2_cache_get_name(s, c), offset); | ||
115 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||
116 | } | ||
117 | |||
118 | /* Check if the table is already cached */ | ||
119 | - i = lookup_index = (offset / s->cluster_size * 4) % c->size; | ||
120 | + i = lookup_index = (offset / c->table_size * 4) % c->size; | ||
121 | do { | ||
122 | const Qcow2CachedTable *t = &c->entries[i]; | ||
123 | if (t->offset == offset) { | ||
124 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||
125 | |||
126 | ret = bdrv_pread(bs->file, offset, | ||
127 | qcow2_cache_get_table_addr(bs, c, i), | ||
128 | - s->cluster_size); | ||
129 | + c->table_size); | ||
130 | if (ret < 0) { | ||
131 | return ret; | ||
132 | } | ||
133 | -- | 64 | -- |
134 | 2.13.6 | 65 | 2.13.6 |
135 | 66 | ||
136 | 67 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | Block jobs must be paused if any of the involved nodes are drained. |
---|---|---|---|
2 | 2 | ||
3 | Instead of expecting the current size to be 0, query it and allocate | ||
4 | only the area [current_size, offset) if preallocation is requested. | ||
5 | |||
6 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 3 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 4 | --- |
10 | block/gluster.c | 21 +++++++++++++++++++-- | 5 | tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ |
11 | 1 file changed, 19 insertions(+), 2 deletions(-) | 6 | 1 file changed, 121 insertions(+) |
12 | 7 | ||
13 | diff --git a/block/gluster.c b/block/gluster.c | 8 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
14 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/gluster.c | 10 | --- a/tests/test-bdrv-drain.c |
16 | +++ b/block/gluster.c | 11 | +++ b/tests/test-bdrv-drain.c |
17 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, | 12 | @@ -XXX,XX +XXX,XX @@ |
18 | static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset, | 13 | |
19 | PreallocMode prealloc, Error **errp) | 14 | #include "qemu/osdep.h" |
20 | { | 15 | #include "block/block.h" |
21 | + int64_t current_length; | 16 | +#include "block/blockjob_int.h" |
17 | #include "sysemu/block-backend.h" | ||
18 | #include "qapi/error.h" | ||
19 | |||
20 | @@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void) | ||
21 | test_quiesce_common(BDRV_DRAIN, false); | ||
22 | } | ||
23 | |||
22 | + | 24 | + |
23 | + current_length = glfs_lseek(fd, 0, SEEK_END); | 25 | +typedef struct TestBlockJob { |
24 | + if (current_length < 0) { | 26 | + BlockJob common; |
25 | + error_setg_errno(errp, errno, "Failed to determine current size"); | 27 | + bool should_complete; |
26 | + return -errno; | 28 | +} TestBlockJob; |
29 | + | ||
30 | +static void test_job_completed(BlockJob *job, void *opaque) | ||
31 | +{ | ||
32 | + block_job_completed(job, 0); | ||
33 | +} | ||
34 | + | ||
35 | +static void coroutine_fn test_job_start(void *opaque) | ||
36 | +{ | ||
37 | + TestBlockJob *s = opaque; | ||
38 | + | ||
39 | + while (!s->should_complete) { | ||
40 | + block_job_sleep_ns(&s->common, 100000); | ||
27 | + } | 41 | + } |
28 | + | 42 | + |
29 | + if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { | 43 | + block_job_defer_to_main_loop(&s->common, test_job_completed, NULL); |
30 | + error_setg(errp, "Cannot use preallocation for shrinking files"); | 44 | +} |
31 | + return -ENOTSUP; | 45 | + |
46 | +static void test_job_complete(BlockJob *job, Error **errp) | ||
47 | +{ | ||
48 | + TestBlockJob *s = container_of(job, TestBlockJob, common); | ||
49 | + s->should_complete = true; | ||
50 | +} | ||
51 | + | ||
52 | +BlockJobDriver test_job_driver = { | ||
53 | + .instance_size = sizeof(TestBlockJob), | ||
54 | + .start = test_job_start, | ||
55 | + .complete = test_job_complete, | ||
56 | +}; | ||
57 | + | ||
58 | +static void test_blockjob_common(enum drain_type drain_type) | ||
59 | +{ | ||
60 | + BlockBackend *blk_src, *blk_target; | ||
61 | + BlockDriverState *src, *target; | ||
62 | + BlockJob *job; | ||
63 | + int ret; | ||
64 | + | ||
65 | + src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, | ||
66 | + &error_abort); | ||
67 | + blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||
68 | + blk_insert_bs(blk_src, src, &error_abort); | ||
69 | + | ||
70 | + target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, | ||
71 | + &error_abort); | ||
72 | + blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||
73 | + blk_insert_bs(blk_target, target, &error_abort); | ||
74 | + | ||
75 | + job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0, | ||
76 | + 0, NULL, NULL, &error_abort); | ||
77 | + block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); | ||
78 | + block_job_start(job); | ||
79 | + | ||
80 | + g_assert_cmpint(job->pause_count, ==, 0); | ||
81 | + g_assert_false(job->paused); | ||
82 | + g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ | ||
83 | + | ||
84 | + do_drain_begin(drain_type, src); | ||
85 | + | ||
86 | + if (drain_type == BDRV_DRAIN_ALL) { | ||
87 | + /* bdrv_drain_all() drains both src and target, and involves an | ||
88 | + * additional block_job_pause_all() */ | ||
89 | + g_assert_cmpint(job->pause_count, ==, 3); | ||
90 | + } else { | ||
91 | + g_assert_cmpint(job->pause_count, ==, 1); | ||
32 | + } | 92 | + } |
93 | + /* XXX We don't wait until the job is actually paused. Is this okay? */ | ||
94 | + /* g_assert_true(job->paused); */ | ||
95 | + g_assert_false(job->busy); /* The job is paused */ | ||
33 | + | 96 | + |
34 | + if (current_length == offset) { | 97 | + do_drain_end(drain_type, src); |
35 | + return 0; | 98 | + |
99 | + g_assert_cmpint(job->pause_count, ==, 0); | ||
100 | + g_assert_false(job->paused); | ||
101 | + g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ | ||
102 | + | ||
103 | + do_drain_begin(drain_type, target); | ||
104 | + | ||
105 | + if (drain_type == BDRV_DRAIN_ALL) { | ||
106 | + /* bdrv_drain_all() drains both src and target, and involves an | ||
107 | + * additional block_job_pause_all() */ | ||
108 | + g_assert_cmpint(job->pause_count, ==, 3); | ||
109 | + } else { | ||
110 | + g_assert_cmpint(job->pause_count, ==, 1); | ||
36 | + } | 111 | + } |
112 | + /* XXX We don't wait until the job is actually paused. Is this okay? */ | ||
113 | + /* g_assert_true(job->paused); */ | ||
114 | + g_assert_false(job->busy); /* The job is paused */ | ||
37 | + | 115 | + |
38 | switch (prealloc) { | 116 | + do_drain_end(drain_type, target); |
39 | #ifdef CONFIG_GLUSTERFS_FALLOCATE | 117 | + |
40 | case PREALLOC_MODE_FALLOC: | 118 | + g_assert_cmpint(job->pause_count, ==, 0); |
41 | - if (glfs_fallocate(fd, 0, 0, offset)) { | 119 | + g_assert_false(job->paused); |
42 | + if (glfs_fallocate(fd, 0, current_length, offset - current_length)) { | 120 | + g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ |
43 | error_setg_errno(errp, errno, "Could not preallocate data"); | 121 | + |
44 | return -errno; | 122 | + ret = block_job_complete_sync(job, &error_abort); |
45 | } | 123 | + g_assert_cmpint(ret, ==, 0); |
46 | @@ -XXX,XX +XXX,XX @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset, | 124 | + |
47 | error_setg_errno(errp, errno, "Could not resize file"); | 125 | + blk_unref(blk_src); |
48 | return -errno; | 126 | + blk_unref(blk_target); |
49 | } | 127 | + bdrv_unref(src); |
50 | - if (glfs_zerofill(fd, 0, offset)) { | 128 | + bdrv_unref(target); |
51 | + if (glfs_zerofill(fd, current_length, offset - current_length)) { | 129 | +} |
52 | error_setg_errno(errp, errno, "Could not zerofill the new area"); | 130 | + |
53 | return -errno; | 131 | +static void test_blockjob_drain_all(void) |
54 | } | 132 | +{ |
133 | + test_blockjob_common(BDRV_DRAIN_ALL); | ||
134 | +} | ||
135 | + | ||
136 | +static void test_blockjob_drain(void) | ||
137 | +{ | ||
138 | + test_blockjob_common(BDRV_DRAIN); | ||
139 | +} | ||
140 | + | ||
141 | int main(int argc, char **argv) | ||
142 | { | ||
143 | bdrv_init(); | ||
144 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
145 | g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); | ||
146 | g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); | ||
147 | |||
148 | + g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); | ||
149 | + g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); | ||
150 | + | ||
151 | return g_test_run(); | ||
152 | } | ||
55 | -- | 153 | -- |
56 | 2.13.6 | 154 | 2.13.6 |
57 | 155 | ||
58 | 156 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | Block jobs are already paused using the BdrvChildRole drain callbacks, |
---|---|---|---|
2 | so we don't need an additional block_job_pause_all() call. | ||
2 | 3 | ||
3 | sd_prealloc() will now preallocate the area [old_size, new_size). As | ||
4 | before, it rounds to buf_size and may thus overshoot and preallocate | ||
5 | areas that were not requested to be preallocated. For image creation, | ||
6 | this is no change in behavior. For truncation, this is in accordance | ||
7 | with the documentation for preallocated truncation. | ||
8 | |||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 5 | --- |
13 | block/sheepdog.c | 16 +++++----------- | 6 | block/io.c | 4 ---- |
14 | 1 file changed, 5 insertions(+), 11 deletions(-) | 7 | tests/test-bdrv-drain.c | 10 ++++------ |
8 | 2 files changed, 4 insertions(+), 10 deletions(-) | ||
15 | 9 | ||
16 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 10 | diff --git a/block/io.c b/block/io.c |
17 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/sheepdog.c | 12 | --- a/block/io.c |
19 | +++ b/block/sheepdog.c | 13 | +++ b/block/io.c |
20 | @@ -XXX,XX +XXX,XX @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, | 14 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) |
21 | return 0; | 15 | * context. */ |
16 | assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | ||
17 | |||
18 | - block_job_pause_all(); | ||
19 | - | ||
20 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
21 | AioContext *aio_context = bdrv_get_aio_context(bs); | ||
22 | |||
23 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
24 | aio_enable_external(aio_context); | ||
25 | aio_context_release(aio_context); | ||
26 | } | ||
27 | - | ||
28 | - block_job_resume_all(); | ||
22 | } | 29 | } |
23 | 30 | ||
24 | -static int sd_prealloc(BlockDriverState *bs, Error **errp) | 31 | void bdrv_drain_all(void) |
25 | +static int sd_prealloc(BlockDriverState *bs, int64_t old_size, int64_t new_size, | 32 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
26 | + Error **errp) | 33 | index XXXXXXX..XXXXXXX 100644 |
27 | { | 34 | --- a/tests/test-bdrv-drain.c |
28 | BlockBackend *blk = NULL; | 35 | +++ b/tests/test-bdrv-drain.c |
29 | BDRVSheepdogState *base = bs->opaque; | 36 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type) |
30 | unsigned long buf_size; | 37 | do_drain_begin(drain_type, src); |
31 | uint32_t idx, max_idx; | 38 | |
32 | uint32_t object_size; | 39 | if (drain_type == BDRV_DRAIN_ALL) { |
33 | - int64_t vdi_size; | 40 | - /* bdrv_drain_all() drains both src and target, and involves an |
34 | void *buf = NULL; | 41 | - * additional block_job_pause_all() */ |
35 | int ret; | 42 | - g_assert_cmpint(job->pause_count, ==, 3); |
36 | 43 | + /* bdrv_drain_all() drains both src and target */ | |
37 | @@ -XXX,XX +XXX,XX @@ static int sd_prealloc(BlockDriverState *bs, Error **errp) | 44 | + g_assert_cmpint(job->pause_count, ==, 2); |
38 | 45 | } else { | |
39 | blk_set_allow_write_beyond_eof(blk, true); | 46 | g_assert_cmpint(job->pause_count, ==, 1); |
40 | 47 | } | |
41 | - vdi_size = blk_getlength(blk); | 48 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type) |
42 | - if (vdi_size < 0) { | 49 | do_drain_begin(drain_type, target); |
43 | - ret = vdi_size; | 50 | |
44 | - goto out; | 51 | if (drain_type == BDRV_DRAIN_ALL) { |
45 | - } | 52 | - /* bdrv_drain_all() drains both src and target, and involves an |
46 | - | 53 | - * additional block_job_pause_all() */ |
47 | object_size = (UINT32_C(1) << base->inode.block_size_shift); | 54 | - g_assert_cmpint(job->pause_count, ==, 3); |
48 | buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); | 55 | + /* bdrv_drain_all() drains both src and target */ |
49 | buf = g_malloc0(buf_size); | 56 | + g_assert_cmpint(job->pause_count, ==, 2); |
50 | 57 | } else { | |
51 | - max_idx = DIV_ROUND_UP(vdi_size, buf_size); | 58 | g_assert_cmpint(job->pause_count, ==, 1); |
52 | + max_idx = DIV_ROUND_UP(new_size, buf_size); | ||
53 | |||
54 | - for (idx = 0; idx < max_idx; idx++) { | ||
55 | + for (idx = old_size / buf_size; idx < max_idx; idx++) { | ||
56 | /* | ||
57 | * The created image can be a cloned image, so we need to read | ||
58 | * a data from the source image. | ||
59 | @@ -XXX,XX +XXX,XX @@ static int sd_create(const char *filename, QemuOpts *opts, | ||
60 | goto out; | ||
61 | } | ||
62 | |||
63 | - ret = sd_prealloc(bs, errp); | ||
64 | + ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp); | ||
65 | |||
66 | bdrv_unref(bs); | ||
67 | } | 59 | } |
68 | -- | 60 | -- |
69 | 2.13.6 | 61 | 2.13.6 |
70 | 62 | ||
71 | 63 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | bdrv_do_drained_begin() restricts the call of parent callbacks and |
---|---|---|---|
2 | aio_disable_external() to the outermost drain section, but the block | ||
3 | driver callbacks are always called. bdrv_do_drained_end() must match | ||
4 | this behaviour, otherwise nodes stay drained even if begin/end calls | ||
5 | were balanced. | ||
2 | 6 | ||
3 | This function doesn't need any changes to support L2 slices, but since | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | it's now dealing with slices intead of full tables, the l2_table | 8 | --- |
5 | variable is renamed for clarity. | 9 | block/io.c | 12 +++++++----- |
10 | 1 file changed, 7 insertions(+), 5 deletions(-) | ||
6 | 11 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 12 | diff --git a/block/io.c b/block/io.c |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 812b0c3505bb1687e51285dccf1a94f0cecb1f74.1517840877.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2-cluster.c | 8 ++++---- | ||
14 | 1 file changed, 4 insertions(+), 4 deletions(-) | ||
15 | |||
16 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/qcow2-cluster.c | 14 | --- a/block/io.c |
19 | +++ b/block/qcow2-cluster.c | 15 | +++ b/block/io.c |
20 | @@ -XXX,XX +XXX,XX @@ fail: | 16 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) |
17 | |||
18 | void bdrv_drained_end(BlockDriverState *bs) | ||
19 | { | ||
20 | + int old_quiesce_counter; | ||
21 | + | ||
22 | if (qemu_in_coroutine()) { | ||
23 | bdrv_co_yield_to_drain(bs, false); | ||
24 | return; | ||
25 | } | ||
26 | assert(bs->quiesce_counter > 0); | ||
27 | - if (atomic_fetch_dec(&bs->quiesce_counter) > 1) { | ||
28 | - return; | ||
29 | - } | ||
30 | + old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter); | ||
31 | |||
32 | /* Re-enable things in child-to-parent order */ | ||
33 | bdrv_drain_invoke(bs, false, false); | ||
34 | - bdrv_parent_drained_end(bs); | ||
35 | - aio_enable_external(bdrv_get_aio_context(bs)); | ||
36 | + if (old_quiesce_counter == 1) { | ||
37 | + bdrv_parent_drained_end(bs); | ||
38 | + aio_enable_external(bdrv_get_aio_context(bs)); | ||
39 | + } | ||
21 | } | 40 | } |
22 | 41 | ||
23 | /* | 42 | /* |
24 | - * Checks how many clusters in a given L2 table are contiguous in the image | ||
25 | + * Checks how many clusters in a given L2 slice are contiguous in the image | ||
26 | * file. As soon as one of the flags in the bitmask stop_flags changes compared | ||
27 | * to the first cluster, the search is stopped and the cluster is not counted | ||
28 | * as contiguous. (This allows it, for example, to stop at the first compressed | ||
29 | * cluster which may require a different handling) | ||
30 | */ | ||
31 | static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||
32 | - uint64_t *l2_table, uint64_t stop_flags) | ||
33 | + uint64_t *l2_slice, uint64_t stop_flags) | ||
34 | { | ||
35 | int i; | ||
36 | QCow2ClusterType first_cluster_type; | ||
37 | uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; | ||
38 | - uint64_t first_entry = be64_to_cpu(l2_table[0]); | ||
39 | + uint64_t first_entry = be64_to_cpu(l2_slice[0]); | ||
40 | uint64_t offset = first_entry & mask; | ||
41 | |||
42 | if (!offset) { | ||
43 | @@ -XXX,XX +XXX,XX @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||
44 | first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC); | ||
45 | |||
46 | for (i = 0; i < nb_clusters; i++) { | ||
47 | - uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; | ||
48 | + uint64_t l2_entry = be64_to_cpu(l2_slice[i]) & mask; | ||
49 | if (offset + (uint64_t) i * cluster_size != l2_entry) { | ||
50 | break; | ||
51 | } | ||
52 | -- | 43 | -- |
53 | 2.13.6 | 44 | 2.13.6 |
54 | 45 | ||
55 | 46 | diff view generated by jsdifflib |
1 | From: "Daniel P. Berrange" <berrange@redhat.com> | ||
---|---|---|---|
2 | |||
3 | qemu-io puts the TTY into non-canonical mode, which means no EOF processing is | ||
4 | done and thus getchar() will never return the EOF constant. Instead we have to | ||
5 | query the TTY attributes to determine the configured EOF character (usually | ||
6 | Ctrl-D / 0x4), and then explicitly check for that value. This fixes the | ||
7 | regression that prevented Ctrl-D from triggering an exit of qemu-io that has | ||
8 | existed since readline was first added in | ||
9 | |||
10 | commit 0cf17e181798063c3824c8200ba46f25f54faa1a | ||
11 | Author: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | Date: Thu Nov 14 11:54:17 2013 +0100 | ||
13 | |||
14 | qemu-io: use readline.c | ||
15 | |||
16 | It also ensures that a newline is printed when exiting, to complete the | ||
17 | line output by the "qemu-io> " prompt. | ||
18 | |||
19 | Signed-off-by: Daniel P. Berrange <berrange@redhat.com> | ||
20 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
22 | --- | 2 | --- |
23 | qemu-io.c | 27 ++++++++++++++++++++++++++- | 3 | tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ |
24 | 1 file changed, 26 insertions(+), 1 deletion(-) | 4 | 1 file changed, 57 insertions(+) |
25 | 5 | ||
26 | diff --git a/qemu-io.c b/qemu-io.c | 6 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
27 | index XXXXXXX..XXXXXXX 100644 | 7 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/qemu-io.c | 8 | --- a/tests/test-bdrv-drain.c |
29 | +++ b/qemu-io.c | 9 | +++ b/tests/test-bdrv-drain.c |
30 | @@ -XXX,XX +XXX,XX @@ | 10 | @@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret) |
31 | #include "qemu/osdep.h" | 11 | enum drain_type { |
32 | #include <getopt.h> | 12 | BDRV_DRAIN_ALL, |
33 | #include <libgen.h> | 13 | BDRV_DRAIN, |
34 | +#ifndef _WIN32 | 14 | + DRAIN_TYPE_MAX, |
35 | +#include <termios.h> | 15 | }; |
36 | +#endif | 16 | |
37 | 17 | static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) | |
38 | #include "qapi/error.h" | 18 | @@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void) |
39 | #include "qemu-io.h" | 19 | test_quiesce_common(BDRV_DRAIN, false); |
40 | @@ -XXX,XX +XXX,XX @@ static bool imageOpts; | 20 | } |
41 | 21 | ||
42 | static ReadLineState *readline_state; | 22 | +static void test_nested(void) |
43 | 23 | +{ | |
44 | +static int ttyEOF; | 24 | + BlockBackend *blk; |
25 | + BlockDriverState *bs, *backing; | ||
26 | + BDRVTestState *s, *backing_s; | ||
27 | + enum drain_type outer, inner; | ||
45 | + | 28 | + |
46 | +static int get_eof_char(void) | 29 | + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); |
47 | +{ | 30 | + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, |
48 | +#ifdef _WIN32 | 31 | + &error_abort); |
49 | + return 0x4; /* Ctrl-D */ | 32 | + s = bs->opaque; |
50 | +#else | 33 | + blk_insert_bs(blk, bs, &error_abort); |
51 | + struct termios tty; | 34 | + |
52 | + if (tcgetattr(STDIN_FILENO, &tty) != 0) { | 35 | + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); |
53 | + if (errno == ENOTTY) { | 36 | + backing_s = backing->opaque; |
54 | + return 0x0; /* just expect read() == 0 */ | 37 | + bdrv_set_backing_hd(bs, backing, &error_abort); |
55 | + } else { | 38 | + |
56 | + return 0x4; /* Ctrl-D */ | 39 | + for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { |
40 | + for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { | ||
41 | + /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ | ||
42 | + int bs_quiesce = (outer != BDRV_DRAIN_ALL) + | ||
43 | + (inner != BDRV_DRAIN_ALL); | ||
44 | + int backing_quiesce = 0; | ||
45 | + int backing_cb_cnt = (outer != BDRV_DRAIN) + | ||
46 | + (inner != BDRV_DRAIN); | ||
47 | + | ||
48 | + g_assert_cmpint(bs->quiesce_counter, ==, 0); | ||
49 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
50 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
51 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
52 | + | ||
53 | + do_drain_begin(outer, bs); | ||
54 | + do_drain_begin(inner, bs); | ||
55 | + | ||
56 | + g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce); | ||
57 | + g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); | ||
58 | + g_assert_cmpint(s->drain_count, ==, 2); | ||
59 | + g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt); | ||
60 | + | ||
61 | + do_drain_end(inner, bs); | ||
62 | + do_drain_end(outer, bs); | ||
63 | + | ||
64 | + g_assert_cmpint(bs->quiesce_counter, ==, 0); | ||
65 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
66 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
67 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
57 | + } | 68 | + } |
58 | + } | 69 | + } |
59 | + | 70 | + |
60 | + return tty.c_cc[VEOF]; | 71 | + bdrv_unref(backing); |
61 | +#endif | 72 | + bdrv_unref(bs); |
73 | + blk_unref(blk); | ||
62 | +} | 74 | +} |
63 | + | 75 | + |
64 | static int close_f(BlockBackend *blk, int argc, char **argv) | 76 | |
65 | { | 77 | typedef struct TestBlockJob { |
66 | blk_unref(qemuio_blk); | 78 | BlockJob common; |
67 | @@ -XXX,XX +XXX,XX @@ static char *fetchline_readline(void) | ||
68 | readline_start(readline_state, get_prompt(), 0, readline_func, &line); | ||
69 | while (!line) { | ||
70 | int ch = getchar(); | ||
71 | - if (ch == EOF) { | ||
72 | + if (ttyEOF != 0x0 && ch == ttyEOF) { | ||
73 | + printf("\n"); | ||
74 | break; | ||
75 | } | ||
76 | readline_handle_byte(readline_state, ch); | ||
77 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | 79 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) |
78 | qemuio_add_command(&close_cmd); | 80 | g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); |
79 | 81 | g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); | |
80 | if (isatty(STDIN_FILENO)) { | 82 | |
81 | + ttyEOF = get_eof_char(); | 83 | + g_test_add_func("/bdrv-drain/nested", test_nested); |
82 | readline_state = readline_init(readline_printf_func, | 84 | + |
83 | readline_flush_func, | 85 | g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); |
84 | NULL, | 86 | g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); |
87 | |||
85 | -- | 88 | -- |
86 | 2.13.6 | 89 | 2.13.6 |
87 | 90 | ||
88 | 91 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | This is in preparation for subtree drains, i.e. drained sections that |
---|---|---|---|
2 | 2 | affect not only a single node, but recursively all child nodes, too. | |
3 | Suggested-by: Stefan Hajnoczi <stefanha@redhat.com> | 3 | |
4 | Signed-off-by: Fam Zheng <famz@redhat.com> | 4 | Calling the parent callbacks for drain is pointless when we just came |
5 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 5 | from that parent node recursively and leads to multiple increases of |
6 | Reviewed-by: Kashyap Chamarthy <kchamart@redhat.com> | 6 | bs->quiesce_counter in a single drain call. Don't do it. |
7 | |||
8 | In order for this to work correctly, the parent callback must be called | ||
9 | for every bdrv_drain_begin/end() call, not only for the outermost one: | ||
10 | |||
11 | If we have a node N with two parents A and B, recursive draining of A | ||
12 | should cause the quiesce_counter of B to increase because its child N is | ||
13 | drained independently of B. If now B is recursively drained, too, A must | ||
14 | increase its quiesce_counter because N is drained independently of A | ||
15 | only now, even if N is going from quiesce_counter 1 to 2. | ||
16 | |||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | --- | 18 | --- |
9 | docs/qemu-block-drivers.texi | 10 ++++++++++ | 19 | include/block/block.h | 4 ++-- |
10 | 1 file changed, 10 insertions(+) | 20 | block.c | 13 +++++++++---- |
11 | 21 | block/io.c | 47 ++++++++++++++++++++++++++++++++++------------- | |
12 | diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi | 22 | 3 files changed, 45 insertions(+), 19 deletions(-) |
23 | |||
24 | diff --git a/include/block/block.h b/include/block/block.h | ||
13 | index XXXXXXX..XXXXXXX 100644 | 25 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/docs/qemu-block-drivers.texi | 26 | --- a/include/block/block.h |
15 | +++ b/docs/qemu-block-drivers.texi | 27 | +++ b/include/block/block.h |
16 | @@ -XXX,XX +XXX,XX @@ QEMU transparently handles lock handover during shared storage migration. For | 28 | @@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs); |
17 | shared virtual disk images between multiple VMs, the "share-rw" device option | 29 | * Begin a quiesced section of all users of @bs. This is part of |
18 | should be used. | 30 | * bdrv_drained_begin. |
19 | 31 | */ | |
20 | +By default, the guest has exclusive write access to its disk image. If the | 32 | -void bdrv_parent_drained_begin(BlockDriverState *bs); |
21 | +guest can safely share the disk image with other writers the @code{-device | 33 | +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); |
22 | +...,share-rw=on} parameter can be used. This is only safe if the guest is | 34 | |
23 | +running software, such as a cluster file system, that coordinates disk accesses | 35 | /** |
24 | +to avoid corruption. | 36 | * bdrv_parent_drained_end: |
37 | @@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs); | ||
38 | * End a quiesced section of all users of @bs. This is part of | ||
39 | * bdrv_drained_end. | ||
40 | */ | ||
41 | -void bdrv_parent_drained_end(BlockDriverState *bs); | ||
42 | +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); | ||
43 | |||
44 | /** | ||
45 | * bdrv_drained_begin: | ||
46 | diff --git a/block.c b/block.c | ||
47 | index XXXXXXX..XXXXXXX 100644 | ||
48 | --- a/block.c | ||
49 | +++ b/block.c | ||
50 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, | ||
51 | BlockDriverState *new_bs) | ||
52 | { | ||
53 | BlockDriverState *old_bs = child->bs; | ||
54 | + int i; | ||
55 | |||
56 | if (old_bs && new_bs) { | ||
57 | assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); | ||
58 | } | ||
59 | if (old_bs) { | ||
60 | if (old_bs->quiesce_counter && child->role->drained_end) { | ||
61 | - child->role->drained_end(child); | ||
62 | + for (i = 0; i < old_bs->quiesce_counter; i++) { | ||
63 | + child->role->drained_end(child); | ||
64 | + } | ||
65 | } | ||
66 | if (child->role->detach) { | ||
67 | child->role->detach(child); | ||
68 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, | ||
69 | if (new_bs) { | ||
70 | QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); | ||
71 | if (new_bs->quiesce_counter && child->role->drained_begin) { | ||
72 | - child->role->drained_begin(child); | ||
73 | + for (i = 0; i < new_bs->quiesce_counter; i++) { | ||
74 | + child->role->drained_begin(child); | ||
75 | + } | ||
76 | } | ||
77 | |||
78 | if (child->role->attach) { | ||
79 | @@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) | ||
80 | AioContext *ctx = bdrv_get_aio_context(bs); | ||
81 | |||
82 | aio_disable_external(ctx); | ||
83 | - bdrv_parent_drained_begin(bs); | ||
84 | + bdrv_parent_drained_begin(bs, NULL); | ||
85 | bdrv_drain(bs); /* ensure there are no in-flight requests */ | ||
86 | |||
87 | while (aio_poll(ctx, false)) { | ||
88 | @@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) | ||
89 | */ | ||
90 | aio_context_acquire(new_context); | ||
91 | bdrv_attach_aio_context(bs, new_context); | ||
92 | - bdrv_parent_drained_end(bs); | ||
93 | + bdrv_parent_drained_end(bs, NULL); | ||
94 | aio_enable_external(ctx); | ||
95 | aio_context_release(new_context); | ||
96 | } | ||
97 | diff --git a/block/io.c b/block/io.c | ||
98 | index XXXXXXX..XXXXXXX 100644 | ||
99 | --- a/block/io.c | ||
100 | +++ b/block/io.c | ||
101 | @@ -XXX,XX +XXX,XX @@ | ||
102 | static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, | ||
103 | int64_t offset, int bytes, BdrvRequestFlags flags); | ||
104 | |||
105 | -void bdrv_parent_drained_begin(BlockDriverState *bs) | ||
106 | +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) | ||
107 | { | ||
108 | BdrvChild *c, *next; | ||
109 | |||
110 | QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { | ||
111 | + if (c == ignore) { | ||
112 | + continue; | ||
113 | + } | ||
114 | if (c->role->drained_begin) { | ||
115 | c->role->drained_begin(c); | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | -void bdrv_parent_drained_end(BlockDriverState *bs) | ||
121 | +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) | ||
122 | { | ||
123 | BdrvChild *c, *next; | ||
124 | |||
125 | QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { | ||
126 | + if (c == ignore) { | ||
127 | + continue; | ||
128 | + } | ||
129 | if (c->role->drained_end) { | ||
130 | c->role->drained_end(c); | ||
131 | } | ||
132 | @@ -XXX,XX +XXX,XX @@ typedef struct { | ||
133 | BlockDriverState *bs; | ||
134 | bool done; | ||
135 | bool begin; | ||
136 | + BdrvChild *parent; | ||
137 | } BdrvCoDrainData; | ||
138 | |||
139 | static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) | ||
140 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs) | ||
141 | return waited; | ||
142 | } | ||
143 | |||
144 | +static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent); | ||
145 | +static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); | ||
25 | + | 146 | + |
26 | +Note that share-rw=on only declares the guest's ability to share the disk. | 147 | static void bdrv_co_drain_bh_cb(void *opaque) |
27 | +Some QEMU features, such as image file formats, require exclusive write access | 148 | { |
28 | +to the disk image and this is unaffected by the share-rw=on option. | 149 | BdrvCoDrainData *data = opaque; |
150 | @@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque) | ||
151 | |||
152 | bdrv_dec_in_flight(bs); | ||
153 | if (data->begin) { | ||
154 | - bdrv_drained_begin(bs); | ||
155 | + bdrv_do_drained_begin(bs, data->parent); | ||
156 | } else { | ||
157 | - bdrv_drained_end(bs); | ||
158 | + bdrv_do_drained_end(bs, data->parent); | ||
159 | } | ||
160 | |||
161 | data->done = true; | ||
162 | @@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque) | ||
163 | } | ||
164 | |||
165 | static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
166 | - bool begin) | ||
167 | + bool begin, BdrvChild *parent) | ||
168 | { | ||
169 | BdrvCoDrainData data; | ||
170 | |||
171 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
172 | .bs = bs, | ||
173 | .done = false, | ||
174 | .begin = begin, | ||
175 | + .parent = parent, | ||
176 | }; | ||
177 | bdrv_inc_in_flight(bs); | ||
178 | aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), | ||
179 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
180 | assert(data.done); | ||
181 | } | ||
182 | |||
183 | -void bdrv_drained_begin(BlockDriverState *bs) | ||
184 | +static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent) | ||
185 | { | ||
186 | if (qemu_in_coroutine()) { | ||
187 | - bdrv_co_yield_to_drain(bs, true); | ||
188 | + bdrv_co_yield_to_drain(bs, true, parent); | ||
189 | return; | ||
190 | } | ||
191 | |||
192 | /* Stop things in parent-to-child order */ | ||
193 | if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { | ||
194 | aio_disable_external(bdrv_get_aio_context(bs)); | ||
195 | - bdrv_parent_drained_begin(bs); | ||
196 | } | ||
197 | |||
198 | + bdrv_parent_drained_begin(bs, parent); | ||
199 | bdrv_drain_invoke(bs, true, false); | ||
200 | bdrv_drain_recurse(bs); | ||
201 | } | ||
202 | |||
203 | -void bdrv_drained_end(BlockDriverState *bs) | ||
204 | +void bdrv_drained_begin(BlockDriverState *bs) | ||
205 | +{ | ||
206 | + bdrv_do_drained_begin(bs, NULL); | ||
207 | +} | ||
29 | + | 208 | + |
30 | Alternatively, locking can be fully disabled by "locking=off" block device | 209 | +static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) |
31 | option. In the command line, the option is usually in the form of | 210 | { |
32 | "file.locking=off" as the protocol driver is normally placed as a "file" child | 211 | int old_quiesce_counter; |
212 | |||
213 | if (qemu_in_coroutine()) { | ||
214 | - bdrv_co_yield_to_drain(bs, false); | ||
215 | + bdrv_co_yield_to_drain(bs, false, parent); | ||
216 | return; | ||
217 | } | ||
218 | assert(bs->quiesce_counter > 0); | ||
219 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | ||
220 | |||
221 | /* Re-enable things in child-to-parent order */ | ||
222 | bdrv_drain_invoke(bs, false, false); | ||
223 | + bdrv_parent_drained_end(bs, parent); | ||
224 | if (old_quiesce_counter == 1) { | ||
225 | - bdrv_parent_drained_end(bs); | ||
226 | aio_enable_external(bdrv_get_aio_context(bs)); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | +void bdrv_drained_end(BlockDriverState *bs) | ||
231 | +{ | ||
232 | + bdrv_do_drained_end(bs, NULL); | ||
233 | +} | ||
234 | + | ||
235 | /* | ||
236 | * Wait for pending requests to complete on a single BlockDriverState subtree, | ||
237 | * and suspend block driver's internal I/O until next request arrives. | ||
238 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
239 | /* Stop things in parent-to-child order */ | ||
240 | aio_context_acquire(aio_context); | ||
241 | aio_disable_external(aio_context); | ||
242 | - bdrv_parent_drained_begin(bs); | ||
243 | + bdrv_parent_drained_begin(bs, NULL); | ||
244 | bdrv_drain_invoke(bs, true, true); | ||
245 | aio_context_release(aio_context); | ||
246 | |||
247 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
248 | /* Re-enable things in child-to-parent order */ | ||
249 | aio_context_acquire(aio_context); | ||
250 | bdrv_drain_invoke(bs, false, true); | ||
251 | - bdrv_parent_drained_end(bs); | ||
252 | + bdrv_parent_drained_end(bs, NULL); | ||
253 | aio_enable_external(aio_context); | ||
254 | aio_context_release(aio_context); | ||
255 | } | ||
33 | -- | 256 | -- |
34 | 2.13.6 | 257 | 2.13.6 |
35 | 258 | ||
36 | 259 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | bdrv_drained_begin() waits for the completion of requests in the whole |
---|---|---|---|
2 | subtree, but it only actually keeps its immediate bs parameter quiesced | ||
3 | until bdrv_drained_end(). | ||
2 | 4 | ||
3 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 5 | Add a version that keeps the whole subtree drained. As of this commit, |
4 | Reviewed-by: Eric Blake <eblake@redhat.com> | 6 | graph changes cannot be allowed during a subtree drained section, but |
7 | this will be fixed soon. | ||
8 | |||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | --- | 10 | --- |
7 | block/sheepdog.c | 15 ++++++++++++--- | 11 | include/block/block.h | 13 +++++++++++++ |
8 | 1 file changed, 12 insertions(+), 3 deletions(-) | 12 | block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++----------- |
13 | 2 files changed, 56 insertions(+), 11 deletions(-) | ||
9 | 14 | ||
10 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 15 | diff --git a/include/block/block.h b/include/block/block.h |
11 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/block/sheepdog.c | 17 | --- a/include/block/block.h |
13 | +++ b/block/sheepdog.c | 18 | +++ b/include/block/block.h |
14 | @@ -XXX,XX +XXX,XX @@ static int sd_truncate(BlockDriverState *bs, int64_t offset, | 19 | @@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); |
15 | int ret, fd; | 20 | void bdrv_drained_begin(BlockDriverState *bs); |
16 | unsigned int datalen; | 21 | |
17 | uint64_t max_vdi_size; | 22 | /** |
18 | + int64_t old_size = s->inode.vdi_size; | 23 | + * Like bdrv_drained_begin, but recursively begins a quiesced section for |
19 | 24 | + * exclusive access to all child nodes as well. | |
20 | - if (prealloc != PREALLOC_MODE_OFF) { | 25 | + * |
21 | + if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_FULL) { | 26 | + * Graph changes are not allowed during a subtree drain section. |
22 | error_setg(errp, "Unsupported preallocation mode '%s'", | 27 | + */ |
23 | PreallocMode_str(prealloc)); | 28 | +void bdrv_subtree_drained_begin(BlockDriverState *bs); |
24 | return -ENOTSUP; | 29 | + |
30 | +/** | ||
31 | * bdrv_drained_end: | ||
32 | * | ||
33 | * End a quiescent section started by bdrv_drained_begin(). | ||
34 | */ | ||
35 | void bdrv_drained_end(BlockDriverState *bs); | ||
36 | |||
37 | +/** | ||
38 | + * End a quiescent section started by bdrv_subtree_drained_begin(). | ||
39 | + */ | ||
40 | +void bdrv_subtree_drained_end(BlockDriverState *bs); | ||
41 | + | ||
42 | void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, | ||
43 | Error **errp); | ||
44 | void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); | ||
45 | diff --git a/block/io.c b/block/io.c | ||
46 | index XXXXXXX..XXXXXXX 100644 | ||
47 | --- a/block/io.c | ||
48 | +++ b/block/io.c | ||
49 | @@ -XXX,XX +XXX,XX @@ typedef struct { | ||
50 | BlockDriverState *bs; | ||
51 | bool done; | ||
52 | bool begin; | ||
53 | + bool recursive; | ||
54 | BdrvChild *parent; | ||
55 | } BdrvCoDrainData; | ||
56 | |||
57 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs) | ||
58 | return waited; | ||
59 | } | ||
60 | |||
61 | -static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent); | ||
62 | -static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); | ||
63 | +static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, | ||
64 | + BdrvChild *parent); | ||
65 | +static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, | ||
66 | + BdrvChild *parent); | ||
67 | |||
68 | static void bdrv_co_drain_bh_cb(void *opaque) | ||
69 | { | ||
70 | @@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque) | ||
71 | |||
72 | bdrv_dec_in_flight(bs); | ||
73 | if (data->begin) { | ||
74 | - bdrv_do_drained_begin(bs, data->parent); | ||
75 | + bdrv_do_drained_begin(bs, data->recursive, data->parent); | ||
76 | } else { | ||
77 | - bdrv_do_drained_end(bs, data->parent); | ||
78 | + bdrv_do_drained_end(bs, data->recursive, data->parent); | ||
25 | } | 79 | } |
26 | 80 | ||
27 | max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; | 81 | data->done = true; |
28 | - if (offset < s->inode.vdi_size) { | 82 | @@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque) |
29 | + if (offset < old_size) { | 83 | } |
30 | error_setg(errp, "shrinking is not supported"); | 84 | |
31 | return -EINVAL; | 85 | static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, |
32 | } else if (offset > max_vdi_size) { | 86 | - bool begin, BdrvChild *parent) |
33 | @@ -XXX,XX +XXX,XX @@ static int sd_truncate(BlockDriverState *bs, int64_t offset, | 87 | + bool begin, bool recursive, |
34 | 88 | + BdrvChild *parent) | |
35 | if (ret < 0) { | 89 | { |
36 | error_setg_errno(errp, -ret, "failed to update an inode"); | 90 | BdrvCoDrainData data; |
37 | + return ret; | 91 | |
92 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
93 | .bs = bs, | ||
94 | .done = false, | ||
95 | .begin = begin, | ||
96 | + .recursive = recursive, | ||
97 | .parent = parent, | ||
98 | }; | ||
99 | bdrv_inc_in_flight(bs); | ||
100 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
101 | assert(data.done); | ||
102 | } | ||
103 | |||
104 | -static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent) | ||
105 | +static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, | ||
106 | + BdrvChild *parent) | ||
107 | { | ||
108 | + BdrvChild *child, *next; | ||
109 | + | ||
110 | if (qemu_in_coroutine()) { | ||
111 | - bdrv_co_yield_to_drain(bs, true, parent); | ||
112 | + bdrv_co_yield_to_drain(bs, true, recursive, parent); | ||
113 | return; | ||
38 | } | 114 | } |
39 | 115 | ||
40 | - return ret; | 116 | @@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent) |
41 | + if (prealloc == PREALLOC_MODE_FULL) { | 117 | bdrv_parent_drained_begin(bs, parent); |
42 | + ret = sd_prealloc(bs, old_size, offset, errp); | 118 | bdrv_drain_invoke(bs, true, false); |
43 | + if (ret < 0) { | 119 | bdrv_drain_recurse(bs); |
44 | + return ret; | 120 | + |
121 | + if (recursive) { | ||
122 | + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { | ||
123 | + bdrv_do_drained_begin(child->bs, true, child); | ||
45 | + } | 124 | + } |
46 | + } | 125 | + } |
126 | } | ||
127 | |||
128 | void bdrv_drained_begin(BlockDriverState *bs) | ||
129 | { | ||
130 | - bdrv_do_drained_begin(bs, NULL); | ||
131 | + bdrv_do_drained_begin(bs, false, NULL); | ||
132 | +} | ||
47 | + | 133 | + |
48 | + return 0; | 134 | +void bdrv_subtree_drained_begin(BlockDriverState *bs) |
135 | +{ | ||
136 | + bdrv_do_drained_begin(bs, true, NULL); | ||
49 | } | 137 | } |
138 | |||
139 | -static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) | ||
140 | +static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, | ||
141 | + BdrvChild *parent) | ||
142 | { | ||
143 | + BdrvChild *child, *next; | ||
144 | int old_quiesce_counter; | ||
145 | |||
146 | if (qemu_in_coroutine()) { | ||
147 | - bdrv_co_yield_to_drain(bs, false, parent); | ||
148 | + bdrv_co_yield_to_drain(bs, false, recursive, parent); | ||
149 | return; | ||
150 | } | ||
151 | assert(bs->quiesce_counter > 0); | ||
152 | @@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) | ||
153 | if (old_quiesce_counter == 1) { | ||
154 | aio_enable_external(bdrv_get_aio_context(bs)); | ||
155 | } | ||
156 | + | ||
157 | + if (recursive) { | ||
158 | + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { | ||
159 | + bdrv_do_drained_end(child->bs, true, child); | ||
160 | + } | ||
161 | + } | ||
162 | } | ||
163 | |||
164 | void bdrv_drained_end(BlockDriverState *bs) | ||
165 | { | ||
166 | - bdrv_do_drained_end(bs, NULL); | ||
167 | + bdrv_do_drained_end(bs, false, NULL); | ||
168 | +} | ||
169 | + | ||
170 | +void bdrv_subtree_drained_end(BlockDriverState *bs) | ||
171 | +{ | ||
172 | + bdrv_do_drained_end(bs, true, NULL); | ||
173 | } | ||
50 | 174 | ||
51 | /* | 175 | /* |
52 | -- | 176 | -- |
53 | 2.13.6 | 177 | 2.13.6 |
54 | 178 | ||
55 | 179 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | Add a subtree drain version to the existing test cases. |
---|---|---|---|
2 | 2 | ||
3 | By using qemu_do_cluster_truncate() in qemu_cluster_truncate(), we now | ||
4 | automatically have preallocated truncation. | ||
5 | |||
6 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 3 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 4 | --- |
10 | block/gluster.c | 17 +---------------- | 5 | tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++- |
11 | 1 file changed, 1 insertion(+), 16 deletions(-) | 6 | 1 file changed, 26 insertions(+), 1 deletion(-) |
12 | 7 | ||
13 | diff --git a/block/gluster.c b/block/gluster.c | 8 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
14 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/gluster.c | 10 | --- a/tests/test-bdrv-drain.c |
16 | +++ b/block/gluster.c | 11 | +++ b/tests/test-bdrv-drain.c |
17 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, | 12 | @@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret) |
18 | static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, | 13 | enum drain_type { |
19 | PreallocMode prealloc, Error **errp) | 14 | BDRV_DRAIN_ALL, |
15 | BDRV_DRAIN, | ||
16 | + BDRV_SUBTREE_DRAIN, | ||
17 | DRAIN_TYPE_MAX, | ||
18 | }; | ||
19 | |||
20 | @@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) | ||
21 | switch (drain_type) { | ||
22 | case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; | ||
23 | case BDRV_DRAIN: bdrv_drained_begin(bs); break; | ||
24 | + case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break; | ||
25 | default: g_assert_not_reached(); | ||
26 | } | ||
27 | } | ||
28 | @@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) | ||
29 | switch (drain_type) { | ||
30 | case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; | ||
31 | case BDRV_DRAIN: bdrv_drained_end(bs); break; | ||
32 | + case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break; | ||
33 | default: g_assert_not_reached(); | ||
34 | } | ||
35 | } | ||
36 | @@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void) | ||
37 | test_drv_cb_common(BDRV_DRAIN, false); | ||
38 | } | ||
39 | |||
40 | +static void test_drv_cb_drain_subtree(void) | ||
41 | +{ | ||
42 | + test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); | ||
43 | +} | ||
44 | + | ||
45 | static void test_quiesce_common(enum drain_type drain_type, bool recursive) | ||
20 | { | 46 | { |
21 | - int ret; | 47 | BlockBackend *blk; |
22 | BDRVGlusterState *s = bs->opaque; | 48 | @@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void) |
23 | - | 49 | test_quiesce_common(BDRV_DRAIN, false); |
24 | - if (prealloc != PREALLOC_MODE_OFF) { | ||
25 | - error_setg(errp, "Unsupported preallocation mode '%s'", | ||
26 | - PreallocMode_str(prealloc)); | ||
27 | - return -ENOTSUP; | ||
28 | - } | ||
29 | - | ||
30 | - ret = glfs_ftruncate(s->fd, offset); | ||
31 | - if (ret < 0) { | ||
32 | - ret = -errno; | ||
33 | - error_setg_errno(errp, -ret, "Failed to truncate file"); | ||
34 | - return ret; | ||
35 | - } | ||
36 | - | ||
37 | - return 0; | ||
38 | + return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); | ||
39 | } | 50 | } |
40 | 51 | ||
41 | static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, | 52 | +static void test_quiesce_drain_subtree(void) |
53 | +{ | ||
54 | + test_quiesce_common(BDRV_SUBTREE_DRAIN, true); | ||
55 | +} | ||
56 | + | ||
57 | static void test_nested(void) | ||
58 | { | ||
59 | BlockBackend *blk; | ||
60 | @@ -XXX,XX +XXX,XX @@ static void test_nested(void) | ||
61 | /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ | ||
62 | int bs_quiesce = (outer != BDRV_DRAIN_ALL) + | ||
63 | (inner != BDRV_DRAIN_ALL); | ||
64 | - int backing_quiesce = 0; | ||
65 | + int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) + | ||
66 | + (inner == BDRV_SUBTREE_DRAIN); | ||
67 | int backing_cb_cnt = (outer != BDRV_DRAIN) + | ||
68 | (inner != BDRV_DRAIN); | ||
69 | |||
70 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void) | ||
71 | test_blockjob_common(BDRV_DRAIN); | ||
72 | } | ||
73 | |||
74 | +static void test_blockjob_drain_subtree(void) | ||
75 | +{ | ||
76 | + test_blockjob_common(BDRV_SUBTREE_DRAIN); | ||
77 | +} | ||
78 | + | ||
79 | int main(int argc, char **argv) | ||
80 | { | ||
81 | bdrv_init(); | ||
82 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
83 | |||
84 | g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); | ||
85 | g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); | ||
86 | + g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", | ||
87 | + test_drv_cb_drain_subtree); | ||
88 | |||
89 | g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); | ||
90 | g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); | ||
91 | + g_test_add_func("/bdrv-drain/quiesce/drain_subtree", | ||
92 | + test_quiesce_drain_subtree); | ||
93 | |||
94 | g_test_add_func("/bdrv-drain/nested", test_nested); | ||
95 | |||
96 | g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); | ||
97 | g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); | ||
98 | + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", | ||
99 | + test_blockjob_drain_subtree); | ||
100 | |||
101 | return g_test_run(); | ||
102 | } | ||
42 | -- | 103 | -- |
43 | 2.13.6 | 104 | 2.13.6 |
44 | 105 | ||
45 | 106 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | If bdrv_do_drained_begin/end() are called in coroutine context, they |
---|---|---|---|
2 | first use a BH to get out of the coroutine context. Call some existing | ||
3 | tests again from a coroutine to cover this code path. | ||
2 | 4 | ||
3 | The l2-cache-entry-size setting can only contain values that are | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | powers of two between 512 and the cluster size. | 6 | --- |
7 | tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ | ||
8 | 1 file changed, 59 insertions(+) | ||
5 | 9 | ||
6 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 10 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | 11 | index XXXXXXX..XXXXXXX 100644 |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 12 | --- a/tests/test-bdrv-drain.c |
9 | Message-id: bd3547b670b8d0af11480c760991a22bcae5b48c.1517840877.git.berto@igalia.com | 13 | +++ b/tests/test-bdrv-drain.c |
10 | [mreitz: Changed non-power-of-two test value from 300 to 4242] | 14 | @@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret) |
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 15 | *aio_ret = ret; |
12 | --- | 16 | } |
13 | tests/qemu-iotests/103 | 17 +++++++++++++++++ | 17 | |
14 | tests/qemu-iotests/103.out | 3 +++ | 18 | +typedef struct CallInCoroutineData { |
15 | 2 files changed, 20 insertions(+) | 19 | + void (*entry)(void); |
16 | 20 | + bool done; | |
17 | diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 | 21 | +} CallInCoroutineData; |
18 | index XXXXXXX..XXXXXXX 100755 | ||
19 | --- a/tests/qemu-iotests/103 | ||
20 | +++ b/tests/qemu-iotests/103 | ||
21 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "open -o cache-size=1M,refcount-cache-size=2M $TEST_IMG" 2>&1 \ | ||
22 | $QEMU_IO -c "open -o cache-size=0,l2-cache-size=0,refcount-cache-size=0 $TEST_IMG" \ | ||
23 | 2>&1 | _filter_testdir | _filter_imgfmt | ||
24 | |||
25 | +# Invalid cache entry sizes | ||
26 | +$QEMU_IO -c "open -o l2-cache-entry-size=256 $TEST_IMG" \ | ||
27 | + 2>&1 | _filter_testdir | _filter_imgfmt | ||
28 | +$QEMU_IO -c "open -o l2-cache-entry-size=4242 $TEST_IMG" \ | ||
29 | + 2>&1 | _filter_testdir | _filter_imgfmt | ||
30 | +$QEMU_IO -c "open -o l2-cache-entry-size=128k $TEST_IMG" \ | ||
31 | + 2>&1 | _filter_testdir | _filter_imgfmt | ||
32 | + | 22 | + |
33 | echo | 23 | +static coroutine_fn void call_in_coroutine_entry(void *opaque) |
34 | echo '=== Testing valid option combinations ===' | 24 | +{ |
35 | echo | 25 | + CallInCoroutineData *data = opaque; |
36 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \ | 26 | + |
37 | -c 'read -P 42 0 64k' \ | 27 | + data->entry(); |
38 | | _filter_qemu_io | 28 | + data->done = true; |
39 | 29 | +} | |
40 | +# Valid cache entry sizes | 30 | + |
41 | +$QEMU_IO -c "open -o l2-cache-entry-size=512 $TEST_IMG" \ | 31 | +static void call_in_coroutine(void (*entry)(void)) |
42 | + 2>&1 | _filter_testdir | _filter_imgfmt | 32 | +{ |
43 | +$QEMU_IO -c "open -o l2-cache-entry-size=16k $TEST_IMG" \ | 33 | + Coroutine *co; |
44 | + 2>&1 | _filter_testdir | _filter_imgfmt | 34 | + CallInCoroutineData data = { |
45 | +$QEMU_IO -c "open -o l2-cache-entry-size=64k $TEST_IMG" \ | 35 | + .entry = entry, |
46 | + 2>&1 | _filter_testdir | _filter_imgfmt | 36 | + .done = false, |
37 | + }; | ||
38 | + | ||
39 | + co = qemu_coroutine_create(call_in_coroutine_entry, &data); | ||
40 | + qemu_coroutine_enter(co); | ||
41 | + while (!data.done) { | ||
42 | + aio_poll(qemu_get_aio_context(), true); | ||
43 | + } | ||
44 | +} | ||
45 | + | ||
46 | enum drain_type { | ||
47 | BDRV_DRAIN_ALL, | ||
48 | BDRV_DRAIN, | ||
49 | @@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void) | ||
50 | test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); | ||
51 | } | ||
52 | |||
53 | +static void test_drv_cb_co_drain(void) | ||
54 | +{ | ||
55 | + call_in_coroutine(test_drv_cb_drain); | ||
56 | +} | ||
57 | + | ||
58 | +static void test_drv_cb_co_drain_subtree(void) | ||
59 | +{ | ||
60 | + call_in_coroutine(test_drv_cb_drain_subtree); | ||
61 | +} | ||
62 | + | ||
63 | static void test_quiesce_common(enum drain_type drain_type, bool recursive) | ||
64 | { | ||
65 | BlockBackend *blk; | ||
66 | @@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void) | ||
67 | test_quiesce_common(BDRV_SUBTREE_DRAIN, true); | ||
68 | } | ||
69 | |||
70 | +static void test_quiesce_co_drain(void) | ||
71 | +{ | ||
72 | + call_in_coroutine(test_quiesce_drain); | ||
73 | +} | ||
74 | + | ||
75 | +static void test_quiesce_co_drain_subtree(void) | ||
76 | +{ | ||
77 | + call_in_coroutine(test_quiesce_drain_subtree); | ||
78 | +} | ||
79 | + | ||
80 | static void test_nested(void) | ||
81 | { | ||
82 | BlockBackend *blk; | ||
83 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
84 | g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", | ||
85 | test_drv_cb_drain_subtree); | ||
86 | |||
87 | + // XXX bdrv_drain_all() doesn't work in coroutine context | ||
88 | + g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); | ||
89 | + g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", | ||
90 | + test_drv_cb_co_drain_subtree); | ||
47 | + | 91 | + |
48 | + | 92 | + |
49 | echo | 93 | g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); |
50 | echo '=== Testing minimal L2 cache and COW ===' | 94 | g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); |
51 | echo | 95 | g_test_add_func("/bdrv-drain/quiesce/drain_subtree", |
52 | diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out | 96 | test_quiesce_drain_subtree); |
53 | index XXXXXXX..XXXXXXX 100644 | 97 | |
54 | --- a/tests/qemu-iotests/103.out | 98 | + // XXX bdrv_drain_all() doesn't work in coroutine context |
55 | +++ b/tests/qemu-iotests/103.out | 99 | + g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); |
56 | @@ -XXX,XX +XXX,XX @@ can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cach | 100 | + g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", |
57 | can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size | 101 | + test_quiesce_co_drain_subtree); |
58 | can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size | 102 | + |
59 | can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time | 103 | g_test_add_func("/bdrv-drain/nested", test_nested); |
60 | +can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) | 104 | |
61 | +can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) | 105 | g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); |
62 | +can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) | ||
63 | |||
64 | === Testing valid option combinations === | ||
65 | |||
66 | -- | 106 | -- |
67 | 2.13.6 | 107 | 2.13.6 |
68 | 108 | ||
69 | 109 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Test that drain sections are correctly propagated through the graph. |
---|---|---|---|
2 | 2 | ||
3 | Similar to offset_to_l2_index(), this function returns the index in | 3 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | the L1 table for a given guest offset. This is only used in a couple | 4 | --- |
5 | of places and it's not a particularly complex calculation, but it | 5 | tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ |
6 | makes the code a bit more readable. | 6 | 1 file changed, 74 insertions(+) |
7 | 7 | ||
8 | Although in the qcow2_get_cluster_offset() case the old code was | 8 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
9 | taking advantage of the l1_bits variable, we're going to get rid of | ||
10 | the other uses of l1_bits in a later patch anyway, so it doesn't make | ||
11 | sense to keep it just for this. | ||
12 | |||
13 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
14 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
15 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
16 | Message-id: a5f626fed526b7459a0425fad06d823d18df8522.1517840877.git.berto@igalia.com | ||
17 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
18 | --- | ||
19 | block/qcow2.h | 5 +++++ | ||
20 | block/qcow2-cluster.c | 4 ++-- | ||
21 | 2 files changed, 7 insertions(+), 2 deletions(-) | ||
22 | |||
23 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
24 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/qcow2.h | 10 | --- a/tests/test-bdrv-drain.c |
26 | +++ b/block/qcow2.h | 11 | +++ b/tests/test-bdrv-drain.c |
27 | @@ -XXX,XX +XXX,XX @@ static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) | 12 | @@ -XXX,XX +XXX,XX @@ static void test_nested(void) |
28 | return (size + (1ULL << shift) - 1) >> shift; | 13 | blk_unref(blk); |
29 | } | 14 | } |
30 | 15 | ||
31 | +static inline int offset_to_l1_index(BDRVQcow2State *s, uint64_t offset) | 16 | +static void test_multiparent(void) |
32 | +{ | 17 | +{ |
33 | + return offset >> (s->l2_bits + s->cluster_bits); | 18 | + BlockBackend *blk_a, *blk_b; |
19 | + BlockDriverState *bs_a, *bs_b, *backing; | ||
20 | + BDRVTestState *a_s, *b_s, *backing_s; | ||
21 | + | ||
22 | + blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||
23 | + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, | ||
24 | + &error_abort); | ||
25 | + a_s = bs_a->opaque; | ||
26 | + blk_insert_bs(blk_a, bs_a, &error_abort); | ||
27 | + | ||
28 | + blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||
29 | + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, | ||
30 | + &error_abort); | ||
31 | + b_s = bs_b->opaque; | ||
32 | + blk_insert_bs(blk_b, bs_b, &error_abort); | ||
33 | + | ||
34 | + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); | ||
35 | + backing_s = backing->opaque; | ||
36 | + bdrv_set_backing_hd(bs_a, backing, &error_abort); | ||
37 | + bdrv_set_backing_hd(bs_b, backing, &error_abort); | ||
38 | + | ||
39 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); | ||
40 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); | ||
41 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
42 | + g_assert_cmpint(a_s->drain_count, ==, 0); | ||
43 | + g_assert_cmpint(b_s->drain_count, ==, 0); | ||
44 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
45 | + | ||
46 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); | ||
47 | + | ||
48 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); | ||
49 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); | ||
50 | + g_assert_cmpint(backing->quiesce_counter, ==, 1); | ||
51 | + g_assert_cmpint(a_s->drain_count, ==, 1); | ||
52 | + g_assert_cmpint(b_s->drain_count, ==, 1); | ||
53 | + g_assert_cmpint(backing_s->drain_count, ==, 1); | ||
54 | + | ||
55 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); | ||
56 | + | ||
57 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 2); | ||
58 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 2); | ||
59 | + g_assert_cmpint(backing->quiesce_counter, ==, 2); | ||
60 | + g_assert_cmpint(a_s->drain_count, ==, 2); | ||
61 | + g_assert_cmpint(b_s->drain_count, ==, 2); | ||
62 | + g_assert_cmpint(backing_s->drain_count, ==, 2); | ||
63 | + | ||
64 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); | ||
65 | + | ||
66 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); | ||
67 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); | ||
68 | + g_assert_cmpint(backing->quiesce_counter, ==, 1); | ||
69 | + g_assert_cmpint(a_s->drain_count, ==, 1); | ||
70 | + g_assert_cmpint(b_s->drain_count, ==, 1); | ||
71 | + g_assert_cmpint(backing_s->drain_count, ==, 1); | ||
72 | + | ||
73 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); | ||
74 | + | ||
75 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); | ||
76 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); | ||
77 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
78 | + g_assert_cmpint(a_s->drain_count, ==, 0); | ||
79 | + g_assert_cmpint(b_s->drain_count, ==, 0); | ||
80 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
81 | + | ||
82 | + bdrv_unref(backing); | ||
83 | + bdrv_unref(bs_a); | ||
84 | + bdrv_unref(bs_b); | ||
85 | + blk_unref(blk_a); | ||
86 | + blk_unref(blk_b); | ||
34 | +} | 87 | +} |
35 | + | 88 | + |
36 | static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) | 89 | |
37 | { | 90 | typedef struct TestBlockJob { |
38 | return (offset >> s->cluster_bits) & (s->l2_size - 1); | 91 | BlockJob common; |
39 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | 92 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) |
40 | index XXXXXXX..XXXXXXX 100644 | 93 | test_quiesce_co_drain_subtree); |
41 | --- a/block/qcow2-cluster.c | 94 | |
42 | +++ b/block/qcow2-cluster.c | 95 | g_test_add_func("/bdrv-drain/nested", test_nested); |
43 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | 96 | + g_test_add_func("/bdrv-drain/multiparent", test_multiparent); |
44 | 97 | ||
45 | /* seek to the l2 offset in the l1 table */ | 98 | g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); |
46 | 99 | g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); | |
47 | - l1_index = offset >> l1_bits; | ||
48 | + l1_index = offset_to_l1_index(s, offset); | ||
49 | if (l1_index >= s->l1_size) { | ||
50 | type = QCOW2_CLUSTER_UNALLOCATED; | ||
51 | goto out; | ||
52 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
53 | |||
54 | /* seek to the l2 offset in the l1 table */ | ||
55 | |||
56 | - l1_index = offset >> (s->l2_bits + s->cluster_bits); | ||
57 | + l1_index = offset_to_l1_index(s, offset); | ||
58 | if (l1_index >= s->l1_size) { | ||
59 | ret = qcow2_grow_l1_table(bs, l1_index + 1, false); | ||
60 | if (ret < 0) { | ||
61 | -- | 100 | -- |
62 | 2.13.6 | 101 | 2.13.6 |
63 | 102 | ||
64 | 103 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | We need to remember how many of the drain sections in which a node is |
---|---|---|---|
2 | 2 | were recursive (i.e. subtree drain rather than node drain), so that they | |
3 | discard_single_l2() limits the number of clusters to be discarded | 3 | can be correctly applied when children are added or removed during the |
4 | to the amount that fits inside an L2 table. Since we'll be loading | 4 | drained section. |
5 | L2 slices instead of full tables we need to update that limit. The | 5 | |
6 | function is renamed to discard_in_l2_slice() for clarity. | 6 | With this change, it is safe to modify the graph even inside a |
7 | 7 | bdrv_subtree_drained_begin/end() section. | |
8 | Apart from that, this function doesn't need any additional changes, so | 8 | |
9 | this patch simply updates the variable name from l2_table to l2_slice. | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | |||
11 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
12 | Message-id: 1cb44a5b68be5334cb01b97a3db3a3c5a43396e5.1517840877.git.berto@igalia.com | ||
13 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | 10 | --- |
16 | block/qcow2-cluster.c | 32 ++++++++++++++++---------------- | 11 | include/block/block.h | 2 -- |
17 | 1 file changed, 16 insertions(+), 16 deletions(-) | 12 | include/block/block_int.h | 5 +++++ |
18 | 13 | block.c | 32 +++++++++++++++++++++++++++++--- | |
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | 14 | block/io.c | 28 ++++++++++++++++++++++++---- |
20 | index XXXXXXX..XXXXXXX 100644 | 15 | 4 files changed, 58 insertions(+), 9 deletions(-) |
21 | --- a/block/qcow2-cluster.c | 16 | |
22 | +++ b/block/qcow2-cluster.c | 17 | diff --git a/include/block/block.h b/include/block/block.h |
23 | @@ -XXX,XX +XXX,XX @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | 18 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/include/block/block.h | ||
20 | +++ b/include/block/block.h | ||
21 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs); | ||
22 | /** | ||
23 | * Like bdrv_drained_begin, but recursively begins a quiesced section for | ||
24 | * exclusive access to all child nodes as well. | ||
25 | - * | ||
26 | - * Graph changes are not allowed during a subtree drain section. | ||
27 | */ | ||
28 | void bdrv_subtree_drained_begin(BlockDriverState *bs); | ||
29 | |||
30 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
31 | index XXXXXXX..XXXXXXX 100644 | ||
32 | --- a/include/block/block_int.h | ||
33 | +++ b/include/block/block_int.h | ||
34 | @@ -XXX,XX +XXX,XX @@ struct BlockDriverState { | ||
35 | |||
36 | /* Accessed with atomic ops. */ | ||
37 | int quiesce_counter; | ||
38 | + int recursive_quiesce_counter; | ||
39 | + | ||
40 | unsigned int write_gen; /* Current data generation */ | ||
41 | |||
42 | /* Protected by reqs_lock. */ | ||
43 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, | ||
44 | int64_t offset, unsigned int bytes, QEMUIOVector *qiov, | ||
45 | BdrvRequestFlags flags); | ||
46 | |||
47 | +void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); | ||
48 | +void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); | ||
49 | + | ||
50 | int get_tmp_filename(char *filename, int size); | ||
51 | BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, | ||
52 | const char *filename); | ||
53 | diff --git a/block.c b/block.c | ||
54 | index XXXXXXX..XXXXXXX 100644 | ||
55 | --- a/block.c | ||
56 | +++ b/block.c | ||
57 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child) | ||
58 | bdrv_drained_end(bs); | ||
59 | } | ||
60 | |||
61 | +static void bdrv_child_cb_attach(BdrvChild *child) | ||
62 | +{ | ||
63 | + BlockDriverState *bs = child->opaque; | ||
64 | + bdrv_apply_subtree_drain(child, bs); | ||
65 | +} | ||
66 | + | ||
67 | +static void bdrv_child_cb_detach(BdrvChild *child) | ||
68 | +{ | ||
69 | + BlockDriverState *bs = child->opaque; | ||
70 | + bdrv_unapply_subtree_drain(child, bs); | ||
71 | +} | ||
72 | + | ||
73 | static int bdrv_child_cb_inactivate(BdrvChild *child) | ||
74 | { | ||
75 | BlockDriverState *bs = child->opaque; | ||
76 | @@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = { | ||
77 | .inherit_options = bdrv_inherited_options, | ||
78 | .drained_begin = bdrv_child_cb_drained_begin, | ||
79 | .drained_end = bdrv_child_cb_drained_end, | ||
80 | + .attach = bdrv_child_cb_attach, | ||
81 | + .detach = bdrv_child_cb_detach, | ||
82 | .inactivate = bdrv_child_cb_inactivate, | ||
83 | }; | ||
84 | |||
85 | @@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = { | ||
86 | .inherit_options = bdrv_inherited_fmt_options, | ||
87 | .drained_begin = bdrv_child_cb_drained_begin, | ||
88 | .drained_end = bdrv_child_cb_drained_end, | ||
89 | + .attach = bdrv_child_cb_attach, | ||
90 | + .detach = bdrv_child_cb_detach, | ||
91 | .inactivate = bdrv_child_cb_inactivate, | ||
92 | }; | ||
93 | |||
94 | @@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c) | ||
95 | parent->backing_blocker); | ||
96 | bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET, | ||
97 | parent->backing_blocker); | ||
98 | + | ||
99 | + bdrv_child_cb_attach(c); | ||
100 | } | ||
101 | |||
102 | static void bdrv_backing_detach(BdrvChild *c) | ||
103 | @@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c) | ||
104 | bdrv_op_unblock_all(c->bs, parent->backing_blocker); | ||
105 | error_free(parent->backing_blocker); | ||
106 | parent->backing_blocker = NULL; | ||
107 | + | ||
108 | + bdrv_child_cb_detach(c); | ||
109 | } | ||
24 | 110 | ||
25 | /* | 111 | /* |
26 | * This discards as many clusters of nb_clusters as possible at once (i.e. | 112 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, |
27 | - * all clusters in the same L2 table) and returns the number of discarded | 113 | assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); |
28 | + * all clusters in the same L2 slice) and returns the number of discarded | 114 | } |
29 | * clusters. | 115 | if (old_bs) { |
30 | */ | 116 | + /* Detach first so that the recursive drain sections coming from @child |
31 | -static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | 117 | + * are already gone and we only end the drain sections that came from |
32 | - uint64_t nb_clusters, enum qcow2_discard_type type, | 118 | + * elsewhere. */ |
33 | - bool full_discard) | 119 | + if (child->role->detach) { |
34 | +static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, | 120 | + child->role->detach(child); |
35 | + uint64_t nb_clusters, | 121 | + } |
36 | + enum qcow2_discard_type type, bool full_discard) | 122 | if (old_bs->quiesce_counter && child->role->drained_end) { |
123 | for (i = 0; i < old_bs->quiesce_counter; i++) { | ||
124 | child->role->drained_end(child); | ||
125 | } | ||
126 | } | ||
127 | - if (child->role->detach) { | ||
128 | - child->role->detach(child); | ||
129 | - } | ||
130 | QLIST_REMOVE(child, next_parent); | ||
131 | } | ||
132 | |||
133 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, | ||
134 | } | ||
135 | } | ||
136 | |||
137 | + /* Attach only after starting new drained sections, so that recursive | ||
138 | + * drain sections coming from @child don't get an extra .drained_begin | ||
139 | + * callback. */ | ||
140 | if (child->role->attach) { | ||
141 | child->role->attach(child); | ||
142 | } | ||
143 | diff --git a/block/io.c b/block/io.c | ||
144 | index XXXXXXX..XXXXXXX 100644 | ||
145 | --- a/block/io.c | ||
146 | +++ b/block/io.c | ||
147 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
148 | assert(data.done); | ||
149 | } | ||
150 | |||
151 | -static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, | ||
152 | - BdrvChild *parent) | ||
153 | +void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, | ||
154 | + BdrvChild *parent) | ||
37 | { | 155 | { |
38 | BDRVQcow2State *s = bs->opaque; | 156 | BdrvChild *child, *next; |
39 | - uint64_t *l2_table; | 157 | |
40 | + uint64_t *l2_slice; | 158 | @@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, |
41 | int l2_index; | 159 | bdrv_drain_recurse(bs); |
42 | int ret; | 160 | |
43 | int i; | 161 | if (recursive) { |
44 | 162 | + bs->recursive_quiesce_counter++; | |
45 | - ret = get_cluster_table(bs, offset, &l2_table, &l2_index); | 163 | QLIST_FOREACH_SAFE(child, &bs->children, next, next) { |
46 | + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); | 164 | bdrv_do_drained_begin(child->bs, true, child); |
47 | if (ret < 0) { | 165 | } |
48 | return ret; | 166 | @@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs) |
167 | bdrv_do_drained_begin(bs, true, NULL); | ||
168 | } | ||
169 | |||
170 | -static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, | ||
171 | - BdrvChild *parent) | ||
172 | +void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, | ||
173 | + BdrvChild *parent) | ||
174 | { | ||
175 | BdrvChild *child, *next; | ||
176 | int old_quiesce_counter; | ||
177 | @@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, | ||
49 | } | 178 | } |
50 | 179 | ||
51 | - /* Limit nb_clusters to one L2 table */ | 180 | if (recursive) { |
52 | - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | 181 | + bs->recursive_quiesce_counter--; |
53 | + /* Limit nb_clusters to one L2 slice */ | 182 | QLIST_FOREACH_SAFE(child, &bs->children, next, next) { |
54 | + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); | 183 | bdrv_do_drained_end(child->bs, true, child); |
55 | assert(nb_clusters <= INT_MAX); | 184 | } |
56 | 185 | @@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs) | |
57 | for (i = 0; i < nb_clusters; i++) { | 186 | bdrv_do_drained_end(bs, true, NULL); |
58 | uint64_t old_l2_entry; | 187 | } |
59 | 188 | ||
60 | - old_l2_entry = be64_to_cpu(l2_table[l2_index + i]); | 189 | +void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) |
61 | + old_l2_entry = be64_to_cpu(l2_slice[l2_index + i]); | 190 | +{ |
62 | 191 | + int i; | |
63 | /* | 192 | + |
64 | * If full_discard is false, make sure that a discarded area reads back | 193 | + for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { |
65 | @@ -XXX,XX +XXX,XX @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | 194 | + bdrv_do_drained_begin(child->bs, true, child); |
66 | } | 195 | + } |
67 | 196 | +} | |
68 | /* First remove L2 entries */ | 197 | + |
69 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | 198 | +void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) |
70 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | 199 | +{ |
71 | if (!full_discard && s->qcow_version >= 3) { | 200 | + int i; |
72 | - l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | 201 | + |
73 | + l2_slice[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | 202 | + for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { |
74 | } else { | 203 | + bdrv_do_drained_end(child->bs, true, child); |
75 | - l2_table[l2_index + i] = cpu_to_be64(0); | 204 | + } |
76 | + l2_slice[l2_index + i] = cpu_to_be64(0); | 205 | +} |
77 | } | 206 | + |
78 | 207 | /* | |
79 | /* Then decrease the refcount */ | 208 | * Wait for pending requests to complete on a single BlockDriverState subtree, |
80 | qcow2_free_any_clusters(bs, old_l2_entry, 1, type); | 209 | * and suspend block driver's internal I/O until next request arrives. |
81 | } | ||
82 | |||
83 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
84 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
85 | |||
86 | return nb_clusters; | ||
87 | } | ||
88 | @@ -XXX,XX +XXX,XX @@ int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, | ||
89 | |||
90 | s->cache_discards = true; | ||
91 | |||
92 | - /* Each L2 table is handled by its own loop iteration */ | ||
93 | + /* Each L2 slice is handled by its own loop iteration */ | ||
94 | while (nb_clusters > 0) { | ||
95 | - cleared = discard_single_l2(bs, offset, nb_clusters, type, | ||
96 | - full_discard); | ||
97 | + cleared = discard_in_l2_slice(bs, offset, nb_clusters, type, | ||
98 | + full_discard); | ||
99 | if (cleared < 0) { | ||
100 | ret = cleared; | ||
101 | goto fail; | ||
102 | -- | 210 | -- |
103 | 2.13.6 | 211 | 2.13.6 |
104 | 212 | ||
105 | 213 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | ||
---|---|---|---|
2 | |||
3 | We want to use this function in sd_truncate() later on, so taking a | ||
4 | filename is not exactly ideal. | ||
5 | |||
6 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 2 | --- |
10 | block/sheepdog.c | 29 +++++++++++++++++++++-------- | 3 | tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ |
11 | 1 file changed, 21 insertions(+), 8 deletions(-) | 4 | 1 file changed, 80 insertions(+) |
12 | 5 | ||
13 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 6 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
14 | index XXXXXXX..XXXXXXX 100644 | 7 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/sheepdog.c | 8 | --- a/tests/test-bdrv-drain.c |
16 | +++ b/block/sheepdog.c | 9 | +++ b/tests/test-bdrv-drain.c |
17 | @@ -XXX,XX +XXX,XX @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, | 10 | @@ -XXX,XX +XXX,XX @@ static void test_multiparent(void) |
18 | return 0; | 11 | blk_unref(blk_b); |
19 | } | 12 | } |
20 | 13 | ||
21 | -static int sd_prealloc(const char *filename, Error **errp) | 14 | +static void test_graph_change(void) |
22 | +static int sd_prealloc(BlockDriverState *bs, Error **errp) | 15 | +{ |
23 | { | 16 | + BlockBackend *blk_a, *blk_b; |
24 | BlockBackend *blk = NULL; | 17 | + BlockDriverState *bs_a, *bs_b, *backing; |
25 | - BDRVSheepdogState *base = NULL; | 18 | + BDRVTestState *a_s, *b_s, *backing_s; |
26 | + BDRVSheepdogState *base = bs->opaque; | ||
27 | unsigned long buf_size; | ||
28 | uint32_t idx, max_idx; | ||
29 | uint32_t object_size; | ||
30 | @@ -XXX,XX +XXX,XX @@ static int sd_prealloc(const char *filename, Error **errp) | ||
31 | void *buf = NULL; | ||
32 | int ret; | ||
33 | |||
34 | - blk = blk_new_open(filename, NULL, NULL, | ||
35 | - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||
36 | - if (blk == NULL) { | ||
37 | - ret = -EIO; | ||
38 | + blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, | ||
39 | + BLK_PERM_ALL); | ||
40 | + | 19 | + |
41 | + ret = blk_insert_bs(blk, bs, errp); | 20 | + blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); |
42 | + if (ret < 0) { | 21 | + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, |
43 | goto out_with_err_set; | 22 | + &error_abort); |
44 | } | 23 | + a_s = bs_a->opaque; |
45 | 24 | + blk_insert_bs(blk_a, bs_a, &error_abort); | |
46 | @@ -XXX,XX +XXX,XX @@ static int sd_prealloc(const char *filename, Error **errp) | ||
47 | goto out; | ||
48 | } | ||
49 | |||
50 | - base = blk_bs(blk)->opaque; | ||
51 | object_size = (UINT32_C(1) << base->inode.block_size_shift); | ||
52 | buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); | ||
53 | buf = g_malloc0(buf_size); | ||
54 | @@ -XXX,XX +XXX,XX @@ static int sd_create(const char *filename, QemuOpts *opts, | ||
55 | } | ||
56 | |||
57 | if (prealloc) { | ||
58 | - ret = sd_prealloc(filename, errp); | ||
59 | + BlockDriverState *bs; | ||
60 | + QDict *opts; | ||
61 | + | 25 | + |
62 | + opts = qdict_new(); | 26 | + blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); |
63 | + qdict_put_str(opts, "driver", "sheepdog"); | 27 | + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, |
64 | + bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR, | 28 | + &error_abort); |
65 | + errp); | 29 | + b_s = bs_b->opaque; |
66 | + if (!bs) { | 30 | + blk_insert_bs(blk_b, bs_b, &error_abort); |
67 | + goto out; | ||
68 | + } | ||
69 | + | 31 | + |
70 | + ret = sd_prealloc(bs, errp); | 32 | + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); |
33 | + backing_s = backing->opaque; | ||
34 | + bdrv_set_backing_hd(bs_a, backing, &error_abort); | ||
71 | + | 35 | + |
72 | + bdrv_unref(bs); | 36 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); |
73 | } | 37 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); |
74 | out: | 38 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); |
75 | g_free(backing_file); | 39 | + g_assert_cmpint(a_s->drain_count, ==, 0); |
40 | + g_assert_cmpint(b_s->drain_count, ==, 0); | ||
41 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
42 | + | ||
43 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); | ||
44 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); | ||
45 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); | ||
46 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); | ||
47 | + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); | ||
48 | + | ||
49 | + bdrv_set_backing_hd(bs_b, backing, &error_abort); | ||
50 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 5); | ||
51 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 5); | ||
52 | + g_assert_cmpint(backing->quiesce_counter, ==, 5); | ||
53 | + g_assert_cmpint(a_s->drain_count, ==, 5); | ||
54 | + g_assert_cmpint(b_s->drain_count, ==, 5); | ||
55 | + g_assert_cmpint(backing_s->drain_count, ==, 5); | ||
56 | + | ||
57 | + bdrv_set_backing_hd(bs_b, NULL, &error_abort); | ||
58 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 3); | ||
59 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 2); | ||
60 | + g_assert_cmpint(backing->quiesce_counter, ==, 3); | ||
61 | + g_assert_cmpint(a_s->drain_count, ==, 3); | ||
62 | + g_assert_cmpint(b_s->drain_count, ==, 2); | ||
63 | + g_assert_cmpint(backing_s->drain_count, ==, 3); | ||
64 | + | ||
65 | + bdrv_set_backing_hd(bs_b, backing, &error_abort); | ||
66 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 5); | ||
67 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 5); | ||
68 | + g_assert_cmpint(backing->quiesce_counter, ==, 5); | ||
69 | + g_assert_cmpint(a_s->drain_count, ==, 5); | ||
70 | + g_assert_cmpint(b_s->drain_count, ==, 5); | ||
71 | + g_assert_cmpint(backing_s->drain_count, ==, 5); | ||
72 | + | ||
73 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); | ||
74 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); | ||
75 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); | ||
76 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); | ||
77 | + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); | ||
78 | + | ||
79 | + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); | ||
80 | + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); | ||
81 | + g_assert_cmpint(backing->quiesce_counter, ==, 0); | ||
82 | + g_assert_cmpint(a_s->drain_count, ==, 0); | ||
83 | + g_assert_cmpint(b_s->drain_count, ==, 0); | ||
84 | + g_assert_cmpint(backing_s->drain_count, ==, 0); | ||
85 | + | ||
86 | + bdrv_unref(backing); | ||
87 | + bdrv_unref(bs_a); | ||
88 | + bdrv_unref(bs_b); | ||
89 | + blk_unref(blk_a); | ||
90 | + blk_unref(blk_b); | ||
91 | +} | ||
92 | + | ||
93 | |||
94 | typedef struct TestBlockJob { | ||
95 | BlockJob common; | ||
96 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
97 | |||
98 | g_test_add_func("/bdrv-drain/nested", test_nested); | ||
99 | g_test_add_func("/bdrv-drain/multiparent", test_multiparent); | ||
100 | + g_test_add_func("/bdrv-drain/graph-change", test_graph_change); | ||
101 | |||
102 | g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); | ||
103 | g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); | ||
76 | -- | 104 | -- |
77 | 2.13.6 | 105 | 2.13.6 |
78 | 106 | ||
79 | 107 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This function has not been returning the offset of the L2 table since | ||
4 | commit 3948d1d4876065160583e79533bf604481063833 | ||
5 | |||
6 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
9 | Message-id: b498733b6706a859a03678d74ecbd26aeba129aa.1517840876.git.berto@igalia.com | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
11 | --- | ||
12 | block/qcow2-cluster.c | 3 +-- | ||
13 | 1 file changed, 1 insertion(+), 2 deletions(-) | ||
14 | |||
15 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block/qcow2-cluster.c | ||
18 | +++ b/block/qcow2-cluster.c | ||
19 | @@ -XXX,XX +XXX,XX @@ fail: | ||
20 | * for a given disk offset, load (and allocate if needed) | ||
21 | * the l2 table. | ||
22 | * | ||
23 | - * the l2 table offset in the qcow2 file and the cluster index | ||
24 | - * in the l2 table are given to the caller. | ||
25 | + * the cluster index in the l2 table is given to the caller. | ||
26 | * | ||
27 | * Returns 0 on success, -errno in failure case | ||
28 | */ | ||
29 | -- | ||
30 | 2.13.6 | ||
31 | |||
32 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This function was only using the BlockDriverState parameter to get the | ||
4 | cache table size (since it was equal to the cluster size). This is no | ||
5 | longer necessary so this parameter can be removed. | ||
6 | |||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: e1f943a9e89e1deb876f45de1bb22419ccdb6ad3.1517840876.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2-cache.c | 13 ++++++------- | ||
14 | 1 file changed, 6 insertions(+), 7 deletions(-) | ||
15 | |||
16 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/block/qcow2-cache.c | ||
19 | +++ b/block/qcow2-cache.c | ||
20 | @@ -XXX,XX +XXX,XX @@ struct Qcow2Cache { | ||
21 | uint64_t cache_clean_lru_counter; | ||
22 | }; | ||
23 | |||
24 | -static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, | ||
25 | - Qcow2Cache *c, int table) | ||
26 | +static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table) | ||
27 | { | ||
28 | return (uint8_t *) c->table_array + (size_t) table * c->table_size; | ||
29 | } | ||
30 | @@ -XXX,XX +XXX,XX @@ static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, | ||
31 | { | ||
32 | /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ | ||
33 | #ifdef CONFIG_LINUX | ||
34 | - void *t = qcow2_cache_get_table_addr(bs, c, i); | ||
35 | + void *t = qcow2_cache_get_table_addr(c, i); | ||
36 | int align = getpagesize(); | ||
37 | size_t mem_size = (size_t) c->table_size * num_tables; | ||
38 | size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; | ||
39 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||
40 | } | ||
41 | |||
42 | ret = bdrv_pwrite(bs->file, c->entries[i].offset, | ||
43 | - qcow2_cache_get_table_addr(bs, c, i), c->table_size); | ||
44 | + qcow2_cache_get_table_addr(c, i), c->table_size); | ||
45 | if (ret < 0) { | ||
46 | return ret; | ||
47 | } | ||
48 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||
49 | } | ||
50 | |||
51 | ret = bdrv_pread(bs->file, offset, | ||
52 | - qcow2_cache_get_table_addr(bs, c, i), | ||
53 | + qcow2_cache_get_table_addr(c, i), | ||
54 | c->table_size); | ||
55 | if (ret < 0) { | ||
56 | return ret; | ||
57 | @@ -XXX,XX +XXX,XX @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||
58 | /* And return the right table */ | ||
59 | found: | ||
60 | c->entries[i].ref++; | ||
61 | - *table = qcow2_cache_get_table_addr(bs, c, i); | ||
62 | + *table = qcow2_cache_get_table_addr(c, i); | ||
63 | |||
64 | trace_qcow2_cache_get_done(qemu_coroutine_self(), | ||
65 | c == s->l2_table_cache, i); | ||
66 | @@ -XXX,XX +XXX,XX @@ void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | ||
67 | |||
68 | for (i = 0; i < c->size; i++) { | ||
69 | if (c->entries[i].offset == offset) { | ||
70 | - return qcow2_cache_get_table_addr(bs, c, i); | ||
71 | + return qcow2_cache_get_table_addr(c, i); | ||
72 | } | ||
73 | } | ||
74 | return NULL; | ||
75 | -- | ||
76 | 2.13.6 | ||
77 | |||
78 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This function was only using the BlockDriverState parameter to get the | ||
4 | cache table size (since it was equal to the cluster size). This is no | ||
5 | longer necessary so this parameter can be removed. | ||
6 | |||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: da3575d47c9a181a2cfd4715e53dd84a2c651017.1517840876.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2-cache.c | 9 ++++----- | ||
14 | 1 file changed, 4 insertions(+), 5 deletions(-) | ||
15 | |||
16 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/block/qcow2-cache.c | ||
19 | +++ b/block/qcow2-cache.c | ||
20 | @@ -XXX,XX +XXX,XX @@ static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table) | ||
21 | return (uint8_t *) c->table_array + (size_t) table * c->table_size; | ||
22 | } | ||
23 | |||
24 | -static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, | ||
25 | - Qcow2Cache *c, void *table) | ||
26 | +static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table) | ||
27 | { | ||
28 | ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; | ||
29 | int idx = table_offset / c->table_size; | ||
30 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||
31 | |||
32 | void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | ||
33 | { | ||
34 | - int i = qcow2_cache_get_table_idx(bs, c, *table); | ||
35 | + int i = qcow2_cache_get_table_idx(c, *table); | ||
36 | |||
37 | c->entries[i].ref--; | ||
38 | *table = NULL; | ||
39 | @@ -XXX,XX +XXX,XX @@ void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | ||
40 | void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, | ||
41 | void *table) | ||
42 | { | ||
43 | - int i = qcow2_cache_get_table_idx(bs, c, table); | ||
44 | + int i = qcow2_cache_get_table_idx(c, table); | ||
45 | assert(c->entries[i].offset != 0); | ||
46 | c->entries[i].dirty = true; | ||
47 | } | ||
48 | @@ -XXX,XX +XXX,XX @@ void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | ||
49 | |||
50 | void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table) | ||
51 | { | ||
52 | - int i = qcow2_cache_get_table_idx(bs, c, table); | ||
53 | + int i = qcow2_cache_get_table_idx(c, table); | ||
54 | |||
55 | assert(c->entries[i].ref == 0); | ||
56 | |||
57 | -- | ||
58 | 2.13.6 | ||
59 | |||
60 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | Since commit bde70715, base is the only node that is reopened in |
---|---|---|---|
2 | commit_start(). This means that the code, which still involves an | ||
3 | explicit BlockReopenQueue, can now be simplified by using bdrv_reopen(). | ||
2 | 4 | ||
3 | zero_single_l2() limits the number of clusters to be zeroed to the | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | amount that fits inside an L2 table. Since we'll be loading L2 slices | 6 | Reviewed-by: Fam Zheng <famz@redhat.com> |
5 | instead of full tables we need to update that limit. The function is | 7 | --- |
6 | renamed to zero_in_l2_slice() for clarity. | 8 | block/commit.c | 8 +------- |
9 | 1 file changed, 1 insertion(+), 7 deletions(-) | ||
7 | 10 | ||
8 | Apart from that, this function doesn't need any additional changes, so | 11 | diff --git a/block/commit.c b/block/commit.c |
9 | this patch simply updates the variable name from l2_table to l2_slice. | ||
10 | |||
11 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
12 | Message-id: ebc16e7e79fa6969d8975ef487d679794de4fbcc.1517840877.git.berto@igalia.com | ||
13 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2-cluster.c | 28 ++++++++++++++-------------- | ||
17 | 1 file changed, 14 insertions(+), 14 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/block/qcow2-cluster.c | 13 | --- a/block/commit.c |
22 | +++ b/block/qcow2-cluster.c | 14 | +++ b/block/commit.c |
23 | @@ -XXX,XX +XXX,XX @@ fail: | 15 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, |
24 | 16 | const char *filter_node_name, Error **errp) | |
25 | /* | ||
26 | * This zeroes as many clusters of nb_clusters as possible at once (i.e. | ||
27 | - * all clusters in the same L2 table) and returns the number of zeroed | ||
28 | + * all clusters in the same L2 slice) and returns the number of zeroed | ||
29 | * clusters. | ||
30 | */ | ||
31 | -static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||
32 | - uint64_t nb_clusters, int flags) | ||
33 | +static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, | ||
34 | + uint64_t nb_clusters, int flags) | ||
35 | { | 17 | { |
36 | BDRVQcow2State *s = bs->opaque; | 18 | CommitBlockJob *s; |
37 | - uint64_t *l2_table; | 19 | - BlockReopenQueue *reopen_queue = NULL; |
38 | + uint64_t *l2_slice; | 20 | int orig_base_flags; |
39 | int l2_index; | 21 | BlockDriverState *iter; |
40 | int ret; | 22 | BlockDriverState *commit_top_bs = NULL; |
41 | int i; | 23 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, |
42 | bool unmap = !!(flags & BDRV_REQ_MAY_UNMAP); | 24 | /* convert base to r/w, if necessary */ |
43 | 25 | orig_base_flags = bdrv_get_flags(base); | |
44 | - ret = get_cluster_table(bs, offset, &l2_table, &l2_index); | 26 | if (!(orig_base_flags & BDRV_O_RDWR)) { |
45 | + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); | 27 | - reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, |
46 | if (ret < 0) { | 28 | - orig_base_flags | BDRV_O_RDWR); |
47 | return ret; | 29 | - } |
48 | } | 30 | - |
49 | 31 | - if (reopen_queue) { | |
50 | - /* Limit nb_clusters to one L2 table */ | 32 | - bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err); |
51 | - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | 33 | + bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); |
52 | + /* Limit nb_clusters to one L2 slice */ | 34 | if (local_err != NULL) { |
53 | + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); | 35 | error_propagate(errp, local_err); |
54 | assert(nb_clusters <= INT_MAX); | ||
55 | |||
56 | for (i = 0; i < nb_clusters; i++) { | ||
57 | uint64_t old_offset; | ||
58 | QCow2ClusterType cluster_type; | ||
59 | |||
60 | - old_offset = be64_to_cpu(l2_table[l2_index + i]); | ||
61 | + old_offset = be64_to_cpu(l2_slice[l2_index + i]); | ||
62 | |||
63 | /* | ||
64 | * Minimize L2 changes if the cluster already reads back as | ||
65 | @@ -XXX,XX +XXX,XX @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||
66 | continue; | ||
67 | } | ||
68 | |||
69 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
70 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | ||
71 | if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) { | ||
72 | - l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | ||
73 | + l2_slice[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | ||
74 | qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); | ||
75 | } else { | ||
76 | - l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); | ||
77 | + l2_slice[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
82 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
83 | |||
84 | return nb_clusters; | ||
85 | } | ||
86 | @@ -XXX,XX +XXX,XX @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, | ||
87 | return -ENOTSUP; | ||
88 | } | ||
89 | |||
90 | - /* Each L2 table is handled by its own loop iteration */ | ||
91 | + /* Each L2 slice is handled by its own loop iteration */ | ||
92 | nb_clusters = size_to_clusters(s, bytes); | ||
93 | |||
94 | s->cache_discards = true; | ||
95 | |||
96 | while (nb_clusters > 0) { | ||
97 | - cleared = zero_single_l2(bs, offset, nb_clusters, flags); | ||
98 | + cleared = zero_in_l2_slice(bs, offset, nb_clusters, flags); | ||
99 | if (cleared < 0) { | ||
100 | ret = cleared; | ||
101 | goto fail; | 36 | goto fail; |
102 | -- | 37 | -- |
103 | 2.13.6 | 38 | 2.13.6 |
104 | 39 | ||
105 | 40 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | The bdrv_reopen*() implementation doesn't like it if the graph is |
---|---|---|---|
2 | changed between queuing nodes for reopen and actually reopening them | ||
3 | (one of the reasons is that queuing can be recursive). | ||
2 | 4 | ||
3 | This function was only using the BlockDriverState parameter to pass it | 5 | So instead of draining the device only in bdrv_reopen_multiple(), |
4 | to qcow2_cache_get_table_idx(). This is no longer necessary so this | 6 | require that callers already drained all affected nodes, and assert this |
5 | parameter can be removed. | 7 | in bdrv_reopen_queue(). |
6 | 8 | ||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 10 | Reviewed-by: Fam Zheng <famz@redhat.com> |
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 6f98155489054a457563da77cdad1a66ebb3e896.1517840876.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | 11 | --- |
13 | block/qcow2.h | 2 +- | 12 | block.c | 23 ++++++++++++++++------- |
14 | block/qcow2-cache.c | 2 +- | 13 | block/replication.c | 6 ++++++ |
15 | block/qcow2-cluster.c | 28 ++++++++++++++-------------- | 14 | qemu-io-cmds.c | 3 +++ |
16 | block/qcow2-refcount.c | 30 +++++++++++++++--------------- | 15 | 3 files changed, 25 insertions(+), 7 deletions(-) |
17 | 4 files changed, 31 insertions(+), 31 deletions(-) | ||
18 | 16 | ||
19 | diff --git a/block/qcow2.h b/block/qcow2.h | 17 | diff --git a/block.c b/block.c |
20 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/block/qcow2.h | 19 | --- a/block.c |
22 | +++ b/block/qcow2.h | 20 | +++ b/block.c |
23 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | 21 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, |
24 | void **table); | 22 | * returns a pointer to bs_queue, which is either the newly allocated |
25 | int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | 23 | * bs_queue, or the existing bs_queue being used. |
26 | void **table); | 24 | * |
27 | -void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); | 25 | + * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple(). |
28 | +void qcow2_cache_put(Qcow2Cache *c, void **table); | 26 | */ |
29 | void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | 27 | static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, |
30 | uint64_t offset); | 28 | BlockDriverState *bs, |
31 | void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table); | 29 | @@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, |
32 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | 30 | BdrvChild *child; |
33 | index XXXXXXX..XXXXXXX 100644 | 31 | QDict *old_options, *explicit_options; |
34 | --- a/block/qcow2-cache.c | 32 | |
35 | +++ b/block/qcow2-cache.c | 33 | + /* Make sure that the caller remembered to use a drained section. This is |
36 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | 34 | + * important to avoid graph changes between the recursive queuing here and |
37 | return qcow2_cache_do_get(bs, c, offset, table, false); | 35 | + * bdrv_reopen_multiple(). */ |
38 | } | 36 | + assert(bs->quiesce_counter > 0); |
39 | 37 | + | |
40 | -void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | 38 | if (bs_queue == NULL) { |
41 | +void qcow2_cache_put(Qcow2Cache *c, void **table) | 39 | bs_queue = g_new0(BlockReopenQueue, 1); |
40 | QSIMPLEQ_INIT(bs_queue); | ||
41 | @@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, | ||
42 | * If all devices prepare successfully, then the changes are committed | ||
43 | * to all devices. | ||
44 | * | ||
45 | + * All affected nodes must be drained between bdrv_reopen_queue() and | ||
46 | + * bdrv_reopen_multiple(). | ||
47 | */ | ||
48 | int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp) | ||
42 | { | 49 | { |
43 | int i = qcow2_cache_get_table_idx(c, *table); | 50 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er |
44 | 51 | ||
45 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | 52 | assert(bs_queue != NULL); |
46 | index XXXXXXX..XXXXXXX 100644 | 53 | |
47 | --- a/block/qcow2-cluster.c | 54 | - aio_context_release(ctx); |
48 | +++ b/block/qcow2-cluster.c | 55 | - bdrv_drain_all_begin(); |
49 | @@ -XXX,XX +XXX,XX @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | 56 | - aio_context_acquire(ctx); |
50 | 57 | - | |
51 | memcpy(l2_table, old_table, s->cluster_size); | 58 | QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { |
52 | 59 | + assert(bs_entry->state.bs->quiesce_counter > 0); | |
53 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &old_table); | 60 | if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { |
54 | + qcow2_cache_put(s->l2_table_cache, (void **) &old_table); | 61 | error_propagate(errp, local_err); |
62 | goto cleanup; | ||
63 | @@ -XXX,XX +XXX,XX @@ cleanup: | ||
55 | } | 64 | } |
56 | 65 | g_free(bs_queue); | |
57 | /* write the l2 table to the file */ | 66 | |
58 | @@ -XXX,XX +XXX,XX @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | 67 | - bdrv_drain_all_end(); |
59 | fail: | 68 | - |
60 | trace_qcow2_l2_allocate_done(bs, l1_index, ret); | ||
61 | if (l2_table != NULL) { | ||
62 | - qcow2_cache_put(bs, s->l2_table_cache, (void**) table); | ||
63 | + qcow2_cache_put(s->l2_table_cache, (void **) table); | ||
64 | } | ||
65 | s->l1_table[l1_index] = old_l2_offset; | ||
66 | if (l2_offset > 0) { | ||
67 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
68 | abort(); | ||
69 | } | ||
70 | |||
71 | - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||
72 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
73 | |||
74 | bytes_available = (int64_t)c * s->cluster_size; | ||
75 | |||
76 | @@ -XXX,XX +XXX,XX @@ out: | ||
77 | return type; | ||
78 | |||
79 | fail: | ||
80 | - qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); | ||
81 | + qcow2_cache_put(s->l2_table_cache, (void **)&l2_table); | ||
82 | return ret; | 69 | return ret; |
83 | } | 70 | } |
84 | 71 | ||
85 | @@ -XXX,XX +XXX,XX @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | 72 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) |
86 | * allocated. */ | 73 | { |
87 | cluster_offset = be64_to_cpu(l2_table[l2_index]); | 74 | int ret = -1; |
88 | if (cluster_offset & L2E_OFFSET_MASK) { | 75 | Error *local_err = NULL; |
89 | - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | 76 | - BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); |
90 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | 77 | + BlockReopenQueue *queue; |
91 | return 0; | 78 | |
79 | + bdrv_subtree_drained_begin(bs); | ||
80 | + | ||
81 | + queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); | ||
82 | ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err); | ||
83 | if (local_err != NULL) { | ||
84 | error_propagate(errp, local_err); | ||
92 | } | 85 | } |
93 | 86 | + | |
94 | cluster_offset = qcow2_alloc_bytes(bs, compressed_size); | 87 | + bdrv_subtree_drained_end(bs); |
95 | if (cluster_offset < 0) { | 88 | + |
96 | - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||
97 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | @@ -XXX,XX +XXX,XX @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||
102 | BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); | ||
103 | qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
104 | l2_table[l2_index] = cpu_to_be64(cluster_offset); | ||
105 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
106 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
107 | |||
108 | return cluster_offset; | ||
109 | } | ||
110 | @@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||
111 | } | ||
112 | |||
113 | |||
114 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
115 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
116 | |||
117 | /* | ||
118 | * If this was a COW, we need to decrease the refcount of the old cluster. | ||
119 | @@ -XXX,XX +XXX,XX @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||
120 | |||
121 | /* Cleanup */ | ||
122 | out: | ||
123 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
124 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
125 | |||
126 | /* Only return a host offset if we actually made progress. Otherwise we | ||
127 | * would make requirements for handle_alloc() that it can't fulfill */ | ||
128 | @@ -XXX,XX +XXX,XX @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||
129 | keep_old_clusters = true; | ||
130 | } | ||
131 | |||
132 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
133 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
134 | |||
135 | if (!alloc_cluster_offset) { | ||
136 | /* Allocate, if necessary at a given offset in the image file */ | ||
137 | @@ -XXX,XX +XXX,XX @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||
138 | qcow2_free_any_clusters(bs, old_l2_entry, 1, type); | ||
139 | } | ||
140 | |||
141 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
142 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
143 | |||
144 | return nb_clusters; | ||
145 | } | ||
146 | @@ -XXX,XX +XXX,XX @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||
147 | } | ||
148 | } | ||
149 | |||
150 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
151 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
152 | |||
153 | return nb_clusters; | ||
154 | } | ||
155 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
156 | qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
157 | qcow2_cache_depends_on_flush(s->l2_table_cache); | ||
158 | } | ||
159 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
160 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
161 | } else { | ||
162 | if (l2_dirty) { | ||
163 | ret = qcow2_pre_write_overlap_check(bs, | ||
164 | @@ -XXX,XX +XXX,XX @@ fail: | ||
165 | if (!is_active_l1) { | ||
166 | qemu_vfree(l2_table); | ||
167 | } else { | ||
168 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||
169 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
170 | } | ||
171 | } | ||
172 | return ret; | ||
173 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
174 | index XXXXXXX..XXXXXXX 100644 | ||
175 | --- a/block/qcow2-refcount.c | ||
176 | +++ b/block/qcow2-refcount.c | ||
177 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, | ||
178 | block_index = cluster_index & (s->refcount_block_size - 1); | ||
179 | *refcount = s->get_refcount(refcount_block, block_index); | ||
180 | |||
181 | - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||
182 | + qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | @@ -XXX,XX +XXX,XX @@ static int alloc_refcount_block(BlockDriverState *bs, | ||
187 | return -EAGAIN; | ||
188 | } | ||
189 | |||
190 | - qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); | ||
191 | + qcow2_cache_put(s->refcount_block_cache, refcount_block); | ||
192 | |||
193 | /* | ||
194 | * If we come here, we need to grow the refcount table. Again, a new | ||
195 | @@ -XXX,XX +XXX,XX @@ static int alloc_refcount_block(BlockDriverState *bs, | ||
196 | |||
197 | fail: | ||
198 | if (*refcount_block != NULL) { | ||
199 | - qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); | ||
200 | + qcow2_cache_put(s->refcount_block_cache, refcount_block); | ||
201 | } | ||
202 | return ret; | 89 | return ret; |
203 | } | 90 | } |
204 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | 91 | |
205 | refblock_data); | 92 | diff --git a/block/replication.c b/block/replication.c |
206 | } | 93 | index XXXXXXX..XXXXXXX 100644 |
207 | 94 | --- a/block/replication.c | |
208 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock_data); | 95 | +++ b/block/replication.c |
209 | + qcow2_cache_put(s->refcount_block_cache, &refblock_data); | 96 | @@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, |
97 | new_secondary_flags = s->orig_secondary_flags; | ||
210 | } | 98 | } |
211 | 99 | ||
212 | assert(block_offset == table_offset); | 100 | + bdrv_subtree_drained_begin(s->hidden_disk->bs); |
213 | @@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | 101 | + bdrv_subtree_drained_begin(s->secondary_disk->bs); |
214 | /* Load the refcount block and allocate it if needed */ | 102 | + |
215 | if (table_index != old_table_index) { | 103 | if (orig_hidden_flags != new_hidden_flags) { |
216 | if (refcount_block) { | 104 | reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, |
217 | - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | 105 | new_hidden_flags); |
218 | + qcow2_cache_put(s->refcount_block_cache, &refcount_block); | 106 | @@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, |
219 | } | 107 | reopen_queue, &local_err); |
220 | ret = alloc_refcount_block(bs, cluster_index, &refcount_block); | 108 | error_propagate(errp, local_err); |
221 | if (ret < 0) { | ||
222 | @@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||
223 | table = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, | ||
224 | offset); | ||
225 | if (table != NULL) { | ||
226 | - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||
227 | + qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||
228 | qcow2_cache_discard(bs, s->refcount_block_cache, table); | ||
229 | } | ||
230 | |||
231 | @@ -XXX,XX +XXX,XX @@ fail: | ||
232 | |||
233 | /* Write last changed block to disk */ | ||
234 | if (refcount_block) { | ||
235 | - qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||
236 | + qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||
237 | } | 109 | } |
238 | 110 | + | |
239 | /* | 111 | + bdrv_subtree_drained_end(s->hidden_disk->bs); |
240 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | 112 | + bdrv_subtree_drained_end(s->secondary_disk->bs); |
241 | } | 113 | } |
242 | } | 114 | |
243 | 115 | static void backup_job_cleanup(BlockDriverState *bs) | |
244 | - qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | 116 | diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c |
245 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | 117 | index XXXXXXX..XXXXXXX 100644 |
246 | 118 | --- a/qemu-io-cmds.c | |
247 | if (addend != 0) { | 119 | +++ b/qemu-io-cmds.c |
248 | ret = qcow2_update_cluster_refcount(bs, l2_offset >> | 120 | @@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) |
249 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | 121 | opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL; |
250 | ret = bdrv_flush(bs); | 122 | qemu_opts_reset(&reopen_opts); |
251 | fail: | 123 | |
252 | if (l2_table) { | 124 | + bdrv_subtree_drained_begin(bs); |
253 | - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | 125 | brq = bdrv_reopen_queue(NULL, bs, opts, flags); |
254 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | 126 | bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err); |
255 | } | 127 | + bdrv_subtree_drained_end(bs); |
256 | 128 | + | |
257 | s->cache_discards = false; | 129 | if (local_err) { |
258 | @@ -XXX,XX +XXX,XX @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, | 130 | error_report_err(local_err); |
259 | new_reftable_size, new_refblock, | 131 | } else { |
260 | new_refblock_empty, allocated, errp); | ||
261 | if (ret < 0) { | ||
262 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
263 | + qcow2_cache_put(s->refcount_block_cache, &refblock); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | @@ -XXX,XX +XXX,XX @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, | ||
268 | if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { | ||
269 | uint64_t offset; | ||
270 | |||
271 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
272 | + qcow2_cache_put(s->refcount_block_cache, &refblock); | ||
273 | |||
274 | offset = ((reftable_index << s->refcount_block_bits) | ||
275 | + refblock_index) << s->cluster_bits; | ||
276 | @@ -XXX,XX +XXX,XX @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, | ||
277 | new_refblock_empty = new_refblock_empty && refcount == 0; | ||
278 | } | ||
279 | |||
280 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
281 | + qcow2_cache_put(s->refcount_block_cache, &refblock); | ||
282 | } else { | ||
283 | /* No refblock means every refcount is 0 */ | ||
284 | for (refblock_index = 0; refblock_index < s->refcount_block_size; | ||
285 | @@ -XXX,XX +XXX,XX @@ static int qcow2_discard_refcount_block(BlockDriverState *bs, | ||
286 | offset_to_reftable_index(s, discard_block_offs), | ||
287 | discard_block_offs, | ||
288 | s->get_refcount(refblock, block_index)); | ||
289 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
290 | + qcow2_cache_put(s->refcount_block_cache, &refblock); | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | s->set_refcount(refblock, block_index, 0); | ||
294 | |||
295 | qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock); | ||
296 | |||
297 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
298 | + qcow2_cache_put(s->refcount_block_cache, &refblock); | ||
299 | |||
300 | if (cluster_index < s->free_cluster_index) { | ||
301 | s->free_cluster_index = cluster_index; | ||
302 | @@ -XXX,XX +XXX,XX @@ int qcow2_shrink_reftable(BlockDriverState *bs) | ||
303 | } else { | ||
304 | unused_block = buffer_is_zero(refblock, s->cluster_size); | ||
305 | } | ||
306 | - qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||
307 | + qcow2_cache_put(s->refcount_block_cache, &refblock); | ||
308 | |||
309 | reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); | ||
310 | } | ||
311 | -- | 132 | -- |
312 | 2.13.6 | 133 | 2.13.6 |
313 | 134 | ||
314 | 135 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This function was only using the BlockDriverState parameter to pass it | ||
4 | to qcow2_cache_get_table_idx() and qcow2_cache_table_release(). This | ||
5 | is no longer necessary so this parameter can be removed. | ||
6 | |||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 9724f7e38e763ad3be32627c6b7fe8df9edb1476.1517840877.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2.h | 2 +- | ||
14 | block/qcow2-cache.c | 2 +- | ||
15 | block/qcow2-refcount.c | 6 +++--- | ||
16 | 3 files changed, 5 insertions(+), 5 deletions(-) | ||
17 | |||
18 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/block/qcow2.h | ||
21 | +++ b/block/qcow2.h | ||
22 | @@ -XXX,XX +XXX,XX @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||
23 | void qcow2_cache_put(Qcow2Cache *c, void **table); | ||
24 | void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | ||
25 | uint64_t offset); | ||
26 | -void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table); | ||
27 | +void qcow2_cache_discard(Qcow2Cache *c, void *table); | ||
28 | |||
29 | /* qcow2-bitmap.c functions */ | ||
30 | int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||
31 | diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block/qcow2-cache.c | ||
34 | +++ b/block/qcow2-cache.c | ||
35 | @@ -XXX,XX +XXX,XX @@ void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, | ||
36 | return NULL; | ||
37 | } | ||
38 | |||
39 | -void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table) | ||
40 | +void qcow2_cache_discard(Qcow2Cache *c, void *table) | ||
41 | { | ||
42 | int i = qcow2_cache_get_table_idx(c, table); | ||
43 | |||
44 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/block/qcow2-refcount.c | ||
47 | +++ b/block/qcow2-refcount.c | ||
48 | @@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||
49 | offset); | ||
50 | if (table != NULL) { | ||
51 | qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||
52 | - qcow2_cache_discard(bs, s->refcount_block_cache, table); | ||
53 | + qcow2_cache_discard(s->refcount_block_cache, table); | ||
54 | } | ||
55 | |||
56 | table = qcow2_cache_is_table_offset(bs, s->l2_table_cache, offset); | ||
57 | if (table != NULL) { | ||
58 | - qcow2_cache_discard(bs, s->l2_table_cache, table); | ||
59 | + qcow2_cache_discard(s->l2_table_cache, table); | ||
60 | } | ||
61 | |||
62 | if (s->discard_passthrough[type]) { | ||
63 | @@ -XXX,XX +XXX,XX @@ static int qcow2_discard_refcount_block(BlockDriverState *bs, | ||
64 | discard_block_offs); | ||
65 | if (refblock) { | ||
66 | /* discard refblock from the cache if refblock is cached */ | ||
67 | - qcow2_cache_discard(bs, s->refcount_block_cache, refblock); | ||
68 | + qcow2_cache_discard(s->refcount_block_cache, refblock); | ||
69 | } | ||
70 | update_refcount_discard(bs, discard_block_offs, s->cluster_size); | ||
71 | |||
72 | -- | ||
73 | 2.13.6 | ||
74 | |||
75 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | The BDRVQcow2State structure contains an l2_size field, which stores | ||
4 | the number of 64-bit entries in an L2 table. | ||
5 | |||
6 | For efficiency reasons we want to be able to load slices instead of | ||
7 | full L2 tables, so we need to know how many entries an L2 slice can | ||
8 | hold. | ||
9 | |||
10 | An L2 slice is the portion of an L2 table that is loaded by the qcow2 | ||
11 | cache. At the moment that cache can only load complete tables, | ||
12 | therefore an L2 slice has the same size as an L2 table (one cluster) | ||
13 | and l2_size == l2_slice_size. | ||
14 | |||
15 | Later we'll allow smaller slices, but until then we have to use this | ||
16 | new l2_slice_size field to make the rest of the code ready for that. | ||
17 | |||
18 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
19 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
20 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
21 | Message-id: adb048595f9fb5dfb110c802a8b3c3be3b937f37.1517840877.git.berto@igalia.com | ||
22 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
23 | --- | ||
24 | block/qcow2.h | 1 + | ||
25 | block/qcow2.c | 3 +++ | ||
26 | 2 files changed, 4 insertions(+) | ||
27 | |||
28 | diff --git a/block/qcow2.h b/block/qcow2.h | ||
29 | index XXXXXXX..XXXXXXX 100644 | ||
30 | --- a/block/qcow2.h | ||
31 | +++ b/block/qcow2.h | ||
32 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVQcow2State { | ||
33 | int cluster_bits; | ||
34 | int cluster_size; | ||
35 | int cluster_sectors; | ||
36 | + int l2_slice_size; | ||
37 | int l2_bits; | ||
38 | int l2_size; | ||
39 | int l1_size; | ||
40 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
41 | index XXXXXXX..XXXXXXX 100644 | ||
42 | --- a/block/qcow2.c | ||
43 | +++ b/block/qcow2.c | ||
44 | @@ -XXX,XX +XXX,XX @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, | ||
45 | typedef struct Qcow2ReopenState { | ||
46 | Qcow2Cache *l2_table_cache; | ||
47 | Qcow2Cache *refcount_block_cache; | ||
48 | + int l2_slice_size; /* Number of entries in a slice of the L2 table */ | ||
49 | bool use_lazy_refcounts; | ||
50 | int overlap_check; | ||
51 | bool discard_passthrough[QCOW2_DISCARD_MAX]; | ||
52 | @@ -XXX,XX +XXX,XX @@ static int qcow2_update_options_prepare(BlockDriverState *bs, | ||
53 | } | ||
54 | } | ||
55 | |||
56 | + r->l2_slice_size = s->cluster_size / sizeof(uint64_t); | ||
57 | r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); | ||
58 | r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); | ||
59 | if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { | ||
60 | @@ -XXX,XX +XXX,XX @@ static void qcow2_update_options_commit(BlockDriverState *bs, | ||
61 | } | ||
62 | s->l2_table_cache = r->l2_table_cache; | ||
63 | s->refcount_block_cache = r->refcount_block_cache; | ||
64 | + s->l2_slice_size = r->l2_slice_size; | ||
65 | |||
66 | s->overlap_check = r->overlap_check; | ||
67 | s->use_lazy_refcounts = r->use_lazy_refcounts; | ||
68 | -- | ||
69 | 2.13.6 | ||
70 | |||
71 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | Each entry in the qcow2 L2 cache stores a full L2 table (which uses a | ||
4 | complete cluster in the qcow2 image). A cluster is usually too large | ||
5 | to be used efficiently as the size for a cache entry, so we want to | ||
6 | decouple both values by allowing smaller cache entries. Therefore the | ||
7 | qcow2 L2 cache will no longer return full L2 tables but slices | ||
8 | instead. | ||
9 | |||
10 | This patch updates l2_load() so it can handle L2 slices correctly. | ||
11 | Apart from the offset of the L2 table (which we already had) we also | ||
12 | need the guest offset in order to calculate which one of the slices | ||
13 | we need. | ||
14 | |||
15 | An L2 slice has currently the same size as an L2 table (one cluster), | ||
16 | so for now this function will load exactly the same data as before. | ||
17 | |||
18 | This patch also removes a stale comment about the return value being | ||
19 | a pointer to the L2 table. This function returns an error code since | ||
20 | 55c17e9821c474d5fcdebdc82ed2fc096777d611. | ||
21 | |||
22 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
23 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
24 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
25 | Message-id: b830aa1fc5b6f8e3cb331d006853fe22facca847.1517840877.git.berto@igalia.com | ||
26 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
27 | --- | ||
28 | block/qcow2-cluster.c | 28 +++++++++++++++++----------- | ||
29 | 1 file changed, 17 insertions(+), 11 deletions(-) | ||
30 | |||
31 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block/qcow2-cluster.c | ||
34 | +++ b/block/qcow2-cluster.c | ||
35 | @@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||
36 | /* | ||
37 | * l2_load | ||
38 | * | ||
39 | - * Loads a L2 table into memory. If the table is in the cache, the cache | ||
40 | - * is used; otherwise the L2 table is loaded from the image file. | ||
41 | + * @bs: The BlockDriverState | ||
42 | + * @offset: A guest offset, used to calculate what slice of the L2 | ||
43 | + * table to load. | ||
44 | + * @l2_offset: Offset to the L2 table in the image file. | ||
45 | + * @l2_slice: Location to store the pointer to the L2 slice. | ||
46 | * | ||
47 | - * Returns a pointer to the L2 table on success, or NULL if the read from | ||
48 | - * the image file failed. | ||
49 | + * Loads a L2 slice into memory (L2 slices are the parts of L2 tables | ||
50 | + * that are loaded by the qcow2 cache). If the slice is in the cache, | ||
51 | + * the cache is used; otherwise the L2 slice is loaded from the image | ||
52 | + * file. | ||
53 | */ | ||
54 | - | ||
55 | -static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | ||
56 | - uint64_t **l2_table) | ||
57 | +static int l2_load(BlockDriverState *bs, uint64_t offset, | ||
58 | + uint64_t l2_offset, uint64_t **l2_slice) | ||
59 | { | ||
60 | BDRVQcow2State *s = bs->opaque; | ||
61 | + int start_of_slice = sizeof(uint64_t) * | ||
62 | + (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset)); | ||
63 | |||
64 | - return qcow2_cache_get(bs, s->l2_table_cache, l2_offset, | ||
65 | - (void **)l2_table); | ||
66 | + return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice, | ||
67 | + (void **)l2_slice); | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | @@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
72 | |||
73 | /* load the l2 table in memory */ | ||
74 | |||
75 | - ret = l2_load(bs, l2_offset, &l2_table); | ||
76 | + ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
77 | if (ret < 0) { | ||
78 | return ret; | ||
79 | } | ||
80 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
81 | |||
82 | if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) { | ||
83 | /* load the l2 table in memory */ | ||
84 | - ret = l2_load(bs, l2_offset, &l2_table); | ||
85 | + ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
86 | if (ret < 0) { | ||
87 | return ret; | ||
88 | } | ||
89 | -- | ||
90 | 2.13.6 | ||
91 | |||
92 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | Adding support for L2 slices to l2_allocate() needs (among other | ||
4 | things) an extra loop that iterates over all slices of a new L2 table. | ||
5 | |||
6 | Putting all changes in one patch would make it hard to read because | ||
7 | all semantic changes would be mixed with pure indentation changes. | ||
8 | |||
9 | To make things easier this patch simply creates a new block and | ||
10 | changes the indentation of all lines of code inside it. Thus, all | ||
11 | modifications in this patch are cosmetic. There are no semantic | ||
12 | changes and no variables are renamed yet. The next patch will take | ||
13 | care of that. | ||
14 | |||
15 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
16 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
17 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
18 | Message-id: d0d7dca8520db304524f52f49d8157595a707a35.1517840877.git.berto@igalia.com | ||
19 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
20 | --- | ||
21 | block/qcow2-cluster.c | 53 ++++++++++++++++++++++++++++----------------------- | ||
22 | 1 file changed, 29 insertions(+), 24 deletions(-) | ||
23 | |||
24 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/block/qcow2-cluster.c | ||
27 | +++ b/block/qcow2-cluster.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||
29 | /* allocate a new entry in the l2 cache */ | ||
30 | |||
31 | trace_qcow2_l2_allocate_get_empty(bs, l1_index); | ||
32 | - ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); | ||
33 | - if (ret < 0) { | ||
34 | - goto fail; | ||
35 | - } | ||
36 | + { | ||
37 | + ret = qcow2_cache_get_empty(bs, s->l2_table_cache, | ||
38 | + l2_offset, | ||
39 | + (void **) table); | ||
40 | + if (ret < 0) { | ||
41 | + goto fail; | ||
42 | + } | ||
43 | |||
44 | - l2_table = *table; | ||
45 | + l2_table = *table; | ||
46 | |||
47 | - if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { | ||
48 | - /* if there was no old l2 table, clear the new table */ | ||
49 | - memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||
50 | - } else { | ||
51 | - uint64_t* old_table; | ||
52 | + if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { | ||
53 | + /* if there was no old l2 table, clear the new table */ | ||
54 | + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||
55 | + } else { | ||
56 | + uint64_t *old_table; | ||
57 | |||
58 | - /* if there was an old l2 table, read it from the disk */ | ||
59 | - BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); | ||
60 | - ret = qcow2_cache_get(bs, s->l2_table_cache, | ||
61 | - old_l2_offset & L1E_OFFSET_MASK, | ||
62 | - (void**) &old_table); | ||
63 | - if (ret < 0) { | ||
64 | - goto fail; | ||
65 | + /* if there was an old l2 table, read it from the disk */ | ||
66 | + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); | ||
67 | + ret = qcow2_cache_get(bs, s->l2_table_cache, | ||
68 | + old_l2_offset & L1E_OFFSET_MASK, | ||
69 | + (void **) &old_table); | ||
70 | + if (ret < 0) { | ||
71 | + goto fail; | ||
72 | + } | ||
73 | + | ||
74 | + memcpy(l2_table, old_table, s->cluster_size); | ||
75 | + | ||
76 | + qcow2_cache_put(s->l2_table_cache, (void **) &old_table); | ||
77 | } | ||
78 | |||
79 | - memcpy(l2_table, old_table, s->cluster_size); | ||
80 | + /* write the l2 table to the file */ | ||
81 | + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); | ||
82 | |||
83 | - qcow2_cache_put(s->l2_table_cache, (void **) &old_table); | ||
84 | + trace_qcow2_l2_allocate_write_l2(bs, l1_index); | ||
85 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
86 | } | ||
87 | |||
88 | - /* write the l2 table to the file */ | ||
89 | - BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); | ||
90 | - | ||
91 | - trace_qcow2_l2_allocate_write_l2(bs, l1_index); | ||
92 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
93 | ret = qcow2_cache_flush(bs, s->l2_table_cache); | ||
94 | if (ret < 0) { | ||
95 | goto fail; | ||
96 | -- | ||
97 | 2.13.6 | ||
98 | |||
99 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This patch updates l2_allocate() to support the qcow2 cache returning | ||
4 | L2 slices instead of full L2 tables. | ||
5 | |||
6 | The old code simply gets an L2 table from the cache and initializes it | ||
7 | with zeroes or with the contents of an existing table. With a cache | ||
8 | that returns slices instead of tables the idea remains the same, but | ||
9 | the code must now iterate over all the slices that are contained in an | ||
10 | L2 table. | ||
11 | |||
12 | Since now we're operating with slices the function can no longer | ||
13 | return the newly-allocated table, so it's up to the caller to retrieve | ||
14 | the appropriate L2 slice after calling l2_allocate() (note that with | ||
15 | this patch the caller is still loading full L2 tables, but we'll deal | ||
16 | with that in a separate patch). | ||
17 | |||
18 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
19 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
20 | Message-id: 20fc0415bf0e011e29f6487ec86eb06a11f37445.1517840877.git.berto@igalia.com | ||
21 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
22 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
23 | --- | ||
24 | block/qcow2-cluster.c | 56 +++++++++++++++++++++++++++++++-------------------- | ||
25 | 1 file changed, 34 insertions(+), 22 deletions(-) | ||
26 | |||
27 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/block/qcow2-cluster.c | ||
30 | +++ b/block/qcow2-cluster.c | ||
31 | @@ -XXX,XX +XXX,XX @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | -static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||
36 | +static int l2_allocate(BlockDriverState *bs, int l1_index) | ||
37 | { | ||
38 | BDRVQcow2State *s = bs->opaque; | ||
39 | uint64_t old_l2_offset; | ||
40 | - uint64_t *l2_table = NULL; | ||
41 | + uint64_t *l2_slice = NULL; | ||
42 | + unsigned slice, slice_size2, n_slices; | ||
43 | int64_t l2_offset; | ||
44 | int ret; | ||
45 | |||
46 | @@ -XXX,XX +XXX,XX @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||
47 | |||
48 | /* allocate a new entry in the l2 cache */ | ||
49 | |||
50 | + slice_size2 = s->l2_slice_size * sizeof(uint64_t); | ||
51 | + n_slices = s->cluster_size / slice_size2; | ||
52 | + | ||
53 | trace_qcow2_l2_allocate_get_empty(bs, l1_index); | ||
54 | - { | ||
55 | + for (slice = 0; slice < n_slices; slice++) { | ||
56 | ret = qcow2_cache_get_empty(bs, s->l2_table_cache, | ||
57 | - l2_offset, | ||
58 | - (void **) table); | ||
59 | + l2_offset + slice * slice_size2, | ||
60 | + (void **) &l2_slice); | ||
61 | if (ret < 0) { | ||
62 | goto fail; | ||
63 | } | ||
64 | |||
65 | - l2_table = *table; | ||
66 | - | ||
67 | if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { | ||
68 | - /* if there was no old l2 table, clear the new table */ | ||
69 | - memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||
70 | + /* if there was no old l2 table, clear the new slice */ | ||
71 | + memset(l2_slice, 0, slice_size2); | ||
72 | } else { | ||
73 | - uint64_t *old_table; | ||
74 | + uint64_t *old_slice; | ||
75 | + uint64_t old_l2_slice_offset = | ||
76 | + (old_l2_offset & L1E_OFFSET_MASK) + slice * slice_size2; | ||
77 | |||
78 | - /* if there was an old l2 table, read it from the disk */ | ||
79 | + /* if there was an old l2 table, read a slice from the disk */ | ||
80 | BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); | ||
81 | - ret = qcow2_cache_get(bs, s->l2_table_cache, | ||
82 | - old_l2_offset & L1E_OFFSET_MASK, | ||
83 | - (void **) &old_table); | ||
84 | + ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_slice_offset, | ||
85 | + (void **) &old_slice); | ||
86 | if (ret < 0) { | ||
87 | goto fail; | ||
88 | } | ||
89 | |||
90 | - memcpy(l2_table, old_table, s->cluster_size); | ||
91 | + memcpy(l2_slice, old_slice, slice_size2); | ||
92 | |||
93 | - qcow2_cache_put(s->l2_table_cache, (void **) &old_table); | ||
94 | + qcow2_cache_put(s->l2_table_cache, (void **) &old_slice); | ||
95 | } | ||
96 | |||
97 | - /* write the l2 table to the file */ | ||
98 | + /* write the l2 slice to the file */ | ||
99 | BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); | ||
100 | |||
101 | trace_qcow2_l2_allocate_write_l2(bs, l1_index); | ||
102 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
103 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | ||
104 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
105 | } | ||
106 | |||
107 | ret = qcow2_cache_flush(bs, s->l2_table_cache); | ||
108 | @@ -XXX,XX +XXX,XX @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||
109 | goto fail; | ||
110 | } | ||
111 | |||
112 | - *table = l2_table; | ||
113 | trace_qcow2_l2_allocate_done(bs, l1_index, 0); | ||
114 | return 0; | ||
115 | |||
116 | fail: | ||
117 | trace_qcow2_l2_allocate_done(bs, l1_index, ret); | ||
118 | - if (l2_table != NULL) { | ||
119 | - qcow2_cache_put(s->l2_table_cache, (void **) table); | ||
120 | + if (l2_slice != NULL) { | ||
121 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
122 | } | ||
123 | s->l1_table[l1_index] = old_l2_offset; | ||
124 | if (l2_offset > 0) { | ||
125 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
126 | } | ||
127 | } else { | ||
128 | /* First allocate a new L2 table (and do COW if needed) */ | ||
129 | - ret = l2_allocate(bs, l1_index, &l2_table); | ||
130 | + ret = l2_allocate(bs, l1_index); | ||
131 | if (ret < 0) { | ||
132 | return ret; | ||
133 | } | ||
134 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
135 | qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), | ||
136 | QCOW2_DISCARD_OTHER); | ||
137 | } | ||
138 | + | ||
139 | + /* Get the offset of the newly-allocated l2 table */ | ||
140 | + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; | ||
141 | + assert(offset_into_cluster(s, l2_offset) == 0); | ||
142 | + /* Load the l2 table in memory */ | ||
143 | + ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
144 | + if (ret < 0) { | ||
145 | + return ret; | ||
146 | + } | ||
147 | } | ||
148 | |||
149 | /* find the cluster offset for the given disk offset */ | ||
150 | -- | ||
151 | 2.13.6 | ||
152 | |||
153 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | After the previous patch we're now always using l2_load() in | ||
4 | get_cluster_table() regardless of whether a new L2 table has to be | ||
5 | allocated or not. | ||
6 | |||
7 | This patch refactors that part of the code to use one single l2_load() | ||
8 | call. | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-id: ce31758c4a1fadccea7a6ccb93951eb01d95fd4c.1517840877.git.berto@igalia.com | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2-cluster.c | 21 +++++++-------------- | ||
17 | 1 file changed, 7 insertions(+), 14 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/qcow2-cluster.c | ||
22 | +++ b/block/qcow2-cluster.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
24 | return -EIO; | ||
25 | } | ||
26 | |||
27 | - /* seek the l2 table of the given l2 offset */ | ||
28 | - | ||
29 | - if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) { | ||
30 | - /* load the l2 table in memory */ | ||
31 | - ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
32 | - if (ret < 0) { | ||
33 | - return ret; | ||
34 | - } | ||
35 | - } else { | ||
36 | + if (!(s->l1_table[l1_index] & QCOW_OFLAG_COPIED)) { | ||
37 | /* First allocate a new L2 table (and do COW if needed) */ | ||
38 | ret = l2_allocate(bs, l1_index); | ||
39 | if (ret < 0) { | ||
40 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
41 | /* Get the offset of the newly-allocated l2 table */ | ||
42 | l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; | ||
43 | assert(offset_into_cluster(s, l2_offset) == 0); | ||
44 | - /* Load the l2 table in memory */ | ||
45 | - ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
46 | - if (ret < 0) { | ||
47 | - return ret; | ||
48 | - } | ||
49 | + } | ||
50 | + | ||
51 | + /* load the l2 table in memory */ | ||
52 | + ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
53 | + if (ret < 0) { | ||
54 | + return ret; | ||
55 | } | ||
56 | |||
57 | /* find the cluster offset for the given disk offset */ | ||
58 | -- | ||
59 | 2.13.6 | ||
60 | |||
61 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This patch updates get_cluster_table() to return L2 slices instead of | ||
4 | full L2 tables. | ||
5 | |||
6 | The code itself needs almost no changes, it only needs to call | ||
7 | offset_to_l2_slice_index() instead of offset_to_l2_index(). This patch | ||
8 | also renames all the relevant variables and the documentation. | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-id: 64cf064c0021ba315d3f3032da0f95db1b615f33.1517840877.git.berto@igalia.com | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2-cluster.c | 16 ++++++++-------- | ||
17 | 1 file changed, 8 insertions(+), 8 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/qcow2-cluster.c | ||
22 | +++ b/block/qcow2-cluster.c | ||
23 | @@ -XXX,XX +XXX,XX @@ fail: | ||
24 | * get_cluster_table | ||
25 | * | ||
26 | * for a given disk offset, load (and allocate if needed) | ||
27 | - * the l2 table. | ||
28 | + * the appropriate slice of its l2 table. | ||
29 | * | ||
30 | - * the cluster index in the l2 table is given to the caller. | ||
31 | + * the cluster index in the l2 slice is given to the caller. | ||
32 | * | ||
33 | * Returns 0 on success, -errno in failure case | ||
34 | */ | ||
35 | static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
36 | - uint64_t **new_l2_table, | ||
37 | + uint64_t **new_l2_slice, | ||
38 | int *new_l2_index) | ||
39 | { | ||
40 | BDRVQcow2State *s = bs->opaque; | ||
41 | unsigned int l2_index; | ||
42 | uint64_t l1_index, l2_offset; | ||
43 | - uint64_t *l2_table = NULL; | ||
44 | + uint64_t *l2_slice = NULL; | ||
45 | int ret; | ||
46 | |||
47 | /* seek to the l2 offset in the l1 table */ | ||
48 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||
49 | assert(offset_into_cluster(s, l2_offset) == 0); | ||
50 | } | ||
51 | |||
52 | - /* load the l2 table in memory */ | ||
53 | - ret = l2_load(bs, offset, l2_offset, &l2_table); | ||
54 | + /* load the l2 slice in memory */ | ||
55 | + ret = l2_load(bs, offset, l2_offset, &l2_slice); | ||
56 | if (ret < 0) { | ||
57 | return ret; | ||
58 | } | ||
59 | |||
60 | /* find the cluster offset for the given disk offset */ | ||
61 | |||
62 | - l2_index = offset_to_l2_index(s, offset); | ||
63 | + l2_index = offset_to_l2_slice_index(s, offset); | ||
64 | |||
65 | - *new_l2_table = l2_table; | ||
66 | + *new_l2_slice = l2_slice; | ||
67 | *new_l2_index = l2_index; | ||
68 | |||
69 | return 0; | ||
70 | -- | ||
71 | 2.13.6 | ||
72 | |||
73 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | There's a loop in this function that iterates over the L2 entries in a | ||
4 | table, so now we need to assert that it remains within the limits of | ||
5 | an L2 slice. | ||
6 | |||
7 | Apart from that, this function doesn't need any additional changes, so | ||
8 | this patch simply updates the variable name from l2_table to l2_slice. | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-id: f9846a1c2efc51938e877e2a25852d9ab14797ff.1517840877.git.berto@igalia.com | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2-cluster.c | 16 ++++++++-------- | ||
17 | 1 file changed, 8 insertions(+), 8 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/qcow2-cluster.c | ||
22 | +++ b/block/qcow2-cluster.c | ||
23 | @@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||
24 | { | ||
25 | BDRVQcow2State *s = bs->opaque; | ||
26 | int i, j = 0, l2_index, ret; | ||
27 | - uint64_t *old_cluster, *l2_table; | ||
28 | + uint64_t *old_cluster, *l2_slice; | ||
29 | uint64_t cluster_offset = m->alloc_offset; | ||
30 | |||
31 | trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); | ||
32 | @@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||
33 | s->refcount_block_cache); | ||
34 | } | ||
35 | |||
36 | - ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index); | ||
37 | + ret = get_cluster_table(bs, m->offset, &l2_slice, &l2_index); | ||
38 | if (ret < 0) { | ||
39 | goto err; | ||
40 | } | ||
41 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
42 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | ||
43 | |||
44 | - assert(l2_index + m->nb_clusters <= s->l2_size); | ||
45 | + assert(l2_index + m->nb_clusters <= s->l2_slice_size); | ||
46 | for (i = 0; i < m->nb_clusters; i++) { | ||
47 | /* if two concurrent writes happen to the same unallocated cluster | ||
48 | * each write allocates separate cluster and writes data concurrently. | ||
49 | @@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||
50 | * cluster the second one has to do RMW (which is done above by | ||
51 | * perform_cow()), update l2 table with its cluster pointer and free | ||
52 | * old cluster. This is what this loop does */ | ||
53 | - if (l2_table[l2_index + i] != 0) { | ||
54 | - old_cluster[j++] = l2_table[l2_index + i]; | ||
55 | + if (l2_slice[l2_index + i] != 0) { | ||
56 | + old_cluster[j++] = l2_slice[l2_index + i]; | ||
57 | } | ||
58 | |||
59 | - l2_table[l2_index + i] = cpu_to_be64((cluster_offset + | ||
60 | + l2_slice[l2_index + i] = cpu_to_be64((cluster_offset + | ||
61 | (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); | ||
62 | } | ||
63 | |||
64 | |||
65 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
66 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
67 | |||
68 | /* | ||
69 | * If this was a COW, we need to decrease the refcount of the old cluster. | ||
70 | -- | ||
71 | 2.13.6 | ||
72 | |||
73 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | handle_copied() loads an L2 table and limits the number of checked | ||
4 | clusters to the amount that fits inside that table. Since we'll be | ||
5 | loading L2 slices instead of full tables we need to update that limit. | ||
6 | |||
7 | Apart from that, this function doesn't need any additional changes, so | ||
8 | this patch simply updates the variable name from l2_table to l2_slice. | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-id: 541ac001a7d6b86bab2392554bee53c2b312148c.1517840877.git.berto@igalia.com | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2-cluster.c | 16 ++++++++-------- | ||
17 | 1 file changed, 8 insertions(+), 8 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/qcow2-cluster.c | ||
22 | +++ b/block/qcow2-cluster.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||
24 | BDRVQcow2State *s = bs->opaque; | ||
25 | int l2_index; | ||
26 | uint64_t cluster_offset; | ||
27 | - uint64_t *l2_table; | ||
28 | + uint64_t *l2_slice; | ||
29 | uint64_t nb_clusters; | ||
30 | unsigned int keep_clusters; | ||
31 | int ret; | ||
32 | @@ -XXX,XX +XXX,XX @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||
33 | == offset_into_cluster(s, *host_offset)); | ||
34 | |||
35 | /* | ||
36 | - * Calculate the number of clusters to look for. We stop at L2 table | ||
37 | + * Calculate the number of clusters to look for. We stop at L2 slice | ||
38 | * boundaries to keep things simple. | ||
39 | */ | ||
40 | nb_clusters = | ||
41 | size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); | ||
42 | |||
43 | - l2_index = offset_to_l2_index(s, guest_offset); | ||
44 | - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||
45 | + l2_index = offset_to_l2_slice_index(s, guest_offset); | ||
46 | + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); | ||
47 | assert(nb_clusters <= INT_MAX); | ||
48 | |||
49 | /* Find L2 entry for the first involved cluster */ | ||
50 | - ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); | ||
51 | + ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); | ||
52 | if (ret < 0) { | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | - cluster_offset = be64_to_cpu(l2_table[l2_index]); | ||
57 | + cluster_offset = be64_to_cpu(l2_slice[l2_index]); | ||
58 | |||
59 | /* Check how many clusters are already allocated and don't need COW */ | ||
60 | if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL | ||
61 | @@ -XXX,XX +XXX,XX @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||
62 | /* We keep all QCOW_OFLAG_COPIED clusters */ | ||
63 | keep_clusters = | ||
64 | count_contiguous_clusters(nb_clusters, s->cluster_size, | ||
65 | - &l2_table[l2_index], | ||
66 | + &l2_slice[l2_index], | ||
67 | QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); | ||
68 | assert(keep_clusters <= nb_clusters); | ||
69 | |||
70 | @@ -XXX,XX +XXX,XX @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||
71 | |||
72 | /* Cleanup */ | ||
73 | out: | ||
74 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
75 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
76 | |||
77 | /* Only return a host offset if we actually made progress. Otherwise we | ||
78 | * would make requirements for handle_alloc() that it can't fulfill */ | ||
79 | -- | ||
80 | 2.13.6 | ||
81 | |||
82 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | handle_alloc() loads an L2 table and limits the number of checked | ||
4 | clusters to the amount that fits inside that table. Since we'll be | ||
5 | loading L2 slices instead of full tables we need to update that limit. | ||
6 | |||
7 | Apart from that, this function doesn't need any additional changes, so | ||
8 | this patch simply updates the variable name from l2_table to l2_slice. | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-id: b243299c7136f7014c5af51665431ddbf5e99afd.1517840877.git.berto@igalia.com | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2-cluster.c | 18 +++++++++--------- | ||
17 | 1 file changed, 9 insertions(+), 9 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/qcow2-cluster.c | ||
22 | +++ b/block/qcow2-cluster.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||
24 | { | ||
25 | BDRVQcow2State *s = bs->opaque; | ||
26 | int l2_index; | ||
27 | - uint64_t *l2_table; | ||
28 | + uint64_t *l2_slice; | ||
29 | uint64_t entry; | ||
30 | uint64_t nb_clusters; | ||
31 | int ret; | ||
32 | @@ -XXX,XX +XXX,XX @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||
33 | assert(*bytes > 0); | ||
34 | |||
35 | /* | ||
36 | - * Calculate the number of clusters to look for. We stop at L2 table | ||
37 | + * Calculate the number of clusters to look for. We stop at L2 slice | ||
38 | * boundaries to keep things simple. | ||
39 | */ | ||
40 | nb_clusters = | ||
41 | size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); | ||
42 | |||
43 | - l2_index = offset_to_l2_index(s, guest_offset); | ||
44 | - nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||
45 | + l2_index = offset_to_l2_slice_index(s, guest_offset); | ||
46 | + nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); | ||
47 | assert(nb_clusters <= INT_MAX); | ||
48 | |||
49 | /* Find L2 entry for the first involved cluster */ | ||
50 | - ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); | ||
51 | + ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); | ||
52 | if (ret < 0) { | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | - entry = be64_to_cpu(l2_table[l2_index]); | ||
57 | + entry = be64_to_cpu(l2_slice[l2_index]); | ||
58 | |||
59 | /* For the moment, overwrite compressed clusters one by one */ | ||
60 | if (entry & QCOW_OFLAG_COMPRESSED) { | ||
61 | nb_clusters = 1; | ||
62 | } else { | ||
63 | - nb_clusters = count_cow_clusters(s, nb_clusters, l2_table, l2_index); | ||
64 | + nb_clusters = count_cow_clusters(s, nb_clusters, l2_slice, l2_index); | ||
65 | } | ||
66 | |||
67 | /* This function is only called when there were no non-COW clusters, so if | ||
68 | @@ -XXX,XX +XXX,XX @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||
69 | * nb_clusters already to a range of COW clusters */ | ||
70 | preallocated_nb_clusters = | ||
71 | count_contiguous_clusters(nb_clusters, s->cluster_size, | ||
72 | - &l2_table[l2_index], QCOW_OFLAG_COPIED); | ||
73 | + &l2_slice[l2_index], QCOW_OFLAG_COPIED); | ||
74 | assert(preallocated_nb_clusters > 0); | ||
75 | |||
76 | nb_clusters = preallocated_nb_clusters; | ||
77 | @@ -XXX,XX +XXX,XX @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||
78 | keep_old_clusters = true; | ||
79 | } | ||
80 | |||
81 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
82 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
83 | |||
84 | if (!alloc_cluster_offset) { | ||
85 | /* Allocate, if necessary at a given offset in the image file */ | ||
86 | -- | ||
87 | 2.13.6 | ||
88 | |||
89 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | Adding support for L2 slices to qcow2_update_snapshot_refcount() needs | ||
4 | (among other things) an extra loop that iterates over all slices of | ||
5 | each L2 table. | ||
6 | |||
7 | Putting all changes in one patch would make it hard to read because | ||
8 | all semantic changes would be mixed with pure indentation changes. | ||
9 | |||
10 | To make things easier this patch simply creates a new block and | ||
11 | changes the indentation of all lines of code inside it. Thus, all | ||
12 | modifications in this patch are cosmetic. There are no semantic | ||
13 | changes and no variables are renamed yet. The next patch will take | ||
14 | care of that. | ||
15 | |||
16 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
17 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
18 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
19 | Message-id: 8ffaa5e55bd51121f80e498f4045b64902a94293.1517840877.git.berto@igalia.com | ||
20 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
21 | --- | ||
22 | block/qcow2-refcount.c | 144 +++++++++++++++++++++++++------------------------ | ||
23 | 1 file changed, 75 insertions(+), 69 deletions(-) | ||
24 | |||
25 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
26 | index XXXXXXX..XXXXXXX 100644 | ||
27 | --- a/block/qcow2-refcount.c | ||
28 | +++ b/block/qcow2-refcount.c | ||
29 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
30 | goto fail; | ||
31 | } | ||
32 | |||
33 | - ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, | ||
34 | - (void**) &l2_table); | ||
35 | - if (ret < 0) { | ||
36 | - goto fail; | ||
37 | - } | ||
38 | + { | ||
39 | + ret = qcow2_cache_get(bs, s->l2_table_cache, | ||
40 | + l2_offset, | ||
41 | + (void **) &l2_table); | ||
42 | + if (ret < 0) { | ||
43 | + goto fail; | ||
44 | + } | ||
45 | |||
46 | - for (j = 0; j < s->l2_size; j++) { | ||
47 | - uint64_t cluster_index; | ||
48 | - uint64_t offset; | ||
49 | - | ||
50 | - entry = be64_to_cpu(l2_table[j]); | ||
51 | - old_entry = entry; | ||
52 | - entry &= ~QCOW_OFLAG_COPIED; | ||
53 | - offset = entry & L2E_OFFSET_MASK; | ||
54 | - | ||
55 | - switch (qcow2_get_cluster_type(entry)) { | ||
56 | - case QCOW2_CLUSTER_COMPRESSED: | ||
57 | - nb_csectors = ((entry >> s->csize_shift) & | ||
58 | - s->csize_mask) + 1; | ||
59 | - if (addend != 0) { | ||
60 | - ret = update_refcount(bs, | ||
61 | - (entry & s->cluster_offset_mask) & ~511, | ||
62 | + for (j = 0; j < s->l2_size; j++) { | ||
63 | + uint64_t cluster_index; | ||
64 | + uint64_t offset; | ||
65 | + | ||
66 | + entry = be64_to_cpu(l2_table[j]); | ||
67 | + old_entry = entry; | ||
68 | + entry &= ~QCOW_OFLAG_COPIED; | ||
69 | + offset = entry & L2E_OFFSET_MASK; | ||
70 | + | ||
71 | + switch (qcow2_get_cluster_type(entry)) { | ||
72 | + case QCOW2_CLUSTER_COMPRESSED: | ||
73 | + nb_csectors = ((entry >> s->csize_shift) & | ||
74 | + s->csize_mask) + 1; | ||
75 | + if (addend != 0) { | ||
76 | + ret = update_refcount( | ||
77 | + bs, (entry & s->cluster_offset_mask) & ~511, | ||
78 | nb_csectors * 512, abs(addend), addend < 0, | ||
79 | QCOW2_DISCARD_SNAPSHOT); | ||
80 | - if (ret < 0) { | ||
81 | + if (ret < 0) { | ||
82 | + goto fail; | ||
83 | + } | ||
84 | + } | ||
85 | + /* compressed clusters are never modified */ | ||
86 | + refcount = 2; | ||
87 | + break; | ||
88 | + | ||
89 | + case QCOW2_CLUSTER_NORMAL: | ||
90 | + case QCOW2_CLUSTER_ZERO_ALLOC: | ||
91 | + if (offset_into_cluster(s, offset)) { | ||
92 | + qcow2_signal_corruption( | ||
93 | + bs, true, -1, -1, "Cluster " | ||
94 | + "allocation offset %#" PRIx64 | ||
95 | + " unaligned (L2 offset: %#" | ||
96 | + PRIx64 ", L2 index: %#x)", | ||
97 | + offset, l2_offset, j); | ||
98 | + ret = -EIO; | ||
99 | goto fail; | ||
100 | } | ||
101 | - } | ||
102 | - /* compressed clusters are never modified */ | ||
103 | - refcount = 2; | ||
104 | - break; | ||
105 | - | ||
106 | - case QCOW2_CLUSTER_NORMAL: | ||
107 | - case QCOW2_CLUSTER_ZERO_ALLOC: | ||
108 | - if (offset_into_cluster(s, offset)) { | ||
109 | - qcow2_signal_corruption(bs, true, -1, -1, "Cluster " | ||
110 | - "allocation offset %#" PRIx64 | ||
111 | - " unaligned (L2 offset: %#" | ||
112 | - PRIx64 ", L2 index: %#x)", | ||
113 | - offset, l2_offset, j); | ||
114 | - ret = -EIO; | ||
115 | - goto fail; | ||
116 | - } | ||
117 | |||
118 | - cluster_index = offset >> s->cluster_bits; | ||
119 | - assert(cluster_index); | ||
120 | - if (addend != 0) { | ||
121 | - ret = qcow2_update_cluster_refcount(bs, | ||
122 | - cluster_index, abs(addend), addend < 0, | ||
123 | - QCOW2_DISCARD_SNAPSHOT); | ||
124 | + cluster_index = offset >> s->cluster_bits; | ||
125 | + assert(cluster_index); | ||
126 | + if (addend != 0) { | ||
127 | + ret = qcow2_update_cluster_refcount( | ||
128 | + bs, cluster_index, abs(addend), addend < 0, | ||
129 | + QCOW2_DISCARD_SNAPSHOT); | ||
130 | + if (ret < 0) { | ||
131 | + goto fail; | ||
132 | + } | ||
133 | + } | ||
134 | + | ||
135 | + ret = qcow2_get_refcount(bs, cluster_index, &refcount); | ||
136 | if (ret < 0) { | ||
137 | goto fail; | ||
138 | } | ||
139 | - } | ||
140 | + break; | ||
141 | |||
142 | - ret = qcow2_get_refcount(bs, cluster_index, &refcount); | ||
143 | - if (ret < 0) { | ||
144 | - goto fail; | ||
145 | - } | ||
146 | - break; | ||
147 | - | ||
148 | - case QCOW2_CLUSTER_ZERO_PLAIN: | ||
149 | - case QCOW2_CLUSTER_UNALLOCATED: | ||
150 | - refcount = 0; | ||
151 | - break; | ||
152 | + case QCOW2_CLUSTER_ZERO_PLAIN: | ||
153 | + case QCOW2_CLUSTER_UNALLOCATED: | ||
154 | + refcount = 0; | ||
155 | + break; | ||
156 | |||
157 | - default: | ||
158 | - abort(); | ||
159 | - } | ||
160 | + default: | ||
161 | + abort(); | ||
162 | + } | ||
163 | |||
164 | - if (refcount == 1) { | ||
165 | - entry |= QCOW_OFLAG_COPIED; | ||
166 | - } | ||
167 | - if (entry != old_entry) { | ||
168 | - if (addend > 0) { | ||
169 | - qcow2_cache_set_dependency(bs, s->l2_table_cache, | ||
170 | - s->refcount_block_cache); | ||
171 | + if (refcount == 1) { | ||
172 | + entry |= QCOW_OFLAG_COPIED; | ||
173 | + } | ||
174 | + if (entry != old_entry) { | ||
175 | + if (addend > 0) { | ||
176 | + qcow2_cache_set_dependency(bs, s->l2_table_cache, | ||
177 | + s->refcount_block_cache); | ||
178 | + } | ||
179 | + l2_table[j] = cpu_to_be64(entry); | ||
180 | + qcow2_cache_entry_mark_dirty(s->l2_table_cache, | ||
181 | + l2_table); | ||
182 | } | ||
183 | - l2_table[j] = cpu_to_be64(entry); | ||
184 | - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||
185 | } | ||
186 | - } | ||
187 | |||
188 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
189 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
190 | + | ||
191 | + } | ||
192 | |||
193 | if (addend != 0) { | ||
194 | ret = qcow2_update_cluster_refcount(bs, l2_offset >> | ||
195 | -- | ||
196 | 2.13.6 | ||
197 | |||
198 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | qcow2_update_snapshot_refcount() increases the refcount of all | ||
4 | clusters of a given snapshot. In order to do that it needs to load all | ||
5 | its L2 tables and iterate over their entries. Since we'll be loading | ||
6 | L2 slices instead of full tables we need to add an extra loop that | ||
7 | iterates over all slices of each L2 table. | ||
8 | |||
9 | This function doesn't need any additional changes so apart from that | ||
10 | this patch simply updates the variable name from l2_table to l2_slice. | ||
11 | |||
12 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
13 | Message-id: 5f4db199b9637f4833b58487135124d70add8cf0.1517840877.git.berto@igalia.com | ||
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
15 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
16 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
17 | --- | ||
18 | block/qcow2-refcount.c | 32 ++++++++++++++++++-------------- | ||
19 | 1 file changed, 18 insertions(+), 14 deletions(-) | ||
20 | |||
21 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/block/qcow2-refcount.c | ||
24 | +++ b/block/qcow2-refcount.c | ||
25 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
26 | int64_t l1_table_offset, int l1_size, int addend) | ||
27 | { | ||
28 | BDRVQcow2State *s = bs->opaque; | ||
29 | - uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount; | ||
30 | + uint64_t *l1_table, *l2_slice, l2_offset, entry, l1_size2, refcount; | ||
31 | bool l1_allocated = false; | ||
32 | int64_t old_entry, old_l2_offset; | ||
33 | + unsigned slice, slice_size2, n_slices; | ||
34 | int i, j, l1_modified = 0, nb_csectors; | ||
35 | int ret; | ||
36 | |||
37 | assert(addend >= -1 && addend <= 1); | ||
38 | |||
39 | - l2_table = NULL; | ||
40 | + l2_slice = NULL; | ||
41 | l1_table = NULL; | ||
42 | l1_size2 = l1_size * sizeof(uint64_t); | ||
43 | + slice_size2 = s->l2_slice_size * sizeof(uint64_t); | ||
44 | + n_slices = s->cluster_size / slice_size2; | ||
45 | |||
46 | s->cache_discards = true; | ||
47 | |||
48 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
49 | goto fail; | ||
50 | } | ||
51 | |||
52 | - { | ||
53 | + for (slice = 0; slice < n_slices; slice++) { | ||
54 | ret = qcow2_cache_get(bs, s->l2_table_cache, | ||
55 | - l2_offset, | ||
56 | - (void **) &l2_table); | ||
57 | + l2_offset + slice * slice_size2, | ||
58 | + (void **) &l2_slice); | ||
59 | if (ret < 0) { | ||
60 | goto fail; | ||
61 | } | ||
62 | |||
63 | - for (j = 0; j < s->l2_size; j++) { | ||
64 | + for (j = 0; j < s->l2_slice_size; j++) { | ||
65 | uint64_t cluster_index; | ||
66 | uint64_t offset; | ||
67 | |||
68 | - entry = be64_to_cpu(l2_table[j]); | ||
69 | + entry = be64_to_cpu(l2_slice[j]); | ||
70 | old_entry = entry; | ||
71 | entry &= ~QCOW_OFLAG_COPIED; | ||
72 | offset = entry & L2E_OFFSET_MASK; | ||
73 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
74 | case QCOW2_CLUSTER_NORMAL: | ||
75 | case QCOW2_CLUSTER_ZERO_ALLOC: | ||
76 | if (offset_into_cluster(s, offset)) { | ||
77 | + /* Here l2_index means table (not slice) index */ | ||
78 | + int l2_index = slice * s->l2_slice_size + j; | ||
79 | qcow2_signal_corruption( | ||
80 | bs, true, -1, -1, "Cluster " | ||
81 | "allocation offset %#" PRIx64 | ||
82 | " unaligned (L2 offset: %#" | ||
83 | PRIx64 ", L2 index: %#x)", | ||
84 | - offset, l2_offset, j); | ||
85 | + offset, l2_offset, l2_index); | ||
86 | ret = -EIO; | ||
87 | goto fail; | ||
88 | } | ||
89 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
90 | qcow2_cache_set_dependency(bs, s->l2_table_cache, | ||
91 | s->refcount_block_cache); | ||
92 | } | ||
93 | - l2_table[j] = cpu_to_be64(entry); | ||
94 | + l2_slice[j] = cpu_to_be64(entry); | ||
95 | qcow2_cache_entry_mark_dirty(s->l2_table_cache, | ||
96 | - l2_table); | ||
97 | + l2_slice); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
102 | - | ||
103 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
104 | } | ||
105 | |||
106 | if (addend != 0) { | ||
107 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
108 | |||
109 | ret = bdrv_flush(bs); | ||
110 | fail: | ||
111 | - if (l2_table) { | ||
112 | - qcow2_cache_put(s->l2_table_cache, (void **) &l2_table); | ||
113 | + if (l2_slice) { | ||
114 | + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
115 | } | ||
116 | |||
117 | s->cache_discards = false; | ||
118 | -- | ||
119 | 2.13.6 | ||
120 | |||
121 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | At the moment it doesn't really make a difference whether we call | ||
4 | qcow2_get_refcount() before of after reading the L2 table, but if we | ||
5 | want to support L2 slices we'll need to read the refcount first. | ||
6 | |||
7 | This patch simply changes the order of those two operations to prepare | ||
8 | for that. The patch with the actual semantic changes will be easier to | ||
9 | read because of this. | ||
10 | |||
11 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
12 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
14 | Message-id: 947a91d934053a2dbfef979aeb9568f57ef57c5d.1517840877.git.berto@igalia.com | ||
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
16 | --- | ||
17 | block/qcow2-cluster.c | 12 ++++++------ | ||
18 | 1 file changed, 6 insertions(+), 6 deletions(-) | ||
19 | |||
20 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block/qcow2-cluster.c | ||
23 | +++ b/block/qcow2-cluster.c | ||
24 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
25 | goto fail; | ||
26 | } | ||
27 | |||
28 | + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, | ||
29 | + &l2_refcount); | ||
30 | + if (ret < 0) { | ||
31 | + goto fail; | ||
32 | + } | ||
33 | + | ||
34 | if (is_active_l1) { | ||
35 | /* get active L2 tables from cache */ | ||
36 | ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, | ||
37 | @@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||
38 | goto fail; | ||
39 | } | ||
40 | |||
41 | - ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, | ||
42 | - &l2_refcount); | ||
43 | - if (ret < 0) { | ||
44 | - goto fail; | ||
45 | - } | ||
46 | - | ||
47 | for (j = 0; j < s->l2_size; j++) { | ||
48 | uint64_t l2_entry = be64_to_cpu(l2_table[j]); | ||
49 | int64_t offset = l2_entry & L2E_OFFSET_MASK; | ||
50 | -- | ||
51 | 2.13.6 | ||
52 | |||
53 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | The qcow2_truncate() code is mostly independent from whether | ||
4 | we're using L2 slices or full L2 tables, but in full and | ||
5 | falloc preallocation modes new L2 tables are allocated using | ||
6 | qcow2_alloc_cluster_link_l2(). Therefore the code needs to be | ||
7 | modified to ensure that all nb_clusters that are processed in each | ||
8 | call can be allocated with just one L2 slice. | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
11 | Message-id: 1fd7d272b5e7b66254a090b74cf2bed1cc334c0e.1517840877.git.berto@igalia.com | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2.c | 6 +++--- | ||
17 | 1 file changed, 3 insertions(+), 3 deletions(-) | ||
18 | |||
19 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/qcow2.c | ||
22 | +++ b/block/qcow2.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, | ||
24 | host_offset = allocation_start; | ||
25 | guest_offset = old_length; | ||
26 | while (nb_new_data_clusters) { | ||
27 | - int64_t guest_cluster = guest_offset >> s->cluster_bits; | ||
28 | - int64_t nb_clusters = MIN(nb_new_data_clusters, | ||
29 | - s->l2_size - guest_cluster % s->l2_size); | ||
30 | + int64_t nb_clusters = MIN( | ||
31 | + nb_new_data_clusters, | ||
32 | + s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset)); | ||
33 | QCowL2Meta allocation = { | ||
34 | .offset = guest_offset, | ||
35 | .alloc_offset = host_offset, | ||
36 | -- | ||
37 | 2.13.6 | ||
38 | |||
39 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This function doesn't need any changes to support L2 slices, but since | ||
4 | it's now dealing with slices instead of full tables, the l2_table | ||
5 | variable is renamed for clarity. | ||
6 | |||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 78bcc54bc632574dd0b900a77a00a1b6ffc359e6.1517840877.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2-cluster.c | 6 +++--- | ||
14 | 1 file changed, 3 insertions(+), 3 deletions(-) | ||
15 | |||
16 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/block/qcow2-cluster.c | ||
19 | +++ b/block/qcow2-cluster.c | ||
20 | @@ -XXX,XX +XXX,XX @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||
21 | |||
22 | /* | ||
23 | * Checks how many consecutive unallocated clusters in a given L2 | ||
24 | - * table have the same cluster type. | ||
25 | + * slice have the same cluster type. | ||
26 | */ | ||
27 | static int count_contiguous_clusters_unallocated(int nb_clusters, | ||
28 | - uint64_t *l2_table, | ||
29 | + uint64_t *l2_slice, | ||
30 | QCow2ClusterType wanted_type) | ||
31 | { | ||
32 | int i; | ||
33 | @@ -XXX,XX +XXX,XX @@ static int count_contiguous_clusters_unallocated(int nb_clusters, | ||
34 | assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN || | ||
35 | wanted_type == QCOW2_CLUSTER_UNALLOCATED); | ||
36 | for (i = 0; i < nb_clusters; i++) { | ||
37 | - uint64_t entry = be64_to_cpu(l2_table[i]); | ||
38 | + uint64_t entry = be64_to_cpu(l2_slice[i]); | ||
39 | QCow2ClusterType type = qcow2_get_cluster_type(entry); | ||
40 | |||
41 | if (type != wanted_type) { | ||
42 | -- | ||
43 | 2.13.6 | ||
44 | |||
45 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | This function doesn't need any changes to support L2 slices, but since | ||
4 | it's now dealing with slices intead of full tables, the l2_table | ||
5 | variable is renamed for clarity. | ||
6 | |||
7 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 6107001fc79e6739242f1de7d191375e4f130aac.1517840877.git.berto@igalia.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | block/qcow2-cluster.c | 4 ++-- | ||
14 | 1 file changed, 2 insertions(+), 2 deletions(-) | ||
15 | |||
16 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/block/qcow2-cluster.c | ||
19 | +++ b/block/qcow2-cluster.c | ||
20 | @@ -XXX,XX +XXX,XX @@ err: | ||
21 | * which must copy from the backing file) | ||
22 | */ | ||
23 | static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, | ||
24 | - uint64_t *l2_table, int l2_index) | ||
25 | + uint64_t *l2_slice, int l2_index) | ||
26 | { | ||
27 | int i; | ||
28 | |||
29 | for (i = 0; i < nb_clusters; i++) { | ||
30 | - uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]); | ||
31 | + uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]); | ||
32 | QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); | ||
33 | |||
34 | switch(cluster_type) { | ||
35 | -- | ||
36 | 2.13.6 | ||
37 | |||
38 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
2 | 1 | ||
3 | expand_zero_clusters_in_l1() is used when downgrading qcow2 images | ||
4 | from v3 to v2 (compat=0.10). This is one of the functions that needed | ||
5 | more changes to support L2 slices, so this patch extends iotest 061 to | ||
6 | test downgrading a qcow2 image using a smaller slice size. | ||
7 | |||
8 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
10 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
11 | Message-id: 3e5662dce5e4926c8fabbad4c0b9142b2a506dd4.1517840877.git.berto@igalia.com | ||
12 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
13 | --- | ||
14 | tests/qemu-iotests/061 | 16 ++++++++++++ | ||
15 | tests/qemu-iotests/061.out | 61 ++++++++++++++++++++++++++++++++++++++++++++++ | ||
16 | 2 files changed, 77 insertions(+) | ||
17 | |||
18 | diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 | ||
19 | index XXXXXXX..XXXXXXX 100755 | ||
20 | --- a/tests/qemu-iotests/061 | ||
21 | +++ b/tests/qemu-iotests/061 | ||
22 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io | ||
23 | _check_test_img | ||
24 | |||
25 | echo | ||
26 | +echo "=== Testing version downgrade with zero expansion and 4K cache entries ===" | ||
27 | +echo | ||
28 | +IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M | ||
29 | +$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io | ||
30 | +$QEMU_IO -c "write -z 32M 128k" "$TEST_IMG" | _filter_qemu_io | ||
31 | +$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io | ||
32 | +$PYTHON qcow2.py "$TEST_IMG" dump-header | ||
33 | +$QEMU_IMG amend -o "compat=0.10" --image-opts \ | ||
34 | + driver=qcow2,file.filename=$TEST_IMG,l2-cache-entry-size=4096 | ||
35 | +$PYTHON qcow2.py "$TEST_IMG" dump-header | ||
36 | +$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io | ||
37 | +$QEMU_IO -c "read -P 0 32M 128k" "$TEST_IMG" | _filter_qemu_io | ||
38 | +$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io | ||
39 | +_check_test_img | ||
40 | + | ||
41 | +echo | ||
42 | echo "=== Testing dirty version downgrade ===" | ||
43 | echo | ||
44 | IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M | ||
45 | diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out | ||
46 | index XXXXXXX..XXXXXXX 100644 | ||
47 | --- a/tests/qemu-iotests/061.out | ||
48 | +++ b/tests/qemu-iotests/061.out | ||
49 | @@ -XXX,XX +XXX,XX @@ read 131072/131072 bytes at offset 0 | ||
50 | 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
51 | No errors were found on the image. | ||
52 | |||
53 | +=== Testing version downgrade with zero expansion and 4K cache entries === | ||
54 | + | ||
55 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 | ||
56 | +wrote 131072/131072 bytes at offset 0 | ||
57 | +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
58 | +wrote 131072/131072 bytes at offset 33554432 | ||
59 | +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
60 | +128 KiB (0x20000) bytes allocated at offset 0 bytes (0x0) | ||
61 | +31.875 MiB (0x1fe0000) bytes not allocated at offset 128 KiB (0x20000) | ||
62 | +128 KiB (0x20000) bytes allocated at offset 32 MiB (0x2000000) | ||
63 | +31.875 MiB (0x1fe0000) bytes not allocated at offset 32.125 MiB (0x2020000) | ||
64 | +magic 0x514649fb | ||
65 | +version 3 | ||
66 | +backing_file_offset 0x0 | ||
67 | +backing_file_size 0x0 | ||
68 | +cluster_bits 16 | ||
69 | +size 67108864 | ||
70 | +crypt_method 0 | ||
71 | +l1_size 1 | ||
72 | +l1_table_offset 0x30000 | ||
73 | +refcount_table_offset 0x10000 | ||
74 | +refcount_table_clusters 1 | ||
75 | +nb_snapshots 0 | ||
76 | +snapshot_offset 0x0 | ||
77 | +incompatible_features 0x0 | ||
78 | +compatible_features 0x1 | ||
79 | +autoclear_features 0x0 | ||
80 | +refcount_order 4 | ||
81 | +header_length 104 | ||
82 | + | ||
83 | +Header extension: | ||
84 | +magic 0x6803f857 | ||
85 | +length 144 | ||
86 | +data <binary> | ||
87 | + | ||
88 | +magic 0x514649fb | ||
89 | +version 2 | ||
90 | +backing_file_offset 0x0 | ||
91 | +backing_file_size 0x0 | ||
92 | +cluster_bits 16 | ||
93 | +size 67108864 | ||
94 | +crypt_method 0 | ||
95 | +l1_size 1 | ||
96 | +l1_table_offset 0x30000 | ||
97 | +refcount_table_offset 0x10000 | ||
98 | +refcount_table_clusters 1 | ||
99 | +nb_snapshots 0 | ||
100 | +snapshot_offset 0x0 | ||
101 | +incompatible_features 0x0 | ||
102 | +compatible_features 0x0 | ||
103 | +autoclear_features 0x0 | ||
104 | +refcount_order 4 | ||
105 | +header_length 72 | ||
106 | + | ||
107 | +read 131072/131072 bytes at offset 0 | ||
108 | +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
109 | +read 131072/131072 bytes at offset 33554432 | ||
110 | +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
111 | +64 MiB (0x4000000) bytes not allocated at offset 0 bytes (0x0) | ||
112 | +No errors were found on the image. | ||
113 | + | ||
114 | === Testing dirty version downgrade === | ||
115 | |||
116 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 | ||
117 | -- | ||
118 | 2.13.6 | ||
119 | |||
120 | diff view generated by jsdifflib |