1 | The following changes since commit 98bfaac788be0ca63d7d010c8d4ba100ff1d8278: | 1 | The following changes since commit b34181056c04e05db6c632063012beaee7006a37: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-09-01-v3' into staging (2017-09-04 13:28:09 +0100) | 3 | Merge remote-tracking branch 'remotes/rth/tags/pull-sh4-20180709' into staging (2018-07-09 22:44:22 +0100) |
4 | 4 | ||
5 | are available in the git repository at: | 5 | are available in the git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to 83a8c775a8bf134eb18a719322939b74a818d750: | 9 | for you to fetch changes up to cd47d792d7a27a57f4b621e2ff1ed8f4e83de1e9: |
10 | 10 | ||
11 | qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing (2017-09-06 14:40:18 +0200) | 11 | block: Use common write req handling in truncate (2018-07-10 16:46:22 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches | 14 | Block layer patches: |
15 | |||
16 | - Copy offloading fixes for when the copy increases the image size | ||
17 | - Temporary revert of the removal of deprecated -drive options | ||
18 | - Fix request serialisation in the image fleecing scenario | ||
19 | - Fix copy-on-read crash with unaligned image size | ||
20 | - Fix another drain crash | ||
15 | 21 | ||
16 | ---------------------------------------------------------------- | 22 | ---------------------------------------------------------------- |
17 | Daniel P. Berrange (1): | 23 | Ari Sundholm (2): |
18 | block: document semantics of bdrv_co_preadv|pwritev | 24 | qapi/block-core.json: Add missing documentation for blklogwrites log-append option |
25 | block/blklogwrites: Make sure the log sector size is not too small | ||
19 | 26 | ||
20 | Eric Blake (2): | 27 | Cornelia Huck (4): |
21 | qcow: Change signature of get_cluster_offset() | 28 | Revert "block: Remove dead deprecation warning code" |
22 | qcow: Check failure of bdrv_getlength() and bdrv_truncate() | 29 | Revert "block: Remove deprecated -drive option serial" |
30 | Revert "block: Remove deprecated -drive option addr" | ||
31 | Revert "block: Remove deprecated -drive geometry options" | ||
23 | 32 | ||
24 | Manos Pitsidianakis (10): | 33 | Fam Zheng (11): |
25 | block: pass bdrv_* methods to bs->file by default in block filters | 34 | iotests: 222: Don't run with luks |
26 | block: remove unused bdrv_media_changed | 35 | block: Prefix file driver trace points with "file_" |
27 | block: remove bdrv_truncate callback in blkdebug | 36 | block: Add copy offloading trace points |
28 | block: add default implementations for bdrv_co_get_block_status() | 37 | block: Use BdrvChild to discard |
29 | block: move ThrottleGroup membership to ThrottleGroupMember | 38 | block: Use uint64_t for BdrvTrackedRequest byte fields |
30 | block: add aio_context field in ThrottleGroupMember | 39 | block: Extract common write req handling |
31 | block: tidy ThrottleGroupMember initializations | 40 | block: Fix handling of image enlarging write |
32 | block: convert ThrottleGroup to object with QOM | 41 | block: Use common req handling for discard |
33 | block: add throttle block filter driver | 42 | block: Use common req handling in copy offloading |
34 | qemu-iotests: add 184 for throttle filter driver | 43 | block: Fix bdrv_co_truncate overlap check |
44 | block: Use common write req handling in truncate | ||
35 | 45 | ||
36 | Pavel Butsykin (1): | 46 | Kevin Wolf (3): |
37 | qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing | 47 | block: Poll after drain on attaching a node |
48 | test-bdrv-drain: Test bdrv_append() to drained node | ||
49 | block: Fix copy-on-read crash with partial final cluster | ||
38 | 50 | ||
39 | qapi/block-core.json | 66 +++- | 51 | Vladimir Sementsov-Ogievskiy (4): |
40 | include/block/block.h | 1 - | 52 | block/io: fix copy_range |
41 | include/block/block_int.h | 56 ++- | 53 | block: split flags in copy_range |
42 | include/block/throttle-groups.h | 52 ++- | 54 | block: add BDRV_REQ_SERIALISING flag |
43 | include/qemu/throttle-options.h | 60 +++- | 55 | block/backup: fix fleecing scheme: use serialized writes |
44 | include/qemu/throttle.h | 3 + | ||
45 | include/sysemu/block-backend.h | 20 +- | ||
46 | block.c | 35 +- | ||
47 | block/blkdebug.c | 20 +- | ||
48 | block/block-backend.c | 62 ++-- | ||
49 | block/commit.c | 12 +- | ||
50 | block/io.c | 26 ++ | ||
51 | block/mirror.c | 12 +- | ||
52 | block/qapi.c | 8 +- | ||
53 | block/qcow.c | 153 ++++---- | ||
54 | block/qcow2.c | 16 +- | ||
55 | block/raw-format.c | 6 - | ||
56 | block/throttle-groups.c | 750 ++++++++++++++++++++++++++++++---------- | ||
57 | block/throttle.c | 237 +++++++++++++ | ||
58 | blockdev.c | 4 +- | ||
59 | tests/test-throttle.c | 111 +++--- | ||
60 | util/throttle.c | 151 ++++++++ | ||
61 | block/Makefile.objs | 1 + | ||
62 | tests/qemu-iotests/184 | 205 +++++++++++ | ||
63 | tests/qemu-iotests/184.out | 302 ++++++++++++++++ | ||
64 | tests/qemu-iotests/group | 1 + | ||
65 | 26 files changed, 1917 insertions(+), 453 deletions(-) | ||
66 | create mode 100644 block/throttle.c | ||
67 | create mode 100755 tests/qemu-iotests/184 | ||
68 | create mode 100644 tests/qemu-iotests/184.out | ||
69 | 56 | ||
57 | qapi/block-core.json | 2 + | ||
58 | include/block/block.h | 41 +++++- | ||
59 | include/block/block_int.h | 21 ++- | ||
60 | include/hw/block/block.h | 1 + | ||
61 | include/sysemu/block-backend.h | 3 +- | ||
62 | include/sysemu/blockdev.h | 3 + | ||
63 | block.c | 2 +- | ||
64 | block/backup.c | 20 ++- | ||
65 | block/blkdebug.c | 2 +- | ||
66 | block/blklogwrites.c | 7 +- | ||
67 | block/blkreplay.c | 2 +- | ||
68 | block/block-backend.c | 8 +- | ||
69 | block/copy-on-read.c | 2 +- | ||
70 | block/file-posix.c | 25 ++-- | ||
71 | block/file-win32.c | 2 +- | ||
72 | block/io.c | 318 ++++++++++++++++++++++++++++------------- | ||
73 | block/iscsi.c | 12 +- | ||
74 | block/mirror.c | 2 +- | ||
75 | block/qcow2-refcount.c | 2 +- | ||
76 | block/qcow2.c | 20 +-- | ||
77 | block/raw-format.c | 26 ++-- | ||
78 | block/throttle.c | 2 +- | ||
79 | blockdev.c | 110 ++++++++++++++ | ||
80 | device-hotplug.c | 4 + | ||
81 | hw/block/block.c | 27 ++++ | ||
82 | hw/block/nvme.c | 1 + | ||
83 | hw/block/virtio-blk.c | 1 + | ||
84 | hw/ide/qdev.c | 1 + | ||
85 | hw/scsi/scsi-disk.c | 1 + | ||
86 | hw/usb/dev-storage.c | 1 + | ||
87 | qemu-img.c | 2 +- | ||
88 | tests/ahci-test.c | 6 +- | ||
89 | tests/hd-geo-test.c | 37 ++++- | ||
90 | tests/ide-test.c | 8 +- | ||
91 | tests/test-bdrv-drain.c | 43 ++++++ | ||
92 | block/trace-events | 10 +- | ||
93 | hmp-commands.hx | 1 + | ||
94 | qemu-doc.texi | 15 ++ | ||
95 | qemu-options.hx | 14 +- | ||
96 | tests/qemu-iotests/197 | 9 ++ | ||
97 | tests/qemu-iotests/197.out | 8 ++ | ||
98 | tests/qemu-iotests/222 | 2 + | ||
99 | 42 files changed, 647 insertions(+), 177 deletions(-) | ||
100 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | Commit dcf94a23b1 ('block: Don't poll in parent drain callbacks') |
---|---|---|---|
2 | removed polling in bdrv_child_cb_drained_begin() on the grounds that the | ||
3 | original bdrv_drain() already will poll and BdrvChildRole.drained_begin | ||
4 | calls must not cause graph changes (and therefore must not call | ||
5 | aio_poll() or the recursion through the graph will break. | ||
2 | 6 | ||
3 | This function is not used anywhere, so remove it. | 7 | This reasoning is correct for calls through bdrv_do_drained_begin(). |
8 | However, BdrvChildRole.drained_begin is also called when a node that is | ||
9 | already in a drained section (i.e. bdrv_do_drained_begin() has already | ||
10 | returned and therefore can't poll any more) is attached to a new parent. | ||
11 | In this case, we must explicitly poll to have all requests completed | ||
12 | before the drained new child can be attached to the parent. | ||
4 | 13 | ||
5 | Markus Armbruster adds: | 14 | In bdrv_replace_child_noperm(), we know that we're not inside the |
6 | The i82078 floppy device model used to call bdrv_media_changed() to | 15 | recursion of bdrv_do_drained_begin() because graph changes are not |
7 | implement its media change bit when backed by a host floppy. This | 16 | allowed there, and bdrv_replace_child_noperm() is a graph change. The |
8 | went away in 21fcf36 "fdc: simplify media change handling". | 17 | call of BdrvChildRole.drained_begin() must therefore be followed by a |
9 | Probably broke host floppy media change. Host floppy pass-through | 18 | BDRV_POLL_WHILE() that waits for the completion of requests. |
10 | was dropped in commit f709623. bdrv_media_changed() has never been | ||
11 | used for anything else. Remove it. | ||
12 | (Source is Message-ID: <87y3ruaypm.fsf@dusky.pond.sub.org>) | ||
13 | 19 | ||
14 | Reviewed-by: Eric Blake <eblake@redhat.com> | 20 | Reported-by: Max Reitz <mreitz@redhat.com> |
15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
16 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | --- | 22 | --- |
19 | include/block/block.h | 1 - | 23 | include/block/block.h | 8 ++++++++ |
20 | include/block/block_int.h | 1 - | 24 | include/block/block_int.h | 3 +++ |
21 | block.c | 14 -------------- | 25 | block.c | 2 +- |
22 | block/raw-format.c | 6 ------ | 26 | block/io.c | 26 ++++++++++++++++++++------ |
23 | 4 files changed, 22 deletions(-) | 27 | 4 files changed, 32 insertions(+), 7 deletions(-) |
24 | 28 | ||
25 | diff --git a/include/block/block.h b/include/block/block.h | 29 | diff --git a/include/block/block.h b/include/block/block.h |
26 | index XXXXXXX..XXXXXXX 100644 | 30 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/include/block/block.h | 31 | --- a/include/block/block.h |
28 | +++ b/include/block/block.h | 32 | +++ b/include/block/block.h |
29 | @@ -XXX,XX +XXX,XX @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, | 33 | @@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, |
30 | int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); | 34 | bool ignore_bds_parents); |
31 | bool bdrv_is_sg(BlockDriverState *bs); | 35 | |
32 | bool bdrv_is_inserted(BlockDriverState *bs); | 36 | /** |
33 | -int bdrv_media_changed(BlockDriverState *bs); | 37 | + * bdrv_parent_drained_begin_single: |
34 | void bdrv_lock_medium(BlockDriverState *bs, bool locked); | 38 | + * |
35 | void bdrv_eject(BlockDriverState *bs, bool eject_flag); | 39 | + * Begin a quiesced section for the parent of @c. If @poll is true, wait for |
36 | const char *bdrv_get_format_name(BlockDriverState *bs); | 40 | + * any pending activity to cease. |
41 | + */ | ||
42 | +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); | ||
43 | + | ||
44 | +/** | ||
45 | * bdrv_parent_drained_end: | ||
46 | * | ||
47 | * End a quiesced section of all users of @bs. This is part of | ||
37 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 48 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
38 | index XXXXXXX..XXXXXXX 100644 | 49 | index XXXXXXX..XXXXXXX 100644 |
39 | --- a/include/block/block_int.h | 50 | --- a/include/block/block_int.h |
40 | +++ b/include/block/block_int.h | 51 | +++ b/include/block/block_int.h |
41 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 52 | @@ -XXX,XX +XXX,XX @@ struct BdrvChildRole { |
42 | 53 | * requests after returning from .drained_begin() until .drained_end() is | |
43 | /* removable device specific */ | 54 | * called. |
44 | bool (*bdrv_is_inserted)(BlockDriverState *bs); | 55 | * |
45 | - int (*bdrv_media_changed)(BlockDriverState *bs); | 56 | + * These functions must not change the graph (and therefore also must not |
46 | void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); | 57 | + * call aio_poll(), which could change the graph indirectly). |
47 | void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); | 58 | + * |
48 | 59 | * Note that this can be nested. If drained_begin() was called twice, new | |
60 | * I/O is allowed only after drained_end() was called twice, too. | ||
61 | */ | ||
49 | diff --git a/block.c b/block.c | 62 | diff --git a/block.c b/block.c |
50 | index XXXXXXX..XXXXXXX 100644 | 63 | index XXXXXXX..XXXXXXX 100644 |
51 | --- a/block.c | 64 | --- a/block.c |
52 | +++ b/block.c | 65 | +++ b/block.c |
53 | @@ -XXX,XX +XXX,XX @@ bool bdrv_is_inserted(BlockDriverState *bs) | 66 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, |
67 | } | ||
68 | assert(num >= 0); | ||
69 | for (i = 0; i < num; i++) { | ||
70 | - child->role->drained_begin(child); | ||
71 | + bdrv_parent_drained_begin_single(child, true); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | diff --git a/block/io.c b/block/io.c | ||
76 | index XXXXXXX..XXXXXXX 100644 | ||
77 | --- a/block/io.c | ||
78 | +++ b/block/io.c | ||
79 | @@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, | ||
80 | if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { | ||
81 | continue; | ||
82 | } | ||
83 | - if (c->role->drained_begin) { | ||
84 | - c->role->drained_begin(c); | ||
85 | - } | ||
86 | + bdrv_parent_drained_begin_single(c, false); | ||
87 | } | ||
54 | } | 88 | } |
55 | 89 | ||
56 | /** | 90 | @@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, |
57 | - * Return whether the media changed since the last call to this | 91 | } |
58 | - * function, or -ENOTSUP if we don't know. Most drivers don't know. | ||
59 | - */ | ||
60 | -int bdrv_media_changed(BlockDriverState *bs) | ||
61 | -{ | ||
62 | - BlockDriver *drv = bs->drv; | ||
63 | - | ||
64 | - if (drv && drv->bdrv_media_changed) { | ||
65 | - return drv->bdrv_media_changed(bs); | ||
66 | - } | ||
67 | - return -ENOTSUP; | ||
68 | -} | ||
69 | - | ||
70 | -/** | ||
71 | * If eject_flag is TRUE, eject the media. Otherwise, close the tray | ||
72 | */ | ||
73 | void bdrv_eject(BlockDriverState *bs, bool eject_flag) | ||
74 | diff --git a/block/raw-format.c b/block/raw-format.c | ||
75 | index XXXXXXX..XXXXXXX 100644 | ||
76 | --- a/block/raw-format.c | ||
77 | +++ b/block/raw-format.c | ||
78 | @@ -XXX,XX +XXX,XX @@ static int raw_truncate(BlockDriverState *bs, int64_t offset, | ||
79 | return bdrv_truncate(bs->file, offset, prealloc, errp); | ||
80 | } | 92 | } |
81 | 93 | ||
82 | -static int raw_media_changed(BlockDriverState *bs) | 94 | +static bool bdrv_parent_drained_poll_single(BdrvChild *c) |
83 | -{ | 95 | +{ |
84 | - return bdrv_media_changed(bs->file->bs); | 96 | + if (c->role->drained_poll) { |
85 | -} | 97 | + return c->role->drained_poll(c); |
86 | - | 98 | + } |
87 | static void raw_eject(BlockDriverState *bs, bool eject_flag) | 99 | + return false; |
100 | +} | ||
101 | + | ||
102 | static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, | ||
103 | bool ignore_bds_parents) | ||
88 | { | 104 | { |
89 | bdrv_eject(bs->file->bs, eject_flag); | 105 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, |
90 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = { | 106 | if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { |
91 | .bdrv_refresh_limits = &raw_refresh_limits, | 107 | continue; |
92 | .bdrv_probe_blocksizes = &raw_probe_blocksizes, | 108 | } |
93 | .bdrv_probe_geometry = &raw_probe_geometry, | 109 | - if (c->role->drained_poll) { |
94 | - .bdrv_media_changed = &raw_media_changed, | 110 | - busy |= c->role->drained_poll(c); |
95 | .bdrv_eject = &raw_eject, | 111 | - } |
96 | .bdrv_lock_medium = &raw_lock_medium, | 112 | + busy |= bdrv_parent_drained_poll_single(c); |
97 | .bdrv_co_ioctl = &raw_co_ioctl, | 113 | } |
114 | |||
115 | return busy; | ||
116 | } | ||
117 | |||
118 | +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) | ||
119 | +{ | ||
120 | + if (c->role->drained_begin) { | ||
121 | + c->role->drained_begin(c); | ||
122 | + } | ||
123 | + if (poll) { | ||
124 | + BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c)); | ||
125 | + } | ||
126 | +} | ||
127 | + | ||
128 | static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) | ||
129 | { | ||
130 | dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); | ||
98 | -- | 131 | -- |
99 | 2.13.5 | 132 | 2.13.6 |
100 | 133 | ||
101 | 134 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
---|---|---|---|
2 | |||
3 | timer_cb() needs to know about the current Aio context of the throttle | ||
4 | request that is woken up. In order to make ThrottleGroupMember backend | ||
5 | agnostic, this information is stored in an aio_context field instead of | ||
6 | accessing it from BlockBackend. | ||
7 | |||
8 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 2 | --- |
13 | include/block/throttle-groups.h | 7 ++++- | 3 | tests/test-bdrv-drain.c | 43 +++++++++++++++++++++++++++++++++++++++++++ |
14 | block/block-backend.c | 15 ++++------ | 4 | 1 file changed, 43 insertions(+) |
15 | block/throttle-groups.c | 38 ++++++++++++++++--------- | ||
16 | tests/test-throttle.c | 63 +++++++++++++++++++++-------------------- | ||
17 | 4 files changed, 69 insertions(+), 54 deletions(-) | ||
18 | 5 | ||
19 | diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h | 6 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c |
20 | index XXXXXXX..XXXXXXX 100644 | 7 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/include/block/throttle-groups.h | 8 | --- a/tests/test-bdrv-drain.c |
22 | +++ b/include/block/throttle-groups.h | 9 | +++ b/tests/test-bdrv-drain.c |
23 | @@ -XXX,XX +XXX,XX @@ | 10 | @@ -XXX,XX +XXX,XX @@ static void test_detach_by_driver_cb(void) |
24 | */ | 11 | test_detach_indirect(false); |
25 | |||
26 | typedef struct ThrottleGroupMember { | ||
27 | + AioContext *aio_context; | ||
28 | /* throttled_reqs_lock protects the CoQueues for throttled requests. */ | ||
29 | CoMutex throttled_reqs_lock; | ||
30 | CoQueue throttled_reqs[2]; | ||
31 | @@ -XXX,XX +XXX,XX @@ void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg); | ||
32 | void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg); | ||
33 | |||
34 | void throttle_group_register_tgm(ThrottleGroupMember *tgm, | ||
35 | - const char *groupname); | ||
36 | + const char *groupname, | ||
37 | + AioContext *ctx); | ||
38 | void throttle_group_unregister_tgm(ThrottleGroupMember *tgm); | ||
39 | void throttle_group_restart_tgm(ThrottleGroupMember *tgm); | ||
40 | |||
41 | void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, | ||
42 | unsigned int bytes, | ||
43 | bool is_write); | ||
44 | +void throttle_group_attach_aio_context(ThrottleGroupMember *tgm, | ||
45 | + AioContext *new_context); | ||
46 | +void throttle_group_detach_aio_context(ThrottleGroupMember *tgm); | ||
47 | |||
48 | #endif | ||
49 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
50 | index XXXXXXX..XXXXXXX 100644 | ||
51 | --- a/block/block-backend.c | ||
52 | +++ b/block/block-backend.c | ||
53 | @@ -XXX,XX +XXX,XX @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||
54 | void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) | ||
55 | { | ||
56 | BlockDriverState *bs = blk_bs(blk); | ||
57 | - ThrottleTimers *tt; | ||
58 | + ThrottleGroupMember *tgm = &blk->public.throttle_group_member; | ||
59 | |||
60 | if (bs) { | ||
61 | - if (blk->public.throttle_group_member.throttle_state) { | ||
62 | - tt = &blk->public.throttle_group_member.throttle_timers; | ||
63 | - throttle_timers_detach_aio_context(tt); | ||
64 | + if (tgm->throttle_state) { | ||
65 | + throttle_group_detach_aio_context(tgm); | ||
66 | + throttle_group_attach_aio_context(tgm, new_context); | ||
67 | } | ||
68 | bdrv_set_aio_context(bs, new_context); | ||
69 | - if (blk->public.throttle_group_member.throttle_state) { | ||
70 | - tt = &blk->public.throttle_group_member.throttle_timers; | ||
71 | - throttle_timers_attach_aio_context(tt, new_context); | ||
72 | - } | ||
73 | } | ||
74 | } | 12 | } |
75 | 13 | ||
76 | @@ -XXX,XX +XXX,XX @@ void blk_io_limits_disable(BlockBackend *blk) | 14 | +static void test_append_to_drained(void) |
77 | void blk_io_limits_enable(BlockBackend *blk, const char *group) | ||
78 | { | ||
79 | assert(!blk->public.throttle_group_member.throttle_state); | ||
80 | - throttle_group_register_tgm(&blk->public.throttle_group_member, group); | ||
81 | + throttle_group_register_tgm(&blk->public.throttle_group_member, | ||
82 | + group, blk_get_aio_context(blk)); | ||
83 | } | ||
84 | |||
85 | void blk_io_limits_update_group(BlockBackend *blk, const char *group) | ||
86 | diff --git a/block/throttle-groups.c b/block/throttle-groups.c | ||
87 | index XXXXXXX..XXXXXXX 100644 | ||
88 | --- a/block/throttle-groups.c | ||
89 | +++ b/block/throttle-groups.c | ||
90 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) | ||
91 | |||
92 | static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write) | ||
93 | { | ||
94 | - BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic, | ||
95 | - throttle_group_member); | ||
96 | - BlockBackend *blk = blk_by_public(blkp); | ||
97 | Coroutine *co; | ||
98 | RestartData rd = { | ||
99 | .tgm = tgm, | ||
100 | @@ -XXX,XX +XXX,XX @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write | ||
101 | }; | ||
102 | |||
103 | co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd); | ||
104 | - aio_co_enter(blk_get_aio_context(blk), co); | ||
105 | + aio_co_enter(tgm->aio_context, co); | ||
106 | } | ||
107 | |||
108 | void throttle_group_restart_tgm(ThrottleGroupMember *tgm) | ||
109 | @@ -XXX,XX +XXX,XX @@ void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg) | ||
110 | /* ThrottleTimers callback. This wakes up a request that was waiting | ||
111 | * because it had been throttled. | ||
112 | * | ||
113 | - * @blk: the BlockBackend whose request had been throttled | ||
114 | + * @tgm: the ThrottleGroupMember whose request had been throttled | ||
115 | * @is_write: the type of operation (read/write) | ||
116 | */ | ||
117 | -static void timer_cb(BlockBackend *blk, bool is_write) | ||
118 | +static void timer_cb(ThrottleGroupMember *tgm, bool is_write) | ||
119 | { | ||
120 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
121 | - ThrottleGroupMember *tgm = &blkp->throttle_group_member; | ||
122 | ThrottleState *ts = tgm->throttle_state; | ||
123 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
124 | |||
125 | @@ -XXX,XX +XXX,XX @@ static void write_timer_cb(void *opaque) | ||
126 | * | ||
127 | * @tgm: the ThrottleGroupMember to insert | ||
128 | * @groupname: the name of the group | ||
129 | + * @ctx: the AioContext to use | ||
130 | */ | ||
131 | void throttle_group_register_tgm(ThrottleGroupMember *tgm, | ||
132 | - const char *groupname) | ||
133 | + const char *groupname, | ||
134 | + AioContext *ctx) | ||
135 | { | ||
136 | int i; | ||
137 | - BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic, | ||
138 | - throttle_group_member); | ||
139 | - BlockBackend *blk = blk_by_public(blkp); | ||
140 | ThrottleState *ts = throttle_group_incref(groupname); | ||
141 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
142 | |||
143 | tgm->throttle_state = ts; | ||
144 | + tgm->aio_context = ctx; | ||
145 | |||
146 | qemu_mutex_lock(&tg->lock); | ||
147 | /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */ | ||
148 | @@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, | ||
149 | QLIST_INSERT_HEAD(&tg->head, tgm, round_robin); | ||
150 | |||
151 | throttle_timers_init(&tgm->throttle_timers, | ||
152 | - blk_get_aio_context(blk), | ||
153 | + tgm->aio_context, | ||
154 | tg->clock_type, | ||
155 | read_timer_cb, | ||
156 | write_timer_cb, | ||
157 | - blk); | ||
158 | + tgm); | ||
159 | |||
160 | qemu_mutex_unlock(&tg->lock); | ||
161 | } | ||
162 | @@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) | ||
163 | tgm->throttle_state = NULL; | ||
164 | } | ||
165 | |||
166 | +void throttle_group_attach_aio_context(ThrottleGroupMember *tgm, | ||
167 | + AioContext *new_context) | ||
168 | +{ | 15 | +{ |
169 | + ThrottleTimers *tt = &tgm->throttle_timers; | 16 | + BlockBackend *blk; |
170 | + throttle_timers_attach_aio_context(tt, new_context); | 17 | + BlockDriverState *base, *overlay; |
171 | + tgm->aio_context = new_context; | 18 | + BDRVTestState *base_s, *overlay_s; |
19 | + | ||
20 | + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | ||
21 | + base = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); | ||
22 | + base_s = base->opaque; | ||
23 | + blk_insert_bs(blk, base, &error_abort); | ||
24 | + | ||
25 | + overlay = bdrv_new_open_driver(&bdrv_test, "overlay", BDRV_O_RDWR, | ||
26 | + &error_abort); | ||
27 | + overlay_s = overlay->opaque; | ||
28 | + | ||
29 | + do_drain_begin(BDRV_DRAIN, base); | ||
30 | + g_assert_cmpint(base->quiesce_counter, ==, 1); | ||
31 | + g_assert_cmpint(base_s->drain_count, ==, 1); | ||
32 | + g_assert_cmpint(base->in_flight, ==, 0); | ||
33 | + | ||
34 | + /* Takes ownership of overlay, so we don't have to unref it later */ | ||
35 | + bdrv_append(overlay, base, &error_abort); | ||
36 | + g_assert_cmpint(base->in_flight, ==, 0); | ||
37 | + g_assert_cmpint(overlay->in_flight, ==, 0); | ||
38 | + | ||
39 | + g_assert_cmpint(base->quiesce_counter, ==, 1); | ||
40 | + g_assert_cmpint(base_s->drain_count, ==, 1); | ||
41 | + g_assert_cmpint(overlay->quiesce_counter, ==, 1); | ||
42 | + g_assert_cmpint(overlay_s->drain_count, ==, 1); | ||
43 | + | ||
44 | + do_drain_end(BDRV_DRAIN, base); | ||
45 | + | ||
46 | + g_assert_cmpint(base->quiesce_counter, ==, 0); | ||
47 | + g_assert_cmpint(base_s->drain_count, ==, 0); | ||
48 | + g_assert_cmpint(overlay->quiesce_counter, ==, 0); | ||
49 | + g_assert_cmpint(overlay_s->drain_count, ==, 0); | ||
50 | + | ||
51 | + bdrv_unref(base); | ||
52 | + blk_unref(blk); | ||
172 | +} | 53 | +} |
173 | + | 54 | + |
174 | +void throttle_group_detach_aio_context(ThrottleGroupMember *tgm) | 55 | int main(int argc, char **argv) |
175 | +{ | 56 | { |
176 | + ThrottleTimers *tt = &tgm->throttle_timers; | 57 | int ret; |
177 | + throttle_timers_detach_aio_context(tt); | 58 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) |
178 | + tgm->aio_context = NULL; | 59 | g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); |
179 | +} | 60 | g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); |
61 | |||
62 | + g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); | ||
180 | + | 63 | + |
181 | static void throttle_groups_init(void) | 64 | ret = g_test_run(); |
182 | { | 65 | qemu_event_destroy(&done_event); |
183 | qemu_mutex_init(&throttle_groups_lock); | 66 | return ret; |
184 | diff --git a/tests/test-throttle.c b/tests/test-throttle.c | ||
185 | index XXXXXXX..XXXXXXX 100644 | ||
186 | --- a/tests/test-throttle.c | ||
187 | +++ b/tests/test-throttle.c | ||
188 | @@ -XXX,XX +XXX,XX @@ | ||
189 | static AioContext *ctx; | ||
190 | static LeakyBucket bkt; | ||
191 | static ThrottleConfig cfg; | ||
192 | +static ThrottleGroupMember tgm; | ||
193 | static ThrottleState ts; | ||
194 | -static ThrottleTimers tt; | ||
195 | +static ThrottleTimers *tt; | ||
196 | |||
197 | /* useful function */ | ||
198 | static bool double_cmp(double x, double y) | ||
199 | @@ -XXX,XX +XXX,XX @@ static void test_init(void) | ||
200 | { | ||
201 | int i; | ||
202 | |||
203 | + tt = &tgm.throttle_timers; | ||
204 | + | ||
205 | /* fill the structures with crap */ | ||
206 | memset(&ts, 1, sizeof(ts)); | ||
207 | - memset(&tt, 1, sizeof(tt)); | ||
208 | + memset(tt, 1, sizeof(*tt)); | ||
209 | |||
210 | /* init structures */ | ||
211 | throttle_init(&ts); | ||
212 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
213 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
214 | read_timer_cb, write_timer_cb, &ts); | ||
215 | |||
216 | /* check initialized fields */ | ||
217 | - g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL); | ||
218 | - g_assert(tt.timers[0]); | ||
219 | - g_assert(tt.timers[1]); | ||
220 | + g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL); | ||
221 | + g_assert(tt->timers[0]); | ||
222 | + g_assert(tt->timers[1]); | ||
223 | |||
224 | /* check other fields where cleared */ | ||
225 | g_assert(!ts.previous_leak); | ||
226 | @@ -XXX,XX +XXX,XX @@ static void test_init(void) | ||
227 | g_assert(!ts.cfg.buckets[i].level); | ||
228 | } | ||
229 | |||
230 | - throttle_timers_destroy(&tt); | ||
231 | + throttle_timers_destroy(tt); | ||
232 | } | ||
233 | |||
234 | static void test_destroy(void) | ||
235 | { | ||
236 | int i; | ||
237 | throttle_init(&ts); | ||
238 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
239 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
240 | read_timer_cb, write_timer_cb, &ts); | ||
241 | - throttle_timers_destroy(&tt); | ||
242 | + throttle_timers_destroy(tt); | ||
243 | for (i = 0; i < 2; i++) { | ||
244 | - g_assert(!tt.timers[i]); | ||
245 | + g_assert(!tt->timers[i]); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | @@ -XXX,XX +XXX,XX @@ static void test_config_functions(void) | ||
250 | orig_cfg.op_size = 1; | ||
251 | |||
252 | throttle_init(&ts); | ||
253 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
254 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
255 | read_timer_cb, write_timer_cb, &ts); | ||
256 | /* structure reset by throttle_init previous_leak should be null */ | ||
257 | g_assert(!ts.previous_leak); | ||
258 | @@ -XXX,XX +XXX,XX @@ static void test_config_functions(void) | ||
259 | /* get back the fixed configuration */ | ||
260 | throttle_get_config(&ts, &final_cfg); | ||
261 | |||
262 | - throttle_timers_destroy(&tt); | ||
263 | + throttle_timers_destroy(tt); | ||
264 | |||
265 | g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); | ||
266 | g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); | ||
267 | @@ -XXX,XX +XXX,XX @@ static void test_have_timer(void) | ||
268 | { | ||
269 | /* zero structures */ | ||
270 | memset(&ts, 0, sizeof(ts)); | ||
271 | - memset(&tt, 0, sizeof(tt)); | ||
272 | + memset(tt, 0, sizeof(*tt)); | ||
273 | |||
274 | /* no timer set should return false */ | ||
275 | - g_assert(!throttle_timers_are_initialized(&tt)); | ||
276 | + g_assert(!throttle_timers_are_initialized(tt)); | ||
277 | |||
278 | /* init structures */ | ||
279 | throttle_init(&ts); | ||
280 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
281 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
282 | read_timer_cb, write_timer_cb, &ts); | ||
283 | |||
284 | /* timer set by init should return true */ | ||
285 | - g_assert(throttle_timers_are_initialized(&tt)); | ||
286 | + g_assert(throttle_timers_are_initialized(tt)); | ||
287 | |||
288 | - throttle_timers_destroy(&tt); | ||
289 | + throttle_timers_destroy(tt); | ||
290 | } | ||
291 | |||
292 | static void test_detach_attach(void) | ||
293 | { | ||
294 | /* zero structures */ | ||
295 | memset(&ts, 0, sizeof(ts)); | ||
296 | - memset(&tt, 0, sizeof(tt)); | ||
297 | + memset(tt, 0, sizeof(*tt)); | ||
298 | |||
299 | /* init the structure */ | ||
300 | throttle_init(&ts); | ||
301 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
302 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
303 | read_timer_cb, write_timer_cb, &ts); | ||
304 | |||
305 | /* timer set by init should return true */ | ||
306 | - g_assert(throttle_timers_are_initialized(&tt)); | ||
307 | + g_assert(throttle_timers_are_initialized(tt)); | ||
308 | |||
309 | /* timer should no longer exist after detaching */ | ||
310 | - throttle_timers_detach_aio_context(&tt); | ||
311 | - g_assert(!throttle_timers_are_initialized(&tt)); | ||
312 | + throttle_timers_detach_aio_context(tt); | ||
313 | + g_assert(!throttle_timers_are_initialized(tt)); | ||
314 | |||
315 | /* timer should exist again after attaching */ | ||
316 | - throttle_timers_attach_aio_context(&tt, ctx); | ||
317 | - g_assert(throttle_timers_are_initialized(&tt)); | ||
318 | + throttle_timers_attach_aio_context(tt, ctx); | ||
319 | + g_assert(throttle_timers_are_initialized(tt)); | ||
320 | |||
321 | - throttle_timers_destroy(&tt); | ||
322 | + throttle_timers_destroy(tt); | ||
323 | } | ||
324 | |||
325 | static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ | ||
326 | @@ -XXX,XX +XXX,XX @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ | ||
327 | cfg.op_size = op_size; | ||
328 | |||
329 | throttle_init(&ts); | ||
330 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
331 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | ||
332 | read_timer_cb, write_timer_cb, &ts); | ||
333 | throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg); | ||
334 | |||
335 | @@ -XXX,XX +XXX,XX @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ | ||
336 | return false; | ||
337 | } | ||
338 | |||
339 | - throttle_timers_destroy(&tt); | ||
340 | + throttle_timers_destroy(tt); | ||
341 | |||
342 | return true; | ||
343 | } | ||
344 | @@ -XXX,XX +XXX,XX @@ static void test_groups(void) | ||
345 | g_assert(tgm2->throttle_state == NULL); | ||
346 | g_assert(tgm3->throttle_state == NULL); | ||
347 | |||
348 | - throttle_group_register_tgm(tgm1, "bar"); | ||
349 | - throttle_group_register_tgm(tgm2, "foo"); | ||
350 | - throttle_group_register_tgm(tgm3, "bar"); | ||
351 | + throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1)); | ||
352 | + throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2)); | ||
353 | + throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3)); | ||
354 | |||
355 | g_assert(tgm1->throttle_state != NULL); | ||
356 | g_assert(tgm2->throttle_state != NULL); | ||
357 | -- | 67 | -- |
358 | 2.13.5 | 68 | 2.13.6 |
359 | 69 | ||
360 | 70 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | If the virtual disk size isn't aligned to full clusters, | ||
2 | bdrv_co_do_copy_on_readv() may get pnum == 0 before having the full | ||
3 | cluster completed, which will let it run into an assertion failure: | ||
1 | 4 | ||
5 | qemu-io: block/io.c:1203: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed. | ||
6 | |||
7 | Check for EOF, assert that we read at least as much as the read request | ||
8 | originally wanted to have (which is true at EOF because otherwise | ||
9 | bdrv_check_byte_request() would already have returned an error) and | ||
10 | return success early even though we couldn't copy the full cluster. | ||
11 | |||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | block/io.c | 6 ++++++ | ||
15 | tests/qemu-iotests/197 | 9 +++++++++ | ||
16 | tests/qemu-iotests/197.out | 8 ++++++++ | ||
17 | 3 files changed, 23 insertions(+) | ||
18 | |||
19 | diff --git a/block/io.c b/block/io.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/io.c | ||
22 | +++ b/block/io.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, | ||
24 | pnum = MIN(cluster_bytes, max_transfer); | ||
25 | } | ||
26 | |||
27 | + /* Stop at EOF if the image ends in the middle of the cluster */ | ||
28 | + if (ret == 0 && pnum == 0) { | ||
29 | + assert(progress >= bytes); | ||
30 | + break; | ||
31 | + } | ||
32 | + | ||
33 | assert(skip_bytes < pnum); | ||
34 | |||
35 | if (ret <= 0) { | ||
36 | diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 | ||
37 | index XXXXXXX..XXXXXXX 100755 | ||
38 | --- a/tests/qemu-iotests/197 | ||
39 | +++ b/tests/qemu-iotests/197 | ||
40 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO -f qcow2 -c map "$TEST_WRAP" | ||
41 | _check_test_img | ||
42 | $QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" | ||
43 | |||
44 | +echo | ||
45 | +echo '=== Partial final cluster ===' | ||
46 | +echo | ||
47 | + | ||
48 | +_make_test_img 1024 | ||
49 | +$QEMU_IO -f $IMGFMT -C -c 'read 0 1024' "$TEST_IMG" | _filter_qemu_io | ||
50 | +$QEMU_IO -f $IMGFMT -c map "$TEST_IMG" | ||
51 | +_check_test_img | ||
52 | + | ||
53 | # success, all done | ||
54 | echo '*** done' | ||
55 | status=0 | ||
56 | diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out | ||
57 | index XXXXXXX..XXXXXXX 100644 | ||
58 | --- a/tests/qemu-iotests/197.out | ||
59 | +++ b/tests/qemu-iotests/197.out | ||
60 | @@ -XXX,XX +XXX,XX @@ can't open device TEST_DIR/t.wrap.qcow2: Can't use copy-on-read on read-only dev | ||
61 | 1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) | ||
62 | No errors were found on the image. | ||
63 | Images are identical. | ||
64 | + | ||
65 | +=== Partial final cluster === | ||
66 | + | ||
67 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 | ||
68 | +read 1024/1024 bytes at offset 0 | ||
69 | +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
70 | +1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) | ||
71 | +No errors were found on the image. | ||
72 | *** done | ||
73 | -- | ||
74 | 2.13.6 | ||
75 | |||
76 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fam Zheng <famz@redhat.com> | ||
1 | 2 | ||
3 | Luks needs special parameters to operate the image. Since this test is | ||
4 | focusing on image fleecing, skip skip that format. | ||
5 | |||
6 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | tests/qemu-iotests/222 | 2 ++ | ||
10 | 1 file changed, 2 insertions(+) | ||
11 | |||
12 | diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/tests/qemu-iotests/222 | ||
15 | +++ b/tests/qemu-iotests/222 | ||
16 | @@ -XXX,XX +XXX,XX @@ import iotests | ||
17 | from iotests import log, qemu_img, qemu_io, qemu_io_silent | ||
18 | |||
19 | iotests.verify_platform(['linux']) | ||
20 | +iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', | ||
21 | + 'vhdx', 'raw']) | ||
22 | |||
23 | patterns = [("0x5d", "0", "64k"), | ||
24 | ("0xd5", "1M", "64k"), | ||
25 | -- | ||
26 | 2.13.6 | ||
27 | |||
28 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Here two things are fixed: | ||
4 | |||
5 | 1. Architecture | ||
6 | |||
7 | On each recursion step, we go to the child of src or dst, only for one | ||
8 | of them. So, it's wrong to create tracked requests for both on each | ||
9 | step. It leads to tracked requests duplication. | ||
10 | |||
11 | 2. Wait for serializing requests on write path independently of | ||
12 | BDRV_REQ_NO_SERIALISING | ||
13 | |||
14 | Before commit 9ded4a01149 "backup: Use copy offloading", | ||
15 | BDRV_REQ_NO_SERIALISING was used for only one case: read in | ||
16 | copy-on-write operation during backup. Also, the flag was handled only | ||
17 | on read path (in bdrv_co_preadv and bdrv_aligned_preadv). | ||
18 | |||
19 | After 9ded4a01149, flag is used for not waiting serializing operations | ||
20 | on backup target (in same case of copy-on-write operation). This | ||
21 | behavior change is unsubstantiated and potentially dangerous, let's | ||
22 | drop it and add additional asserts and documentation. | ||
23 | |||
24 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
25 | Reviewed-by: Fam Zheng <famz@redhat.com> | ||
26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
27 | --- | ||
28 | include/block/block.h | 12 ++++++++++++ | ||
29 | block/io.c | 42 +++++++++++++++++++++++++++--------------- | ||
30 | 2 files changed, 39 insertions(+), 15 deletions(-) | ||
31 | |||
32 | diff --git a/include/block/block.h b/include/block/block.h | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/include/block/block.h | ||
35 | +++ b/include/block/block.h | ||
36 | @@ -XXX,XX +XXX,XX @@ typedef enum { | ||
37 | * opened with BDRV_O_UNMAP. | ||
38 | */ | ||
39 | BDRV_REQ_MAY_UNMAP = 0x4, | ||
40 | + | ||
41 | + /* | ||
42 | + * The BDRV_REQ_NO_SERIALISING flag is only valid for reads and means that | ||
43 | + * we don't want wait_serialising_requests() during the read operation. | ||
44 | + * | ||
45 | + * This flag is used for backup copy-on-write operations, when we need to | ||
46 | + * read old data before write (write notifier triggered). It is okay since | ||
47 | + * we already waited for other serializing requests in the initiating write | ||
48 | + * (see bdrv_aligned_pwritev), and it is necessary if the initiating write | ||
49 | + * is already serializing (without the flag, the read would deadlock | ||
50 | + * waiting for the serialising write to complete). | ||
51 | + */ | ||
52 | BDRV_REQ_NO_SERIALISING = 0x8, | ||
53 | BDRV_REQ_FUA = 0x10, | ||
54 | BDRV_REQ_WRITE_COMPRESSED = 0x20, | ||
55 | diff --git a/block/io.c b/block/io.c | ||
56 | index XXXXXXX..XXXXXXX 100644 | ||
57 | --- a/block/io.c | ||
58 | +++ b/block/io.c | ||
59 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, | ||
60 | max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), | ||
61 | align); | ||
62 | |||
63 | + /* BDRV_REQ_NO_SERIALISING is only for read operation */ | ||
64 | + assert(!(flags & BDRV_REQ_NO_SERIALISING)); | ||
65 | waited = wait_serialising_requests(req); | ||
66 | assert(!waited || !req->serialising); | ||
67 | assert(req->overlap_offset <= offset); | ||
68 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, | ||
69 | BdrvRequestFlags flags, | ||
70 | bool recurse_src) | ||
71 | { | ||
72 | - BdrvTrackedRequest src_req, dst_req; | ||
73 | + BdrvTrackedRequest req; | ||
74 | int ret; | ||
75 | |||
76 | if (!dst || !dst->bs) { | ||
77 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, | ||
78 | || src->bs->encrypted || dst->bs->encrypted) { | ||
79 | return -ENOTSUP; | ||
80 | } | ||
81 | - bdrv_inc_in_flight(src->bs); | ||
82 | - bdrv_inc_in_flight(dst->bs); | ||
83 | - tracked_request_begin(&src_req, src->bs, src_offset, | ||
84 | - bytes, BDRV_TRACKED_READ); | ||
85 | - tracked_request_begin(&dst_req, dst->bs, dst_offset, | ||
86 | - bytes, BDRV_TRACKED_WRITE); | ||
87 | |||
88 | - if (!(flags & BDRV_REQ_NO_SERIALISING)) { | ||
89 | - wait_serialising_requests(&src_req); | ||
90 | - wait_serialising_requests(&dst_req); | ||
91 | - } | ||
92 | if (recurse_src) { | ||
93 | + bdrv_inc_in_flight(src->bs); | ||
94 | + tracked_request_begin(&req, src->bs, src_offset, bytes, | ||
95 | + BDRV_TRACKED_READ); | ||
96 | + | ||
97 | + if (!(flags & BDRV_REQ_NO_SERIALISING)) { | ||
98 | + wait_serialising_requests(&req); | ||
99 | + } | ||
100 | + | ||
101 | ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, | ||
102 | src, src_offset, | ||
103 | dst, dst_offset, | ||
104 | bytes, flags); | ||
105 | + | ||
106 | + tracked_request_end(&req); | ||
107 | + bdrv_dec_in_flight(src->bs); | ||
108 | } else { | ||
109 | + bdrv_inc_in_flight(dst->bs); | ||
110 | + tracked_request_begin(&req, dst->bs, dst_offset, bytes, | ||
111 | + BDRV_TRACKED_WRITE); | ||
112 | + | ||
113 | + /* BDRV_REQ_NO_SERIALISING is only for read operation, | ||
114 | + * so we ignore it in flags. | ||
115 | + */ | ||
116 | + wait_serialising_requests(&req); | ||
117 | + | ||
118 | ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, | ||
119 | src, src_offset, | ||
120 | dst, dst_offset, | ||
121 | bytes, flags); | ||
122 | + | ||
123 | + tracked_request_end(&req); | ||
124 | + bdrv_dec_in_flight(dst->bs); | ||
125 | } | ||
126 | - tracked_request_end(&src_req); | ||
127 | - tracked_request_end(&dst_req); | ||
128 | - bdrv_dec_in_flight(src->bs); | ||
129 | - bdrv_dec_in_flight(dst->bs); | ||
130 | + | ||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | -- | ||
135 | 2.13.6 | ||
136 | |||
137 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | This commit eliminates the 1:1 relationship between BlockBackend and | 3 | Pass read flags and write flags separately. This is needed to handle |
4 | throttle group state. Users will be able to create multiple throttle | 4 | coming BDRV_REQ_NO_SERIALISING clearly in following patches. |
5 | nodes, each with its own throttle group state, in the future. The | ||
6 | throttle group state cannot be per-BlockBackend anymore, it must be | ||
7 | per-throttle node. This is done by gathering ThrottleGroup membership | ||
8 | details from BlockBackendPublic into ThrottleGroupMember and refactoring | ||
9 | existing code to use the structure. | ||
10 | 5 | ||
11 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
12 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 7 | Reviewed-by: Fam Zheng <famz@redhat.com> |
13 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | --- | 9 | --- |
16 | include/block/throttle-groups.h | 39 +++++- | 10 | include/block/block.h | 3 ++- |
17 | include/sysemu/block-backend.h | 20 +-- | 11 | include/block/block_int.h | 14 +++++++++---- |
18 | block/block-backend.c | 66 +++++---- | 12 | include/sysemu/block-backend.h | 3 ++- |
19 | block/qapi.c | 8 +- | 13 | block/backup.c | 2 +- |
20 | block/throttle-groups.c | 288 ++++++++++++++++++++-------------------- | 14 | block/block-backend.c | 5 +++-- |
21 | blockdev.c | 4 +- | 15 | block/file-posix.c | 21 +++++++++++-------- |
22 | tests/test-throttle.c | 53 ++++---- | 16 | block/io.c | 46 +++++++++++++++++++++++------------------- |
23 | 7 files changed, 252 insertions(+), 226 deletions(-) | 17 | block/iscsi.c | 9 ++++++--- |
18 | block/qcow2.c | 20 +++++++++--------- | ||
19 | block/raw-format.c | 24 ++++++++++++++-------- | ||
20 | qemu-img.c | 2 +- | ||
21 | 11 files changed, 90 insertions(+), 59 deletions(-) | ||
24 | 22 | ||
25 | diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h | 23 | diff --git a/include/block/block.h b/include/block/block.h |
26 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/include/block/throttle-groups.h | 25 | --- a/include/block/block.h |
28 | +++ b/include/block/throttle-groups.h | 26 | +++ b/include/block/block.h |
29 | @@ -XXX,XX +XXX,XX @@ | 27 | @@ -XXX,XX +XXX,XX @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host); |
30 | #include "qemu/throttle.h" | 28 | **/ |
31 | #include "block/block_int.h" | 29 | int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, |
32 | 30 | BdrvChild *dst, uint64_t dst_offset, | |
33 | -const char *throttle_group_get_name(BlockBackend *blk); | 31 | - uint64_t bytes, BdrvRequestFlags flags); |
34 | +/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup | 32 | + uint64_t bytes, BdrvRequestFlags read_flags, |
35 | + * and holds related data. | 33 | + BdrvRequestFlags write_flags); |
36 | + */ | 34 | #endif |
37 | + | 35 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
38 | +typedef struct ThrottleGroupMember { | 36 | index XXXXXXX..XXXXXXX 100644 |
39 | + /* throttled_reqs_lock protects the CoQueues for throttled requests. */ | 37 | --- a/include/block/block_int.h |
40 | + CoMutex throttled_reqs_lock; | 38 | +++ b/include/block/block_int.h |
41 | + CoQueue throttled_reqs[2]; | 39 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
42 | + | 40 | BdrvChild *dst, |
43 | + /* Nonzero if the I/O limits are currently being ignored; generally | 41 | uint64_t dst_offset, |
44 | + * it is zero. Accessed with atomic operations. | 42 | uint64_t bytes, |
45 | + */ | 43 | - BdrvRequestFlags flags); |
46 | + unsigned int io_limits_disabled; | 44 | + BdrvRequestFlags read_flags, |
47 | + | 45 | + BdrvRequestFlags write_flags); |
48 | + /* The following fields are protected by the ThrottleGroup lock. | 46 | |
49 | + * See the ThrottleGroup documentation for details. | 47 | /* Map [offset, offset + nbytes) range onto a child of bs to copy data to, |
50 | + * throttle_state tells us if I/O limits are configured. */ | 48 | * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy |
51 | + ThrottleState *throttle_state; | 49 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
52 | + ThrottleTimers throttle_timers; | 50 | BdrvChild *dst, |
53 | + unsigned pending_reqs[2]; | 51 | uint64_t dst_offset, |
54 | + QLIST_ENTRY(ThrottleGroupMember) round_robin; | 52 | uint64_t bytes, |
55 | + | 53 | - BdrvRequestFlags flags); |
56 | +} ThrottleGroupMember; | 54 | + BdrvRequestFlags read_flags, |
57 | + | 55 | + BdrvRequestFlags write_flags); |
58 | +const char *throttle_group_get_name(ThrottleGroupMember *tgm); | 56 | |
59 | 57 | /* | |
60 | ThrottleState *throttle_group_incref(const char *name); | 58 | * Building block for bdrv_block_status[_above] and |
61 | void throttle_group_unref(ThrottleState *ts); | 59 | @@ -XXX,XX +XXX,XX @@ void blockdev_close_all_bdrv_states(void); |
62 | 60 | ||
63 | -void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg); | 61 | int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, |
64 | -void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg); | 62 | BdrvChild *dst, uint64_t dst_offset, |
65 | +void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg); | 63 | - uint64_t bytes, BdrvRequestFlags flags); |
66 | +void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg); | 64 | + uint64_t bytes, |
67 | 65 | + BdrvRequestFlags read_flags, | |
68 | -void throttle_group_register_blk(BlockBackend *blk, const char *groupname); | 66 | + BdrvRequestFlags write_flags); |
69 | -void throttle_group_unregister_blk(BlockBackend *blk); | 67 | int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, |
70 | -void throttle_group_restart_blk(BlockBackend *blk); | 68 | BdrvChild *dst, uint64_t dst_offset, |
71 | +void throttle_group_register_tgm(ThrottleGroupMember *tgm, | 69 | - uint64_t bytes, BdrvRequestFlags flags); |
72 | + const char *groupname); | 70 | + uint64_t bytes, |
73 | +void throttle_group_unregister_tgm(ThrottleGroupMember *tgm); | 71 | + BdrvRequestFlags read_flags, |
74 | +void throttle_group_restart_tgm(ThrottleGroupMember *tgm); | 72 | + BdrvRequestFlags write_flags); |
75 | 73 | ||
76 | -void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk, | 74 | int refresh_total_sectors(BlockDriverState *bs, int64_t hint); |
77 | +void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, | ||
78 | unsigned int bytes, | ||
79 | bool is_write); | ||
80 | 75 | ||
81 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | 76 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h |
82 | index XXXXXXX..XXXXXXX 100644 | 77 | index XXXXXXX..XXXXXXX 100644 |
83 | --- a/include/sysemu/block-backend.h | 78 | --- a/include/sysemu/block-backend.h |
84 | +++ b/include/sysemu/block-backend.h | 79 | +++ b/include/sysemu/block-backend.h |
85 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockDevOps { | 80 | @@ -XXX,XX +XXX,XX @@ void blk_unregister_buf(BlockBackend *blk, void *host); |
86 | 81 | ||
87 | /* This struct is embedded in (the private) BlockBackend struct and contains | 82 | int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, |
88 | * fields that must be public. This is in particular for QLIST_ENTRY() and | 83 | BlockBackend *blk_out, int64_t off_out, |
89 | - * friends so that BlockBackends can be kept in lists outside block-backend.c */ | 84 | - int bytes, BdrvRequestFlags flags); |
90 | + * friends so that BlockBackends can be kept in lists outside block-backend.c | 85 | + int bytes, BdrvRequestFlags read_flags, |
91 | + * */ | 86 | + BdrvRequestFlags write_flags); |
92 | typedef struct BlockBackendPublic { | 87 | |
93 | - /* throttled_reqs_lock protects the CoQueues for throttled requests. */ | 88 | #endif |
94 | - CoMutex throttled_reqs_lock; | 89 | diff --git a/block/backup.c b/block/backup.c |
95 | - CoQueue throttled_reqs[2]; | 90 | index XXXXXXX..XXXXXXX 100644 |
96 | - | 91 | --- a/block/backup.c |
97 | - /* Nonzero if the I/O limits are currently being ignored; generally | 92 | +++ b/block/backup.c |
98 | - * it is zero. Accessed with atomic operations. | 93 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, |
99 | - */ | 94 | hbitmap_reset(job->copy_bitmap, start / job->cluster_size, |
100 | - unsigned int io_limits_disabled; | 95 | nr_clusters); |
101 | - | 96 | ret = blk_co_copy_range(blk, start, job->target, start, nbytes, |
102 | - /* The following fields are protected by the ThrottleGroup lock. | 97 | - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); |
103 | - * See the ThrottleGroup documentation for details. | 98 | + is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0, 0); |
104 | - * throttle_state tells us if I/O limits are configured. */ | 99 | if (ret < 0) { |
105 | - ThrottleState *throttle_state; | 100 | trace_backup_do_cow_copy_range_fail(job, start, ret); |
106 | - ThrottleTimers throttle_timers; | 101 | hbitmap_set(job->copy_bitmap, start / job->cluster_size, |
107 | - unsigned pending_reqs[2]; | ||
108 | - QLIST_ENTRY(BlockBackendPublic) round_robin; | ||
109 | + ThrottleGroupMember throttle_group_member; | ||
110 | } BlockBackendPublic; | ||
111 | |||
112 | BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm); | ||
113 | diff --git a/block/block-backend.c b/block/block-backend.c | 102 | diff --git a/block/block-backend.c b/block/block-backend.c |
114 | index XXXXXXX..XXXXXXX 100644 | 103 | index XXXXXXX..XXXXXXX 100644 |
115 | --- a/block/block-backend.c | 104 | --- a/block/block-backend.c |
116 | +++ b/block/block-backend.c | 105 | +++ b/block/block-backend.c |
117 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | 106 | @@ -XXX,XX +XXX,XX @@ void blk_unregister_buf(BlockBackend *blk, void *host) |
118 | blk->shared_perm = shared_perm; | 107 | |
119 | blk_set_enable_write_cache(blk, true); | 108 | int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, |
120 | 109 | BlockBackend *blk_out, int64_t off_out, | |
121 | - qemu_co_mutex_init(&blk->public.throttled_reqs_lock); | 110 | - int bytes, BdrvRequestFlags flags) |
122 | - qemu_co_queue_init(&blk->public.throttled_reqs[0]); | 111 | + int bytes, BdrvRequestFlags read_flags, |
123 | - qemu_co_queue_init(&blk->public.throttled_reqs[1]); | 112 | + BdrvRequestFlags write_flags) |
124 | + qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock); | 113 | { |
125 | + qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]); | 114 | int r; |
126 | + qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]); | 115 | r = blk_check_byte_request(blk_in, off_in, bytes); |
127 | block_acct_init(&blk->stats); | 116 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, |
128 | 117 | } | |
129 | notifier_list_init(&blk->remove_bs_notifiers); | 118 | return bdrv_co_copy_range(blk_in->root, off_in, |
130 | @@ -XXX,XX +XXX,XX @@ static void blk_delete(BlockBackend *blk) | 119 | blk_out->root, off_out, |
131 | assert(!blk->refcnt); | 120 | - bytes, flags); |
132 | assert(!blk->name); | 121 | + bytes, read_flags, write_flags); |
133 | assert(!blk->dev); | 122 | } |
134 | - if (blk->public.throttle_state) { | 123 | diff --git a/block/file-posix.c b/block/file-posix.c |
135 | + if (blk->public.throttle_group_member.throttle_state) { | 124 | index XXXXXXX..XXXXXXX 100644 |
136 | blk_io_limits_disable(blk); | 125 | --- a/block/file-posix.c |
137 | } | 126 | +++ b/block/file-posix.c |
138 | if (blk->root) { | 127 | @@ -XXX,XX +XXX,XX @@ static void raw_abort_perm_update(BlockDriverState *bs) |
139 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public) | 128 | raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); |
140 | */ | 129 | } |
141 | void blk_remove_bs(BlockBackend *blk) | 130 | |
142 | { | 131 | -static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, |
143 | + ThrottleTimers *tt; | 132 | - BdrvChild *src, uint64_t src_offset, |
144 | + | 133 | - BdrvChild *dst, uint64_t dst_offset, |
145 | notifier_list_notify(&blk->remove_bs_notifiers, blk); | 134 | - uint64_t bytes, BdrvRequestFlags flags) |
146 | - if (blk->public.throttle_state) { | 135 | +static int coroutine_fn raw_co_copy_range_from( |
147 | - throttle_timers_detach_aio_context(&blk->public.throttle_timers); | 136 | + BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, |
148 | + if (blk->public.throttle_group_member.throttle_state) { | 137 | + BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, |
149 | + tt = &blk->public.throttle_group_member.throttle_timers; | 138 | + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) |
150 | + throttle_timers_detach_aio_context(tt); | 139 | { |
151 | } | 140 | - return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); |
152 | 141 | + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, | |
153 | blk_update_root_state(blk); | 142 | + read_flags, write_flags); |
154 | @@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) | 143 | } |
155 | bdrv_ref(bs); | 144 | |
156 | 145 | static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, | |
157 | notifier_list_notify(&blk->insert_bs_notifiers, blk); | 146 | - BdrvChild *src, uint64_t src_offset, |
158 | - if (blk->public.throttle_state) { | 147 | - BdrvChild *dst, uint64_t dst_offset, |
159 | + if (blk->public.throttle_group_member.throttle_state) { | 148 | - uint64_t bytes, BdrvRequestFlags flags) |
160 | throttle_timers_attach_aio_context( | 149 | + BdrvChild *src, |
161 | - &blk->public.throttle_timers, bdrv_get_aio_context(bs)); | 150 | + uint64_t src_offset, |
162 | + &blk->public.throttle_group_member.throttle_timers, | 151 | + BdrvChild *dst, |
163 | + bdrv_get_aio_context(bs)); | 152 | + uint64_t dst_offset, |
164 | } | 153 | + uint64_t bytes, |
165 | 154 | + BdrvRequestFlags read_flags, | |
166 | return 0; | 155 | + BdrvRequestFlags write_flags) |
167 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, | 156 | { |
168 | bdrv_inc_in_flight(bs); | 157 | BDRVRawState *s = bs->opaque; |
169 | 158 | BDRVRawState *src_s; | |
170 | /* throttling disk I/O */ | 159 | diff --git a/block/io.c b/block/io.c |
171 | - if (blk->public.throttle_state) { | 160 | index XXXXXXX..XXXXXXX 100644 |
172 | - throttle_group_co_io_limits_intercept(blk, bytes, false); | 161 | --- a/block/io.c |
173 | + if (blk->public.throttle_group_member.throttle_state) { | 162 | +++ b/block/io.c |
174 | + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, | 163 | @@ -XXX,XX +XXX,XX @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) |
175 | + bytes, false); | 164 | } |
176 | } | 165 | } |
177 | 166 | ||
178 | ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); | 167 | -static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, |
179 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, | 168 | - uint64_t src_offset, |
180 | } | 169 | - BdrvChild *dst, |
181 | 170 | - uint64_t dst_offset, | |
182 | bdrv_inc_in_flight(bs); | 171 | - uint64_t bytes, |
183 | - | 172 | - BdrvRequestFlags flags, |
184 | /* throttling disk I/O */ | 173 | - bool recurse_src) |
185 | - if (blk->public.throttle_state) { | 174 | +static int coroutine_fn bdrv_co_copy_range_internal( |
186 | - throttle_group_co_io_limits_intercept(blk, bytes, true); | 175 | + BdrvChild *src, uint64_t src_offset, BdrvChild *dst, |
187 | + if (blk->public.throttle_group_member.throttle_state) { | 176 | + uint64_t dst_offset, uint64_t bytes, |
188 | + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, | 177 | + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, |
189 | + bytes, true); | 178 | + bool recurse_src) |
190 | } | 179 | { |
191 | 180 | BdrvTrackedRequest req; | |
192 | if (!blk->enable_write_cache) { | 181 | int ret; |
193 | @@ -XXX,XX +XXX,XX @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | 182 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, |
194 | void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) | 183 | if (ret) { |
195 | { | 184 | return ret; |
196 | BlockDriverState *bs = blk_bs(blk); | 185 | } |
197 | + ThrottleTimers *tt; | 186 | - if (flags & BDRV_REQ_ZERO_WRITE) { |
198 | 187 | - return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags); | |
199 | if (bs) { | 188 | + if (write_flags & BDRV_REQ_ZERO_WRITE) { |
200 | - if (blk->public.throttle_state) { | 189 | + return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); |
201 | - throttle_timers_detach_aio_context(&blk->public.throttle_timers); | 190 | } |
202 | + if (blk->public.throttle_group_member.throttle_state) { | 191 | |
203 | + tt = &blk->public.throttle_group_member.throttle_timers; | 192 | if (!src || !src->bs) { |
204 | + throttle_timers_detach_aio_context(tt); | 193 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, |
194 | tracked_request_begin(&req, src->bs, src_offset, bytes, | ||
195 | BDRV_TRACKED_READ); | ||
196 | |||
197 | - if (!(flags & BDRV_REQ_NO_SERIALISING)) { | ||
198 | + if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { | ||
199 | wait_serialising_requests(&req); | ||
205 | } | 200 | } |
206 | bdrv_set_aio_context(bs, new_context); | 201 | |
207 | - if (blk->public.throttle_state) { | 202 | ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, |
208 | - throttle_timers_attach_aio_context(&blk->public.throttle_timers, | 203 | src, src_offset, |
209 | - new_context); | 204 | dst, dst_offset, |
210 | + if (blk->public.throttle_group_member.throttle_state) { | 205 | - bytes, flags); |
211 | + tt = &blk->public.throttle_group_member.throttle_timers; | 206 | + bytes, |
212 | + throttle_timers_attach_aio_context(tt, new_context); | 207 | + read_flags, write_flags); |
208 | |||
209 | tracked_request_end(&req); | ||
210 | bdrv_dec_in_flight(src->bs); | ||
211 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, | ||
212 | tracked_request_begin(&req, dst->bs, dst_offset, bytes, | ||
213 | BDRV_TRACKED_WRITE); | ||
214 | |||
215 | - /* BDRV_REQ_NO_SERIALISING is only for read operation, | ||
216 | - * so we ignore it in flags. | ||
217 | - */ | ||
218 | + /* BDRV_REQ_NO_SERIALISING is only for read operation */ | ||
219 | + assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); | ||
220 | wait_serialising_requests(&req); | ||
221 | |||
222 | ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, | ||
223 | src, src_offset, | ||
224 | dst, dst_offset, | ||
225 | - bytes, flags); | ||
226 | + bytes, | ||
227 | + read_flags, write_flags); | ||
228 | |||
229 | tracked_request_end(&req); | ||
230 | bdrv_dec_in_flight(dst->bs); | ||
231 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src, | ||
232 | * semantics. */ | ||
233 | int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, | ||
234 | BdrvChild *dst, uint64_t dst_offset, | ||
235 | - uint64_t bytes, BdrvRequestFlags flags) | ||
236 | + uint64_t bytes, | ||
237 | + BdrvRequestFlags read_flags, | ||
238 | + BdrvRequestFlags write_flags) | ||
239 | { | ||
240 | return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, | ||
241 | - bytes, flags, true); | ||
242 | + bytes, read_flags, write_flags, true); | ||
243 | } | ||
244 | |||
245 | /* Copy range from @src to @dst. | ||
246 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, | ||
247 | * semantics. */ | ||
248 | int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, | ||
249 | BdrvChild *dst, uint64_t dst_offset, | ||
250 | - uint64_t bytes, BdrvRequestFlags flags) | ||
251 | + uint64_t bytes, | ||
252 | + BdrvRequestFlags read_flags, | ||
253 | + BdrvRequestFlags write_flags) | ||
254 | { | ||
255 | return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, | ||
256 | - bytes, flags, false); | ||
257 | + bytes, read_flags, write_flags, false); | ||
258 | } | ||
259 | |||
260 | int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, | ||
261 | BdrvChild *dst, uint64_t dst_offset, | ||
262 | - uint64_t bytes, BdrvRequestFlags flags) | ||
263 | + uint64_t bytes, BdrvRequestFlags read_flags, | ||
264 | + BdrvRequestFlags write_flags) | ||
265 | { | ||
266 | return bdrv_co_copy_range_from(src, src_offset, | ||
267 | dst, dst_offset, | ||
268 | - bytes, flags); | ||
269 | + bytes, read_flags, write_flags); | ||
270 | } | ||
271 | |||
272 | static void bdrv_parent_cb_resize(BlockDriverState *bs) | ||
273 | diff --git a/block/iscsi.c b/block/iscsi.c | ||
274 | index XXXXXXX..XXXXXXX 100644 | ||
275 | --- a/block/iscsi.c | ||
276 | +++ b/block/iscsi.c | ||
277 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, | ||
278 | BdrvChild *dst, | ||
279 | uint64_t dst_offset, | ||
280 | uint64_t bytes, | ||
281 | - BdrvRequestFlags flags) | ||
282 | + BdrvRequestFlags read_flags, | ||
283 | + BdrvRequestFlags write_flags) | ||
284 | { | ||
285 | - return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags); | ||
286 | + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, | ||
287 | + read_flags, write_flags); | ||
288 | } | ||
289 | |||
290 | static struct scsi_task *iscsi_xcopy_task(int param_len) | ||
291 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, | ||
292 | BdrvChild *dst, | ||
293 | uint64_t dst_offset, | ||
294 | uint64_t bytes, | ||
295 | - BdrvRequestFlags flags) | ||
296 | + BdrvRequestFlags read_flags, | ||
297 | + BdrvRequestFlags write_flags) | ||
298 | { | ||
299 | IscsiLun *dst_lun = dst->bs->opaque; | ||
300 | IscsiLun *src_lun; | ||
301 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
302 | index XXXXXXX..XXXXXXX 100644 | ||
303 | --- a/block/qcow2.c | ||
304 | +++ b/block/qcow2.c | ||
305 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn | ||
306 | qcow2_co_copy_range_from(BlockDriverState *bs, | ||
307 | BdrvChild *src, uint64_t src_offset, | ||
308 | BdrvChild *dst, uint64_t dst_offset, | ||
309 | - uint64_t bytes, BdrvRequestFlags flags) | ||
310 | + uint64_t bytes, BdrvRequestFlags read_flags, | ||
311 | + BdrvRequestFlags write_flags) | ||
312 | { | ||
313 | BDRVQcow2State *s = bs->opaque; | ||
314 | int ret; | ||
315 | unsigned int cur_bytes; /* number of bytes in current iteration */ | ||
316 | BdrvChild *child = NULL; | ||
317 | - BdrvRequestFlags cur_flags; | ||
318 | + BdrvRequestFlags cur_write_flags; | ||
319 | |||
320 | assert(!bs->encrypted); | ||
321 | qemu_co_mutex_lock(&s->lock); | ||
322 | @@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_from(BlockDriverState *bs, | ||
323 | uint64_t copy_offset = 0; | ||
324 | /* prepare next request */ | ||
325 | cur_bytes = MIN(bytes, INT_MAX); | ||
326 | - cur_flags = flags; | ||
327 | + cur_write_flags = write_flags; | ||
328 | |||
329 | ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); | ||
330 | if (ret < 0) { | ||
331 | @@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_from(BlockDriverState *bs, | ||
332 | if (bs->backing && bs->backing->bs) { | ||
333 | int64_t backing_length = bdrv_getlength(bs->backing->bs); | ||
334 | if (src_offset >= backing_length) { | ||
335 | - cur_flags |= BDRV_REQ_ZERO_WRITE; | ||
336 | + cur_write_flags |= BDRV_REQ_ZERO_WRITE; | ||
337 | } else { | ||
338 | child = bs->backing; | ||
339 | cur_bytes = MIN(cur_bytes, backing_length - src_offset); | ||
340 | copy_offset = src_offset; | ||
341 | } | ||
342 | } else { | ||
343 | - cur_flags |= BDRV_REQ_ZERO_WRITE; | ||
344 | + cur_write_flags |= BDRV_REQ_ZERO_WRITE; | ||
345 | } | ||
346 | break; | ||
347 | |||
348 | case QCOW2_CLUSTER_ZERO_PLAIN: | ||
349 | case QCOW2_CLUSTER_ZERO_ALLOC: | ||
350 | - cur_flags |= BDRV_REQ_ZERO_WRITE; | ||
351 | + cur_write_flags |= BDRV_REQ_ZERO_WRITE; | ||
352 | break; | ||
353 | |||
354 | case QCOW2_CLUSTER_COMPRESSED: | ||
355 | @@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_from(BlockDriverState *bs, | ||
356 | ret = bdrv_co_copy_range_from(child, | ||
357 | copy_offset, | ||
358 | dst, dst_offset, | ||
359 | - cur_bytes, cur_flags); | ||
360 | + cur_bytes, read_flags, cur_write_flags); | ||
361 | qemu_co_mutex_lock(&s->lock); | ||
362 | if (ret < 0) { | ||
363 | goto out; | ||
364 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn | ||
365 | qcow2_co_copy_range_to(BlockDriverState *bs, | ||
366 | BdrvChild *src, uint64_t src_offset, | ||
367 | BdrvChild *dst, uint64_t dst_offset, | ||
368 | - uint64_t bytes, BdrvRequestFlags flags) | ||
369 | + uint64_t bytes, BdrvRequestFlags read_flags, | ||
370 | + BdrvRequestFlags write_flags) | ||
371 | { | ||
372 | BDRVQcow2State *s = bs->opaque; | ||
373 | int offset_in_cluster; | ||
374 | @@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_to(BlockDriverState *bs, | ||
375 | ret = bdrv_co_copy_range_to(src, src_offset, | ||
376 | bs->file, | ||
377 | cluster_offset + offset_in_cluster, | ||
378 | - cur_bytes, flags); | ||
379 | + cur_bytes, read_flags, write_flags); | ||
380 | qemu_co_mutex_lock(&s->lock); | ||
381 | if (ret < 0) { | ||
382 | goto fail; | ||
383 | diff --git a/block/raw-format.c b/block/raw-format.c | ||
384 | index XXXXXXX..XXXXXXX 100644 | ||
385 | --- a/block/raw-format.c | ||
386 | +++ b/block/raw-format.c | ||
387 | @@ -XXX,XX +XXX,XX @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | ||
388 | } | ||
389 | |||
390 | static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, | ||
391 | - BdrvChild *src, uint64_t src_offset, | ||
392 | - BdrvChild *dst, uint64_t dst_offset, | ||
393 | - uint64_t bytes, BdrvRequestFlags flags) | ||
394 | + BdrvChild *src, | ||
395 | + uint64_t src_offset, | ||
396 | + BdrvChild *dst, | ||
397 | + uint64_t dst_offset, | ||
398 | + uint64_t bytes, | ||
399 | + BdrvRequestFlags read_flags, | ||
400 | + BdrvRequestFlags write_flags) | ||
401 | { | ||
402 | int ret; | ||
403 | |||
404 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, | ||
405 | return ret; | ||
406 | } | ||
407 | return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, | ||
408 | - bytes, flags); | ||
409 | + bytes, read_flags, write_flags); | ||
410 | } | ||
411 | |||
412 | static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, | ||
413 | - BdrvChild *src, uint64_t src_offset, | ||
414 | - BdrvChild *dst, uint64_t dst_offset, | ||
415 | - uint64_t bytes, BdrvRequestFlags flags) | ||
416 | + BdrvChild *src, | ||
417 | + uint64_t src_offset, | ||
418 | + BdrvChild *dst, | ||
419 | + uint64_t dst_offset, | ||
420 | + uint64_t bytes, | ||
421 | + BdrvRequestFlags read_flags, | ||
422 | + BdrvRequestFlags write_flags) | ||
423 | { | ||
424 | int ret; | ||
425 | |||
426 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, | ||
427 | return ret; | ||
428 | } | ||
429 | return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, | ||
430 | - flags); | ||
431 | + read_flags, write_flags); | ||
432 | } | ||
433 | |||
434 | BlockDriver bdrv_raw = { | ||
435 | diff --git a/qemu-img.c b/qemu-img.c | ||
436 | index XXXXXXX..XXXXXXX 100644 | ||
437 | --- a/qemu-img.c | ||
438 | +++ b/qemu-img.c | ||
439 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector | ||
440 | |||
441 | ret = blk_co_copy_range(blk, offset, s->target, | ||
442 | sector_num << BDRV_SECTOR_BITS, | ||
443 | - n << BDRV_SECTOR_BITS, 0); | ||
444 | + n << BDRV_SECTOR_BITS, 0, 0); | ||
445 | if (ret < 0) { | ||
446 | return ret; | ||
213 | } | 447 | } |
214 | } | ||
215 | } | ||
216 | @@ -XXX,XX +XXX,XX @@ int blk_commit_all(void) | ||
217 | /* throttling disk I/O limits */ | ||
218 | void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) | ||
219 | { | ||
220 | - throttle_group_config(blk, cfg); | ||
221 | + throttle_group_config(&blk->public.throttle_group_member, cfg); | ||
222 | } | ||
223 | |||
224 | void blk_io_limits_disable(BlockBackend *blk) | ||
225 | { | ||
226 | - assert(blk->public.throttle_state); | ||
227 | + assert(blk->public.throttle_group_member.throttle_state); | ||
228 | bdrv_drained_begin(blk_bs(blk)); | ||
229 | - throttle_group_unregister_blk(blk); | ||
230 | + throttle_group_unregister_tgm(&blk->public.throttle_group_member); | ||
231 | bdrv_drained_end(blk_bs(blk)); | ||
232 | } | ||
233 | |||
234 | /* should be called before blk_set_io_limits if a limit is set */ | ||
235 | void blk_io_limits_enable(BlockBackend *blk, const char *group) | ||
236 | { | ||
237 | - assert(!blk->public.throttle_state); | ||
238 | - throttle_group_register_blk(blk, group); | ||
239 | + assert(!blk->public.throttle_group_member.throttle_state); | ||
240 | + throttle_group_register_tgm(&blk->public.throttle_group_member, group); | ||
241 | } | ||
242 | |||
243 | void blk_io_limits_update_group(BlockBackend *blk, const char *group) | ||
244 | { | ||
245 | /* this BB is not part of any group */ | ||
246 | - if (!blk->public.throttle_state) { | ||
247 | + if (!blk->public.throttle_group_member.throttle_state) { | ||
248 | return; | ||
249 | } | ||
250 | |||
251 | /* this BB is a part of the same group than the one we want */ | ||
252 | - if (!g_strcmp0(throttle_group_get_name(blk), group)) { | ||
253 | + if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member), | ||
254 | + group)) { | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | @@ -XXX,XX +XXX,XX @@ static void blk_root_drained_begin(BdrvChild *child) | ||
259 | /* Note that blk->root may not be accessible here yet if we are just | ||
260 | * attaching to a BlockDriverState that is drained. Use child instead. */ | ||
261 | |||
262 | - if (atomic_fetch_inc(&blk->public.io_limits_disabled) == 0) { | ||
263 | - throttle_group_restart_blk(blk); | ||
264 | + if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) { | ||
265 | + throttle_group_restart_tgm(&blk->public.throttle_group_member); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | @@ -XXX,XX +XXX,XX @@ static void blk_root_drained_end(BdrvChild *child) | ||
270 | BlockBackend *blk = child->opaque; | ||
271 | assert(blk->quiesce_counter); | ||
272 | |||
273 | - assert(blk->public.io_limits_disabled); | ||
274 | - atomic_dec(&blk->public.io_limits_disabled); | ||
275 | + assert(blk->public.throttle_group_member.io_limits_disabled); | ||
276 | + atomic_dec(&blk->public.throttle_group_member.io_limits_disabled); | ||
277 | |||
278 | if (--blk->quiesce_counter == 0) { | ||
279 | if (blk->dev_ops && blk->dev_ops->drained_end) { | ||
280 | diff --git a/block/qapi.c b/block/qapi.c | ||
281 | index XXXXXXX..XXXXXXX 100644 | ||
282 | --- a/block/qapi.c | ||
283 | +++ b/block/qapi.c | ||
284 | @@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, | ||
285 | |||
286 | info->detect_zeroes = bs->detect_zeroes; | ||
287 | |||
288 | - if (blk && blk_get_public(blk)->throttle_state) { | ||
289 | + if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { | ||
290 | ThrottleConfig cfg; | ||
291 | + BlockBackendPublic *blkp = blk_get_public(blk); | ||
292 | |||
293 | - throttle_group_get_config(blk, &cfg); | ||
294 | + throttle_group_get_config(&blkp->throttle_group_member, &cfg); | ||
295 | |||
296 | info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; | ||
297 | info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; | ||
298 | @@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, | ||
299 | info->iops_size = cfg.op_size; | ||
300 | |||
301 | info->has_group = true; | ||
302 | - info->group = g_strdup(throttle_group_get_name(blk)); | ||
303 | + info->group = | ||
304 | + g_strdup(throttle_group_get_name(&blkp->throttle_group_member)); | ||
305 | } | ||
306 | |||
307 | info->write_threshold = bdrv_write_threshold_get(bs); | ||
308 | diff --git a/block/throttle-groups.c b/block/throttle-groups.c | ||
309 | index XXXXXXX..XXXXXXX 100644 | ||
310 | --- a/block/throttle-groups.c | ||
311 | +++ b/block/throttle-groups.c | ||
312 | @@ -XXX,XX +XXX,XX @@ | ||
313 | #include "sysemu/qtest.h" | ||
314 | |||
315 | /* The ThrottleGroup structure (with its ThrottleState) is shared | ||
316 | - * among different BlockBackends and it's independent from | ||
317 | + * among different ThrottleGroupMembers and it's independent from | ||
318 | * AioContext, so in order to use it from different threads it needs | ||
319 | * its own locking. | ||
320 | * | ||
321 | @@ -XXX,XX +XXX,XX @@ | ||
322 | * The whole ThrottleGroup structure is private and invisible to | ||
323 | * outside users, that only use it through its ThrottleState. | ||
324 | * | ||
325 | - * In addition to the ThrottleGroup structure, BlockBackendPublic has | ||
326 | + * In addition to the ThrottleGroup structure, ThrottleGroupMember has | ||
327 | * fields that need to be accessed by other members of the group and | ||
328 | * therefore also need to be protected by this lock. Once a | ||
329 | - * BlockBackend is registered in a group those fields can be accessed | ||
330 | + * ThrottleGroupMember is registered in a group those fields can be accessed | ||
331 | * by other threads any time. | ||
332 | * | ||
333 | * Again, all this is handled internally and is mostly transparent to | ||
334 | * the outside. The 'throttle_timers' field however has an additional | ||
335 | * constraint because it may be temporarily invalid (see for example | ||
336 | * blk_set_aio_context()). Therefore in this file a thread will | ||
337 | - * access some other BlockBackend's timers only after verifying that | ||
338 | - * that BlockBackend has throttled requests in the queue. | ||
339 | + * access some other ThrottleGroupMember's timers only after verifying that | ||
340 | + * that ThrottleGroupMember has throttled requests in the queue. | ||
341 | */ | ||
342 | typedef struct ThrottleGroup { | ||
343 | char *name; /* This is constant during the lifetime of the group */ | ||
344 | |||
345 | QemuMutex lock; /* This lock protects the following four fields */ | ||
346 | ThrottleState ts; | ||
347 | - QLIST_HEAD(, BlockBackendPublic) head; | ||
348 | - BlockBackend *tokens[2]; | ||
349 | + QLIST_HEAD(, ThrottleGroupMember) head; | ||
350 | + ThrottleGroupMember *tokens[2]; | ||
351 | bool any_timer_armed[2]; | ||
352 | QEMUClockType clock_type; | ||
353 | |||
354 | @@ -XXX,XX +XXX,XX @@ void throttle_group_unref(ThrottleState *ts) | ||
355 | qemu_mutex_unlock(&throttle_groups_lock); | ||
356 | } | ||
357 | |||
358 | -/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer) | ||
359 | +/* Get the name from a ThrottleGroupMember's group. The name (and the pointer) | ||
360 | * is guaranteed to remain constant during the lifetime of the group. | ||
361 | * | ||
362 | - * @blk: a BlockBackend that is member of a throttling group | ||
363 | + * @tgm: a ThrottleGroupMember | ||
364 | * @ret: the name of the group. | ||
365 | */ | ||
366 | -const char *throttle_group_get_name(BlockBackend *blk) | ||
367 | +const char *throttle_group_get_name(ThrottleGroupMember *tgm) | ||
368 | { | ||
369 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
370 | - ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); | ||
371 | + ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts); | ||
372 | return tg->name; | ||
373 | } | ||
374 | |||
375 | -/* Return the next BlockBackend in the round-robin sequence, simulating a | ||
376 | - * circular list. | ||
377 | +/* Return the next ThrottleGroupMember in the round-robin sequence, simulating | ||
378 | + * a circular list. | ||
379 | * | ||
380 | * This assumes that tg->lock is held. | ||
381 | * | ||
382 | - * @blk: the current BlockBackend | ||
383 | - * @ret: the next BlockBackend in the sequence | ||
384 | + * @tgm: the current ThrottleGroupMember | ||
385 | + * @ret: the next ThrottleGroupMember in the sequence | ||
386 | */ | ||
387 | -static BlockBackend *throttle_group_next_blk(BlockBackend *blk) | ||
388 | +static ThrottleGroupMember *throttle_group_next_tgm(ThrottleGroupMember *tgm) | ||
389 | { | ||
390 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
391 | - ThrottleState *ts = blkp->throttle_state; | ||
392 | + ThrottleState *ts = tgm->throttle_state; | ||
393 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
394 | - BlockBackendPublic *next = QLIST_NEXT(blkp, round_robin); | ||
395 | + ThrottleGroupMember *next = QLIST_NEXT(tgm, round_robin); | ||
396 | |||
397 | if (!next) { | ||
398 | next = QLIST_FIRST(&tg->head); | ||
399 | } | ||
400 | |||
401 | - return blk_by_public(next); | ||
402 | + return next; | ||
403 | } | ||
404 | |||
405 | /* | ||
406 | - * Return whether a BlockBackend has pending requests. | ||
407 | + * Return whether a ThrottleGroupMember has pending requests. | ||
408 | * | ||
409 | * This assumes that tg->lock is held. | ||
410 | * | ||
411 | - * @blk: the BlockBackend | ||
412 | - * @is_write: the type of operation (read/write) | ||
413 | - * @ret: whether the BlockBackend has pending requests. | ||
414 | + * @tgm: the ThrottleGroupMember | ||
415 | + * @is_write: the type of operation (read/write) | ||
416 | + * @ret: whether the ThrottleGroupMember has pending requests. | ||
417 | */ | ||
418 | -static inline bool blk_has_pending_reqs(BlockBackend *blk, | ||
419 | +static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm, | ||
420 | bool is_write) | ||
421 | { | ||
422 | - const BlockBackendPublic *blkp = blk_get_public(blk); | ||
423 | - return blkp->pending_reqs[is_write]; | ||
424 | + return tgm->pending_reqs[is_write]; | ||
425 | } | ||
426 | |||
427 | -/* Return the next BlockBackend in the round-robin sequence with pending I/O | ||
428 | - * requests. | ||
429 | +/* Return the next ThrottleGroupMember in the round-robin sequence with pending | ||
430 | + * I/O requests. | ||
431 | * | ||
432 | * This assumes that tg->lock is held. | ||
433 | * | ||
434 | - * @blk: the current BlockBackend | ||
435 | + * @tgm: the current ThrottleGroupMember | ||
436 | * @is_write: the type of operation (read/write) | ||
437 | - * @ret: the next BlockBackend with pending requests, or blk if there is | ||
438 | - * none. | ||
439 | + * @ret: the next ThrottleGroupMember with pending requests, or tgm if | ||
440 | + * there is none. | ||
441 | */ | ||
442 | -static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) | ||
443 | +static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, | ||
444 | + bool is_write) | ||
445 | { | ||
446 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
447 | - ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); | ||
448 | - BlockBackend *token, *start; | ||
449 | + ThrottleState *ts = tgm->throttle_state; | ||
450 | + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
451 | + ThrottleGroupMember *token, *start; | ||
452 | |||
453 | start = token = tg->tokens[is_write]; | ||
454 | |||
455 | /* get next bs round in round robin style */ | ||
456 | - token = throttle_group_next_blk(token); | ||
457 | - while (token != start && !blk_has_pending_reqs(token, is_write)) { | ||
458 | - token = throttle_group_next_blk(token); | ||
459 | + token = throttle_group_next_tgm(token); | ||
460 | + while (token != start && !tgm_has_pending_reqs(token, is_write)) { | ||
461 | + token = throttle_group_next_tgm(token); | ||
462 | } | ||
463 | |||
464 | /* If no IO are queued for scheduling on the next round robin token | ||
465 | - * then decide the token is the current bs because chances are | ||
466 | - * the current bs get the current request queued. | ||
467 | + * then decide the token is the current tgm because chances are | ||
468 | + * the current tgm got the current request queued. | ||
469 | */ | ||
470 | - if (token == start && !blk_has_pending_reqs(token, is_write)) { | ||
471 | - token = blk; | ||
472 | + if (token == start && !tgm_has_pending_reqs(token, is_write)) { | ||
473 | + token = tgm; | ||
474 | } | ||
475 | |||
476 | - /* Either we return the original BB, or one with pending requests */ | ||
477 | - assert(token == blk || blk_has_pending_reqs(token, is_write)); | ||
478 | + /* Either we return the original TGM, or one with pending requests */ | ||
479 | + assert(token == tgm || tgm_has_pending_reqs(token, is_write)); | ||
480 | |||
481 | return token; | ||
482 | } | ||
483 | |||
484 | -/* Check if the next I/O request for a BlockBackend needs to be throttled or | ||
485 | - * not. If there's no timer set in this group, set one and update the token | ||
486 | - * accordingly. | ||
487 | +/* Check if the next I/O request for a ThrottleGroupMember needs to be | ||
488 | + * throttled or not. If there's no timer set in this group, set one and update | ||
489 | + * the token accordingly. | ||
490 | * | ||
491 | * This assumes that tg->lock is held. | ||
492 | * | ||
493 | - * @blk: the current BlockBackend | ||
494 | + * @tgm: the current ThrottleGroupMember | ||
495 | * @is_write: the type of operation (read/write) | ||
496 | * @ret: whether the I/O request needs to be throttled or not | ||
497 | */ | ||
498 | -static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write) | ||
499 | +static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm, | ||
500 | + bool is_write) | ||
501 | { | ||
502 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
503 | - ThrottleState *ts = blkp->throttle_state; | ||
504 | - ThrottleTimers *tt = &blkp->throttle_timers; | ||
505 | + ThrottleState *ts = tgm->throttle_state; | ||
506 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
507 | + ThrottleTimers *tt = &tgm->throttle_timers; | ||
508 | bool must_wait; | ||
509 | |||
510 | - if (atomic_read(&blkp->io_limits_disabled)) { | ||
511 | + if (atomic_read(&tgm->io_limits_disabled)) { | ||
512 | return false; | ||
513 | } | ||
514 | |||
515 | @@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(BlockBackend *blk, bool is_write) | ||
516 | |||
517 | must_wait = throttle_schedule_timer(ts, tt, is_write); | ||
518 | |||
519 | - /* If a timer just got armed, set blk as the current token */ | ||
520 | + /* If a timer just got armed, set tgm as the current token */ | ||
521 | if (must_wait) { | ||
522 | - tg->tokens[is_write] = blk; | ||
523 | + tg->tokens[is_write] = tgm; | ||
524 | tg->any_timer_armed[is_write] = true; | ||
525 | } | ||
526 | |||
527 | return must_wait; | ||
528 | } | ||
529 | |||
530 | -/* Start the next pending I/O request for a BlockBackend. Return whether | ||
531 | +/* Start the next pending I/O request for a ThrottleGroupMember. Return whether | ||
532 | * any request was actually pending. | ||
533 | * | ||
534 | - * @blk: the current BlockBackend | ||
535 | + * @tgm: the current ThrottleGroupMember | ||
536 | * @is_write: the type of operation (read/write) | ||
537 | */ | ||
538 | -static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk, | ||
539 | +static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm, | ||
540 | bool is_write) | ||
541 | { | ||
542 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
543 | bool ret; | ||
544 | |||
545 | - qemu_co_mutex_lock(&blkp->throttled_reqs_lock); | ||
546 | - ret = qemu_co_queue_next(&blkp->throttled_reqs[is_write]); | ||
547 | - qemu_co_mutex_unlock(&blkp->throttled_reqs_lock); | ||
548 | + qemu_co_mutex_lock(&tgm->throttled_reqs_lock); | ||
549 | + ret = qemu_co_queue_next(&tgm->throttled_reqs[is_write]); | ||
550 | + qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); | ||
551 | |||
552 | return ret; | ||
553 | } | ||
554 | @@ -XXX,XX +XXX,XX @@ static bool coroutine_fn throttle_group_co_restart_queue(BlockBackend *blk, | ||
555 | * | ||
556 | * This assumes that tg->lock is held. | ||
557 | * | ||
558 | - * @blk: the current BlockBackend | ||
559 | + * @tgm: the current ThrottleGroupMember | ||
560 | * @is_write: the type of operation (read/write) | ||
561 | */ | ||
562 | -static void schedule_next_request(BlockBackend *blk, bool is_write) | ||
563 | +static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write) | ||
564 | { | ||
565 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
566 | - ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); | ||
567 | + ThrottleState *ts = tgm->throttle_state; | ||
568 | + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
569 | bool must_wait; | ||
570 | - BlockBackend *token; | ||
571 | + ThrottleGroupMember *token; | ||
572 | |||
573 | /* Check if there's any pending request to schedule next */ | ||
574 | - token = next_throttle_token(blk, is_write); | ||
575 | - if (!blk_has_pending_reqs(token, is_write)) { | ||
576 | + token = next_throttle_token(tgm, is_write); | ||
577 | + if (!tgm_has_pending_reqs(token, is_write)) { | ||
578 | return; | ||
579 | } | ||
580 | |||
581 | @@ -XXX,XX +XXX,XX @@ static void schedule_next_request(BlockBackend *blk, bool is_write) | ||
582 | |||
583 | /* If it doesn't have to wait, queue it for immediate execution */ | ||
584 | if (!must_wait) { | ||
585 | - /* Give preference to requests from the current blk */ | ||
586 | + /* Give preference to requests from the current tgm */ | ||
587 | if (qemu_in_coroutine() && | ||
588 | - throttle_group_co_restart_queue(blk, is_write)) { | ||
589 | - token = blk; | ||
590 | + throttle_group_co_restart_queue(tgm, is_write)) { | ||
591 | + token = tgm; | ||
592 | } else { | ||
593 | - ThrottleTimers *tt = &blk_get_public(token)->throttle_timers; | ||
594 | + ThrottleTimers *tt = &token->throttle_timers; | ||
595 | int64_t now = qemu_clock_get_ns(tg->clock_type); | ||
596 | timer_mod(tt->timers[is_write], now); | ||
597 | tg->any_timer_armed[is_write] = true; | ||
598 | @@ -XXX,XX +XXX,XX @@ static void schedule_next_request(BlockBackend *blk, bool is_write) | ||
599 | * if necessary, and schedule the next request using a round robin | ||
600 | * algorithm. | ||
601 | * | ||
602 | - * @blk: the current BlockBackend | ||
603 | + * @tgm: the current ThrottleGroupMember | ||
604 | * @bytes: the number of bytes for this I/O | ||
605 | * @is_write: the type of operation (read/write) | ||
606 | */ | ||
607 | -void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk, | ||
608 | +void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, | ||
609 | unsigned int bytes, | ||
610 | bool is_write) | ||
611 | { | ||
612 | bool must_wait; | ||
613 | - BlockBackend *token; | ||
614 | - | ||
615 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
616 | - ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); | ||
617 | + ThrottleGroupMember *token; | ||
618 | + ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts); | ||
619 | qemu_mutex_lock(&tg->lock); | ||
620 | |||
621 | /* First we check if this I/O has to be throttled. */ | ||
622 | - token = next_throttle_token(blk, is_write); | ||
623 | + token = next_throttle_token(tgm, is_write); | ||
624 | must_wait = throttle_group_schedule_timer(token, is_write); | ||
625 | |||
626 | /* Wait if there's a timer set or queued requests of this type */ | ||
627 | - if (must_wait || blkp->pending_reqs[is_write]) { | ||
628 | - blkp->pending_reqs[is_write]++; | ||
629 | + if (must_wait || tgm->pending_reqs[is_write]) { | ||
630 | + tgm->pending_reqs[is_write]++; | ||
631 | qemu_mutex_unlock(&tg->lock); | ||
632 | - qemu_co_mutex_lock(&blkp->throttled_reqs_lock); | ||
633 | - qemu_co_queue_wait(&blkp->throttled_reqs[is_write], | ||
634 | - &blkp->throttled_reqs_lock); | ||
635 | - qemu_co_mutex_unlock(&blkp->throttled_reqs_lock); | ||
636 | + qemu_co_mutex_lock(&tgm->throttled_reqs_lock); | ||
637 | + qemu_co_queue_wait(&tgm->throttled_reqs[is_write], | ||
638 | + &tgm->throttled_reqs_lock); | ||
639 | + qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); | ||
640 | qemu_mutex_lock(&tg->lock); | ||
641 | - blkp->pending_reqs[is_write]--; | ||
642 | + tgm->pending_reqs[is_write]--; | ||
643 | } | ||
644 | |||
645 | /* The I/O will be executed, so do the accounting */ | ||
646 | - throttle_account(blkp->throttle_state, is_write, bytes); | ||
647 | + throttle_account(tgm->throttle_state, is_write, bytes); | ||
648 | |||
649 | /* Schedule the next request */ | ||
650 | - schedule_next_request(blk, is_write); | ||
651 | + schedule_next_request(tgm, is_write); | ||
652 | |||
653 | qemu_mutex_unlock(&tg->lock); | ||
654 | } | ||
655 | |||
656 | typedef struct { | ||
657 | - BlockBackend *blk; | ||
658 | + ThrottleGroupMember *tgm; | ||
659 | bool is_write; | ||
660 | } RestartData; | ||
661 | |||
662 | static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) | ||
663 | { | ||
664 | RestartData *data = opaque; | ||
665 | - BlockBackend *blk = data->blk; | ||
666 | + ThrottleGroupMember *tgm = data->tgm; | ||
667 | + ThrottleState *ts = tgm->throttle_state; | ||
668 | + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
669 | bool is_write = data->is_write; | ||
670 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
671 | - ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); | ||
672 | bool empty_queue; | ||
673 | |||
674 | - empty_queue = !throttle_group_co_restart_queue(blk, is_write); | ||
675 | + empty_queue = !throttle_group_co_restart_queue(tgm, is_write); | ||
676 | |||
677 | /* If the request queue was empty then we have to take care of | ||
678 | * scheduling the next one */ | ||
679 | if (empty_queue) { | ||
680 | qemu_mutex_lock(&tg->lock); | ||
681 | - schedule_next_request(blk, is_write); | ||
682 | + schedule_next_request(tgm, is_write); | ||
683 | qemu_mutex_unlock(&tg->lock); | ||
684 | } | ||
685 | } | ||
686 | |||
687 | -static void throttle_group_restart_queue(BlockBackend *blk, bool is_write) | ||
688 | +static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write) | ||
689 | { | ||
690 | + BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic, | ||
691 | + throttle_group_member); | ||
692 | + BlockBackend *blk = blk_by_public(blkp); | ||
693 | Coroutine *co; | ||
694 | RestartData rd = { | ||
695 | - .blk = blk, | ||
696 | + .tgm = tgm, | ||
697 | .is_write = is_write | ||
698 | }; | ||
699 | |||
700 | @@ -XXX,XX +XXX,XX @@ static void throttle_group_restart_queue(BlockBackend *blk, bool is_write) | ||
701 | aio_co_enter(blk_get_aio_context(blk), co); | ||
702 | } | ||
703 | |||
704 | -void throttle_group_restart_blk(BlockBackend *blk) | ||
705 | +void throttle_group_restart_tgm(ThrottleGroupMember *tgm) | ||
706 | { | ||
707 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
708 | - | ||
709 | - if (blkp->throttle_state) { | ||
710 | - throttle_group_restart_queue(blk, 0); | ||
711 | - throttle_group_restart_queue(blk, 1); | ||
712 | + if (tgm->throttle_state) { | ||
713 | + throttle_group_restart_queue(tgm, 0); | ||
714 | + throttle_group_restart_queue(tgm, 1); | ||
715 | } | ||
716 | } | ||
717 | |||
718 | @@ -XXX,XX +XXX,XX @@ void throttle_group_restart_blk(BlockBackend *blk) | ||
719 | * to throttle_config(), but guarantees atomicity within the | ||
720 | * throttling group. | ||
721 | * | ||
722 | - * @blk: a BlockBackend that is a member of the group | ||
723 | + * @tgm: a ThrottleGroupMember that is a member of the group | ||
724 | * @cfg: the configuration to set | ||
725 | */ | ||
726 | -void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg) | ||
727 | +void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg) | ||
728 | { | ||
729 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
730 | - ThrottleState *ts = blkp->throttle_state; | ||
731 | + ThrottleState *ts = tgm->throttle_state; | ||
732 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
733 | qemu_mutex_lock(&tg->lock); | ||
734 | throttle_config(ts, tg->clock_type, cfg); | ||
735 | qemu_mutex_unlock(&tg->lock); | ||
736 | |||
737 | - throttle_group_restart_blk(blk); | ||
738 | + throttle_group_restart_tgm(tgm); | ||
739 | } | ||
740 | |||
741 | /* Get the throttle configuration from a particular group. Similar to | ||
742 | * throttle_get_config(), but guarantees atomicity within the | ||
743 | * throttling group. | ||
744 | * | ||
745 | - * @blk: a BlockBackend that is a member of the group | ||
746 | + * @tgm: a ThrottleGroupMember that is a member of the group | ||
747 | * @cfg: the configuration will be written here | ||
748 | */ | ||
749 | -void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg) | ||
750 | +void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg) | ||
751 | { | ||
752 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
753 | - ThrottleState *ts = blkp->throttle_state; | ||
754 | + ThrottleState *ts = tgm->throttle_state; | ||
755 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
756 | qemu_mutex_lock(&tg->lock); | ||
757 | throttle_get_config(ts, cfg); | ||
758 | @@ -XXX,XX +XXX,XX @@ void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg) | ||
759 | static void timer_cb(BlockBackend *blk, bool is_write) | ||
760 | { | ||
761 | BlockBackendPublic *blkp = blk_get_public(blk); | ||
762 | - ThrottleState *ts = blkp->throttle_state; | ||
763 | + ThrottleGroupMember *tgm = &blkp->throttle_group_member; | ||
764 | + ThrottleState *ts = tgm->throttle_state; | ||
765 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
766 | |||
767 | /* The timer has just been fired, so we can update the flag */ | ||
768 | @@ -XXX,XX +XXX,XX @@ static void timer_cb(BlockBackend *blk, bool is_write) | ||
769 | qemu_mutex_unlock(&tg->lock); | ||
770 | |||
771 | /* Run the request that was waiting for this timer */ | ||
772 | - throttle_group_restart_queue(blk, is_write); | ||
773 | + throttle_group_restart_queue(tgm, is_write); | ||
774 | } | ||
775 | |||
776 | static void read_timer_cb(void *opaque) | ||
777 | @@ -XXX,XX +XXX,XX @@ static void write_timer_cb(void *opaque) | ||
778 | timer_cb(opaque, true); | ||
779 | } | ||
780 | |||
781 | -/* Register a BlockBackend in the throttling group, also initializing its | ||
782 | - * timers and updating its throttle_state pointer to point to it. If a | ||
783 | +/* Register a ThrottleGroupMember from the throttling group, also initializing | ||
784 | + * its timers and updating its throttle_state pointer to point to it. If a | ||
785 | * throttling group with that name does not exist yet, it will be created. | ||
786 | * | ||
787 | - * @blk: the BlockBackend to insert | ||
788 | + * @tgm: the ThrottleGroupMember to insert | ||
789 | * @groupname: the name of the group | ||
790 | */ | ||
791 | -void throttle_group_register_blk(BlockBackend *blk, const char *groupname) | ||
792 | +void throttle_group_register_tgm(ThrottleGroupMember *tgm, | ||
793 | + const char *groupname) | ||
794 | { | ||
795 | int i; | ||
796 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
797 | + BlockBackendPublic *blkp = container_of(tgm, BlockBackendPublic, | ||
798 | + throttle_group_member); | ||
799 | + BlockBackend *blk = blk_by_public(blkp); | ||
800 | ThrottleState *ts = throttle_group_incref(groupname); | ||
801 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
802 | - blkp->throttle_state = ts; | ||
803 | + | ||
804 | + tgm->throttle_state = ts; | ||
805 | |||
806 | qemu_mutex_lock(&tg->lock); | ||
807 | - /* If the ThrottleGroup is new set this BlockBackend as the token */ | ||
808 | + /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */ | ||
809 | for (i = 0; i < 2; i++) { | ||
810 | if (!tg->tokens[i]) { | ||
811 | - tg->tokens[i] = blk; | ||
812 | + tg->tokens[i] = tgm; | ||
813 | } | ||
814 | } | ||
815 | |||
816 | - QLIST_INSERT_HEAD(&tg->head, blkp, round_robin); | ||
817 | + QLIST_INSERT_HEAD(&tg->head, tgm, round_robin); | ||
818 | |||
819 | - throttle_timers_init(&blkp->throttle_timers, | ||
820 | + throttle_timers_init(&tgm->throttle_timers, | ||
821 | blk_get_aio_context(blk), | ||
822 | tg->clock_type, | ||
823 | read_timer_cb, | ||
824 | @@ -XXX,XX +XXX,XX @@ void throttle_group_register_blk(BlockBackend *blk, const char *groupname) | ||
825 | qemu_mutex_unlock(&tg->lock); | ||
826 | } | ||
827 | |||
828 | -/* Unregister a BlockBackend from its group, removing it from the list, | ||
829 | +/* Unregister a ThrottleGroupMember from its group, removing it from the list, | ||
830 | * destroying the timers and setting the throttle_state pointer to NULL. | ||
831 | * | ||
832 | - * The BlockBackend must not have pending throttled requests, so the caller has | ||
833 | - * to drain them first. | ||
834 | + * The ThrottleGroupMember must not have pending throttled requests, so the | ||
835 | + * caller has to drain them first. | ||
836 | * | ||
837 | * The group will be destroyed if it's empty after this operation. | ||
838 | * | ||
839 | - * @blk: the BlockBackend to remove | ||
840 | + * @tgm the ThrottleGroupMember to remove | ||
841 | */ | ||
842 | -void throttle_group_unregister_blk(BlockBackend *blk) | ||
843 | +void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) | ||
844 | { | ||
845 | - BlockBackendPublic *blkp = blk_get_public(blk); | ||
846 | - ThrottleGroup *tg = container_of(blkp->throttle_state, ThrottleGroup, ts); | ||
847 | + ThrottleState *ts = tgm->throttle_state; | ||
848 | + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
849 | + ThrottleGroupMember *token; | ||
850 | int i; | ||
851 | |||
852 | - assert(blkp->pending_reqs[0] == 0 && blkp->pending_reqs[1] == 0); | ||
853 | - assert(qemu_co_queue_empty(&blkp->throttled_reqs[0])); | ||
854 | - assert(qemu_co_queue_empty(&blkp->throttled_reqs[1])); | ||
855 | + assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0); | ||
856 | + assert(qemu_co_queue_empty(&tgm->throttled_reqs[0])); | ||
857 | + assert(qemu_co_queue_empty(&tgm->throttled_reqs[1])); | ||
858 | |||
859 | qemu_mutex_lock(&tg->lock); | ||
860 | for (i = 0; i < 2; i++) { | ||
861 | - if (tg->tokens[i] == blk) { | ||
862 | - BlockBackend *token = throttle_group_next_blk(blk); | ||
863 | - /* Take care of the case where this is the last blk in the group */ | ||
864 | - if (token == blk) { | ||
865 | + if (tg->tokens[i] == tgm) { | ||
866 | + token = throttle_group_next_tgm(tgm); | ||
867 | + /* Take care of the case where this is the last tgm in the group */ | ||
868 | + if (token == tgm) { | ||
869 | token = NULL; | ||
870 | } | ||
871 | tg->tokens[i] = token; | ||
872 | } | ||
873 | } | ||
874 | |||
875 | - /* remove the current blk from the list */ | ||
876 | - QLIST_REMOVE(blkp, round_robin); | ||
877 | - throttle_timers_destroy(&blkp->throttle_timers); | ||
878 | + /* remove the current tgm from the list */ | ||
879 | + QLIST_REMOVE(tgm, round_robin); | ||
880 | + throttle_timers_destroy(&tgm->throttle_timers); | ||
881 | qemu_mutex_unlock(&tg->lock); | ||
882 | |||
883 | throttle_group_unref(&tg->ts); | ||
884 | - blkp->throttle_state = NULL; | ||
885 | + tgm->throttle_state = NULL; | ||
886 | } | ||
887 | |||
888 | static void throttle_groups_init(void) | ||
889 | diff --git a/blockdev.c b/blockdev.c | ||
890 | index XXXXXXX..XXXXXXX 100644 | ||
891 | --- a/blockdev.c | ||
892 | +++ b/blockdev.c | ||
893 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) | ||
894 | if (throttle_enabled(&cfg)) { | ||
895 | /* Enable I/O limits if they're not enabled yet, otherwise | ||
896 | * just update the throttling group. */ | ||
897 | - if (!blk_get_public(blk)->throttle_state) { | ||
898 | + if (!blk_get_public(blk)->throttle_group_member.throttle_state) { | ||
899 | blk_io_limits_enable(blk, | ||
900 | arg->has_group ? arg->group : | ||
901 | arg->has_device ? arg->device : | ||
902 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) | ||
903 | } | ||
904 | /* Set the new throttling configuration */ | ||
905 | blk_set_io_limits(blk, &cfg); | ||
906 | - } else if (blk_get_public(blk)->throttle_state) { | ||
907 | + } else if (blk_get_public(blk)->throttle_group_member.throttle_state) { | ||
908 | /* If all throttling settings are set to 0, disable I/O limits */ | ||
909 | blk_io_limits_disable(blk); | ||
910 | } | ||
911 | diff --git a/tests/test-throttle.c b/tests/test-throttle.c | ||
912 | index XXXXXXX..XXXXXXX 100644 | ||
913 | --- a/tests/test-throttle.c | ||
914 | +++ b/tests/test-throttle.c | ||
915 | @@ -XXX,XX +XXX,XX @@ static void test_groups(void) | ||
916 | ThrottleConfig cfg1, cfg2; | ||
917 | BlockBackend *blk1, *blk2, *blk3; | ||
918 | BlockBackendPublic *blkp1, *blkp2, *blkp3; | ||
919 | + ThrottleGroupMember *tgm1, *tgm2, *tgm3; | ||
920 | |||
921 | /* No actual I/O is performed on these devices */ | ||
922 | blk1 = blk_new(0, BLK_PERM_ALL); | ||
923 | @@ -XXX,XX +XXX,XX @@ static void test_groups(void) | ||
924 | blkp2 = blk_get_public(blk2); | ||
925 | blkp3 = blk_get_public(blk3); | ||
926 | |||
927 | - g_assert(blkp1->throttle_state == NULL); | ||
928 | - g_assert(blkp2->throttle_state == NULL); | ||
929 | - g_assert(blkp3->throttle_state == NULL); | ||
930 | + tgm1 = &blkp1->throttle_group_member; | ||
931 | + tgm2 = &blkp2->throttle_group_member; | ||
932 | + tgm3 = &blkp3->throttle_group_member; | ||
933 | |||
934 | - throttle_group_register_blk(blk1, "bar"); | ||
935 | - throttle_group_register_blk(blk2, "foo"); | ||
936 | - throttle_group_register_blk(blk3, "bar"); | ||
937 | + g_assert(tgm1->throttle_state == NULL); | ||
938 | + g_assert(tgm2->throttle_state == NULL); | ||
939 | + g_assert(tgm3->throttle_state == NULL); | ||
940 | |||
941 | - g_assert(blkp1->throttle_state != NULL); | ||
942 | - g_assert(blkp2->throttle_state != NULL); | ||
943 | - g_assert(blkp3->throttle_state != NULL); | ||
944 | + throttle_group_register_tgm(tgm1, "bar"); | ||
945 | + throttle_group_register_tgm(tgm2, "foo"); | ||
946 | + throttle_group_register_tgm(tgm3, "bar"); | ||
947 | |||
948 | - g_assert(!strcmp(throttle_group_get_name(blk1), "bar")); | ||
949 | - g_assert(!strcmp(throttle_group_get_name(blk2), "foo")); | ||
950 | - g_assert(blkp1->throttle_state == blkp3->throttle_state); | ||
951 | + g_assert(tgm1->throttle_state != NULL); | ||
952 | + g_assert(tgm2->throttle_state != NULL); | ||
953 | + g_assert(tgm3->throttle_state != NULL); | ||
954 | + | ||
955 | + g_assert(!strcmp(throttle_group_get_name(tgm1), "bar")); | ||
956 | + g_assert(!strcmp(throttle_group_get_name(tgm2), "foo")); | ||
957 | + g_assert(tgm1->throttle_state == tgm3->throttle_state); | ||
958 | |||
959 | /* Setting the config of a group member affects the whole group */ | ||
960 | throttle_config_init(&cfg1); | ||
961 | @@ -XXX,XX +XXX,XX @@ static void test_groups(void) | ||
962 | cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; | ||
963 | cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; | ||
964 | cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000; | ||
965 | - throttle_group_config(blk1, &cfg1); | ||
966 | + throttle_group_config(tgm1, &cfg1); | ||
967 | |||
968 | - throttle_group_get_config(blk1, &cfg1); | ||
969 | - throttle_group_get_config(blk3, &cfg2); | ||
970 | + throttle_group_get_config(tgm1, &cfg1); | ||
971 | + throttle_group_get_config(tgm3, &cfg2); | ||
972 | g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); | ||
973 | |||
974 | cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; | ||
975 | cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; | ||
976 | cfg2.buckets[THROTTLE_OPS_READ].avg = 123; | ||
977 | cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; | ||
978 | - throttle_group_config(blk3, &cfg1); | ||
979 | + throttle_group_config(tgm3, &cfg1); | ||
980 | |||
981 | - throttle_group_get_config(blk1, &cfg1); | ||
982 | - throttle_group_get_config(blk3, &cfg2); | ||
983 | + throttle_group_get_config(tgm1, &cfg1); | ||
984 | + throttle_group_get_config(tgm3, &cfg2); | ||
985 | g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); | ||
986 | |||
987 | - throttle_group_unregister_blk(blk1); | ||
988 | - throttle_group_unregister_blk(blk2); | ||
989 | - throttle_group_unregister_blk(blk3); | ||
990 | + throttle_group_unregister_tgm(tgm1); | ||
991 | + throttle_group_unregister_tgm(tgm2); | ||
992 | + throttle_group_unregister_tgm(tgm3); | ||
993 | |||
994 | - g_assert(blkp1->throttle_state == NULL); | ||
995 | - g_assert(blkp2->throttle_state == NULL); | ||
996 | - g_assert(blkp3->throttle_state == NULL); | ||
997 | + g_assert(tgm1->throttle_state == NULL); | ||
998 | + g_assert(tgm2->throttle_state == NULL); | ||
999 | + g_assert(tgm3->throttle_state == NULL); | ||
1000 | } | ||
1001 | |||
1002 | int main(int argc, char **argv) | ||
1003 | -- | 448 | -- |
1004 | 2.13.5 | 449 | 2.13.6 |
1005 | 450 | ||
1006 | 451 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | block/throttle.c uses existing I/O throttle infrastructure inside a | 3 | Serialized writes should be used in copy-on-write of backup(sync=none) |
4 | block filter driver. I/O operations are intercepted in the filter's | 4 | for image fleecing scheme. |
5 | read/write coroutines, and referred to block/throttle-groups.c | ||
6 | 5 | ||
7 | The driver can be used with the syntax | 6 | We need to change an assert in bdrv_aligned_pwritev, added in |
8 | -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar | 7 | 28de2dcd88de. The assert may fail now, because call to |
8 | wait_serialising_requests here may become first call to it for this | ||
9 | request with serializing flag set. It occurs if the request is aligned | ||
10 | (otherwise, we should already set serializing flag before calling | ||
11 | bdrv_aligned_pwritev and correspondingly waited for all intersecting | ||
12 | requests). However, for aligned requests, we should not care about | ||
13 | outdating of previously read data, as there no such data. Therefore, | ||
14 | let's just update an assert to not care about aligned requests. | ||
9 | 15 | ||
10 | which registers the throttle filter node with the ThrottleGroup 'bar'. The | 16 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
11 | given group must be created beforehand with object-add or -object. | 17 | Reviewed-by: Fam Zheng <famz@redhat.com> |
12 | |||
13 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
14 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 19 | --- |
18 | qapi/block-core.json | 18 ++- | 20 | include/block/block.h | 14 +++++++++++++- |
19 | include/block/throttle-groups.h | 5 + | 21 | block/io.c | 28 +++++++++++++++++++++++++++- |
20 | include/qemu/throttle-options.h | 1 + | 22 | 2 files changed, 40 insertions(+), 2 deletions(-) |
21 | block/throttle-groups.c | 15 ++- | ||
22 | block/throttle.c | 237 ++++++++++++++++++++++++++++++++++++++++ | ||
23 | block/Makefile.objs | 1 + | ||
24 | 6 files changed, 275 insertions(+), 2 deletions(-) | ||
25 | create mode 100644 block/throttle.c | ||
26 | 23 | ||
27 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 24 | diff --git a/include/block/block.h b/include/block/block.h |
28 | index XXXXXXX..XXXXXXX 100644 | 25 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/qapi/block-core.json | 26 | --- a/include/block/block.h |
30 | +++ b/qapi/block-core.json | 27 | +++ b/include/block/block.h |
31 | @@ -XXX,XX +XXX,XX @@ | 28 | @@ -XXX,XX +XXX,XX @@ typedef enum { |
32 | # Drivers that are supported in block device operations. | 29 | * content. */ |
33 | # | 30 | BDRV_REQ_WRITE_UNCHANGED = 0x40, |
34 | # @vxhs: Since 2.10 | 31 | |
35 | +# @throttle: Since 2.11 | 32 | + /* |
36 | # | 33 | + * BDRV_REQ_SERIALISING forces request serialisation for writes. |
37 | # Since: 2.9 | 34 | + * It is used to ensure that writes to the backing file of a backup process |
38 | ## | 35 | + * target cannot race with a read of the backup target that defers to the |
39 | @@ -XXX,XX +XXX,XX @@ | 36 | + * backing file. |
40 | 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', | 37 | + * |
41 | 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', | 38 | + * Note, that BDRV_REQ_SERIALISING is _not_ opposite in meaning to |
42 | 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', | 39 | + * BDRV_REQ_NO_SERIALISING. A more descriptive name for the latter might be |
43 | - 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } | 40 | + * _DO_NOT_WAIT_FOR_SERIALISING, except that is too long. |
44 | + 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } | 41 | + */ |
45 | 42 | + BDRV_REQ_SERIALISING = 0x80, | |
46 | ## | 43 | + |
47 | # @BlockdevOptionsFile: | 44 | /* Mask of valid flags */ |
48 | @@ -XXX,XX +XXX,XX @@ | 45 | - BDRV_REQ_MASK = 0x7f, |
49 | '*tls-creds': 'str' } } | 46 | + BDRV_REQ_MASK = 0xff, |
50 | 47 | } BdrvRequestFlags; | |
51 | ## | 48 | |
52 | +# @BlockdevOptionsThrottle: | 49 | typedef struct BlockSizes { |
53 | +# | 50 | diff --git a/block/io.c b/block/io.c |
54 | +# Driver specific block device options for the throttle driver | ||
55 | +# | ||
56 | +# @throttle-group: the name of the throttle-group object to use. It | ||
57 | +# must already exist. | ||
58 | +# @file: reference to or definition of the data source block device | ||
59 | +# Since: 2.11 | ||
60 | +## | ||
61 | +{ 'struct': 'BlockdevOptionsThrottle', | ||
62 | + 'data': { 'throttle-group': 'str', | ||
63 | + 'file' : 'BlockdevRef' | ||
64 | + } } | ||
65 | +## | ||
66 | # @BlockdevOptions: | ||
67 | # | ||
68 | # Options for creating a block device. Many options are available for all | ||
69 | @@ -XXX,XX +XXX,XX @@ | ||
70 | 'replication':'BlockdevOptionsReplication', | ||
71 | 'sheepdog': 'BlockdevOptionsSheepdog', | ||
72 | 'ssh': 'BlockdevOptionsSsh', | ||
73 | + 'throttle': 'BlockdevOptionsThrottle', | ||
74 | 'vdi': 'BlockdevOptionsGenericFormat', | ||
75 | 'vhdx': 'BlockdevOptionsGenericFormat', | ||
76 | 'vmdk': 'BlockdevOptionsGenericCOWFormat', | ||
77 | diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h | ||
78 | index XXXXXXX..XXXXXXX 100644 | 51 | index XXXXXXX..XXXXXXX 100644 |
79 | --- a/include/block/throttle-groups.h | 52 | --- a/block/io.c |
80 | +++ b/include/block/throttle-groups.h | 53 | +++ b/block/io.c |
81 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm | 54 | @@ -XXX,XX +XXX,XX @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) |
82 | void throttle_group_attach_aio_context(ThrottleGroupMember *tgm, | 55 | req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); |
83 | AioContext *new_context); | ||
84 | void throttle_group_detach_aio_context(ThrottleGroupMember *tgm); | ||
85 | +/* | ||
86 | + * throttle_group_exists() must be called under the global | ||
87 | + * mutex. | ||
88 | + */ | ||
89 | +bool throttle_group_exists(const char *name); | ||
90 | |||
91 | #endif | ||
92 | diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h | ||
93 | index XXXXXXX..XXXXXXX 100644 | ||
94 | --- a/include/qemu/throttle-options.h | ||
95 | +++ b/include/qemu/throttle-options.h | ||
96 | @@ -XXX,XX +XXX,XX @@ | ||
97 | #define QEMU_OPT_BPS_WRITE_MAX "bps-write-max" | ||
98 | #define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length" | ||
99 | #define QEMU_OPT_IOPS_SIZE "iops-size" | ||
100 | +#define QEMU_OPT_THROTTLE_GROUP_NAME "throttle-group" | ||
101 | |||
102 | #define THROTTLE_OPT_PREFIX "throttling." | ||
103 | #define THROTTLE_OPTS \ | ||
104 | diff --git a/block/throttle-groups.c b/block/throttle-groups.c | ||
105 | index XXXXXXX..XXXXXXX 100644 | ||
106 | --- a/block/throttle-groups.c | ||
107 | +++ b/block/throttle-groups.c | ||
108 | @@ -XXX,XX +XXX,XX @@ static ThrottleGroup *throttle_group_by_name(const char *name) | ||
109 | return NULL; | ||
110 | } | 56 | } |
111 | 57 | ||
112 | +/* This function reads throttle_groups and must be called under the global | 58 | +static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req) |
113 | + * mutex. | ||
114 | + */ | ||
115 | +bool throttle_group_exists(const char *name) | ||
116 | +{ | 59 | +{ |
117 | + return throttle_group_by_name(name) != NULL; | 60 | + /* |
61 | + * If the request is serialising, overlap_offset and overlap_bytes are set, | ||
62 | + * so we can check if the request is aligned. Otherwise, don't care and | ||
63 | + * return false. | ||
64 | + */ | ||
65 | + | ||
66 | + return req->serialising && (req->offset == req->overlap_offset) && | ||
67 | + (req->bytes == req->overlap_bytes); | ||
118 | +} | 68 | +} |
119 | + | 69 | + |
120 | /* Increments the reference count of a ThrottleGroup given its name. | 70 | /** |
121 | * | 71 | * Round a region to cluster boundaries |
122 | * If no ThrottleGroup is found with the given name a new one is | 72 | */ |
123 | @@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) | 73 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, |
124 | ThrottleGroupMember *token; | 74 | mark_request_serialising(req, bdrv_get_cluster_size(bs)); |
125 | int i; | 75 | } |
126 | 76 | ||
127 | + if (!ts) { | 77 | + /* BDRV_REQ_SERIALISING is only for write operation */ |
128 | + /* Discard already unregistered tgm */ | 78 | + assert(!(flags & BDRV_REQ_SERIALISING)); |
129 | + return; | 79 | + |
80 | if (!(flags & BDRV_REQ_NO_SERIALISING)) { | ||
81 | wait_serialising_requests(req); | ||
82 | } | ||
83 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, | ||
84 | |||
85 | /* BDRV_REQ_NO_SERIALISING is only for read operation */ | ||
86 | assert(!(flags & BDRV_REQ_NO_SERIALISING)); | ||
87 | + | ||
88 | + if (flags & BDRV_REQ_SERIALISING) { | ||
89 | + mark_request_serialising(req, bdrv_get_cluster_size(bs)); | ||
130 | + } | 90 | + } |
131 | + | 91 | + |
132 | assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0); | 92 | waited = wait_serialising_requests(req); |
133 | assert(qemu_co_queue_empty(&tgm->throttled_reqs[0])); | 93 | - assert(!waited || !req->serialising); |
134 | assert(qemu_co_queue_empty(&tgm->throttled_reqs[1])); | 94 | + assert(!waited || !req->serialising || |
135 | @@ -XXX,XX +XXX,XX @@ static void throttle_group_obj_complete(UserCreatable *obj, Error **errp) | 95 | + is_request_serialising_and_aligned(req)); |
136 | assert(tg->name); | 96 | assert(req->overlap_offset <= offset); |
137 | 97 | assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); | |
138 | /* error if name is duplicate */ | 98 | if (flags & BDRV_REQ_WRITE_UNCHANGED) { |
139 | - if (throttle_group_by_name(tg->name) != NULL) { | 99 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal( |
140 | + if (throttle_group_exists(tg->name)) { | 100 | tracked_request_begin(&req, src->bs, src_offset, bytes, |
141 | error_setg(errp, "A group with this name already exists"); | 101 | BDRV_TRACKED_READ); |
142 | return; | 102 | |
143 | } | 103 | + /* BDRV_REQ_SERIALISING is only for write operation */ |
144 | diff --git a/block/throttle.c b/block/throttle.c | 104 | + assert(!(read_flags & BDRV_REQ_SERIALISING)); |
145 | new file mode 100644 | 105 | if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { |
146 | index XXXXXXX..XXXXXXX | 106 | wait_serialising_requests(&req); |
147 | --- /dev/null | 107 | } |
148 | +++ b/block/throttle.c | 108 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal( |
149 | @@ -XXX,XX +XXX,XX @@ | 109 | |
150 | +/* | 110 | /* BDRV_REQ_NO_SERIALISING is only for read operation */ |
151 | + * QEMU block throttling filter driver infrastructure | 111 | assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); |
152 | + * | 112 | + if (write_flags & BDRV_REQ_SERIALISING) { |
153 | + * Copyright (c) 2017 Manos Pitsidianakis | 113 | + mark_request_serialising(&req, bdrv_get_cluster_size(dst->bs)); |
154 | + * | 114 | + } |
155 | + * This program is free software; you can redistribute it and/or | 115 | wait_serialising_requests(&req); |
156 | + * modify it under the terms of the GNU General Public License as | 116 | |
157 | + * published by the Free Software Foundation; either version 2 or | 117 | ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, |
158 | + * (at your option) version 3 of the License. | ||
159 | + * | ||
160 | + * This program is distributed in the hope that it will be useful, | ||
161 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
162 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
163 | + * GNU General Public License for more details. | ||
164 | + * | ||
165 | + * You should have received a copy of the GNU General Public License | ||
166 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
167 | + */ | ||
168 | + | ||
169 | +#include "qemu/osdep.h" | ||
170 | +#include "block/throttle-groups.h" | ||
171 | +#include "qemu/throttle-options.h" | ||
172 | +#include "qapi/error.h" | ||
173 | + | ||
174 | +static QemuOptsList throttle_opts = { | ||
175 | + .name = "throttle", | ||
176 | + .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head), | ||
177 | + .desc = { | ||
178 | + { | ||
179 | + .name = QEMU_OPT_THROTTLE_GROUP_NAME, | ||
180 | + .type = QEMU_OPT_STRING, | ||
181 | + .help = "Name of the throttle group", | ||
182 | + }, | ||
183 | + { /* end of list */ } | ||
184 | + }, | ||
185 | +}; | ||
186 | + | ||
187 | +static int throttle_configure_tgm(BlockDriverState *bs, | ||
188 | + ThrottleGroupMember *tgm, | ||
189 | + QDict *options, Error **errp) | ||
190 | +{ | ||
191 | + int ret; | ||
192 | + const char *group_name; | ||
193 | + Error *local_err = NULL; | ||
194 | + QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort); | ||
195 | + | ||
196 | + qemu_opts_absorb_qdict(opts, options, &local_err); | ||
197 | + if (local_err) { | ||
198 | + error_propagate(errp, local_err); | ||
199 | + ret = -EINVAL; | ||
200 | + goto fin; | ||
201 | + } | ||
202 | + | ||
203 | + group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME); | ||
204 | + if (!group_name) { | ||
205 | + error_setg(errp, "Please specify a throttle group"); | ||
206 | + ret = -EINVAL; | ||
207 | + goto fin; | ||
208 | + } else if (!throttle_group_exists(group_name)) { | ||
209 | + error_setg(errp, "Throttle group '%s' does not exist", group_name); | ||
210 | + ret = -EINVAL; | ||
211 | + goto fin; | ||
212 | + } | ||
213 | + | ||
214 | + /* Register membership to group with name group_name */ | ||
215 | + throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs)); | ||
216 | + ret = 0; | ||
217 | +fin: | ||
218 | + qemu_opts_del(opts); | ||
219 | + return ret; | ||
220 | +} | ||
221 | + | ||
222 | +static int throttle_open(BlockDriverState *bs, QDict *options, | ||
223 | + int flags, Error **errp) | ||
224 | +{ | ||
225 | + ThrottleGroupMember *tgm = bs->opaque; | ||
226 | + | ||
227 | + bs->file = bdrv_open_child(NULL, options, "file", bs, | ||
228 | + &child_file, false, errp); | ||
229 | + if (!bs->file) { | ||
230 | + return -EINVAL; | ||
231 | + } | ||
232 | + bs->supported_write_flags = bs->file->bs->supported_write_flags; | ||
233 | + bs->supported_zero_flags = bs->file->bs->supported_zero_flags; | ||
234 | + | ||
235 | + return throttle_configure_tgm(bs, tgm, options, errp); | ||
236 | +} | ||
237 | + | ||
238 | +static void throttle_close(BlockDriverState *bs) | ||
239 | +{ | ||
240 | + ThrottleGroupMember *tgm = bs->opaque; | ||
241 | + throttle_group_unregister_tgm(tgm); | ||
242 | +} | ||
243 | + | ||
244 | + | ||
245 | +static int64_t throttle_getlength(BlockDriverState *bs) | ||
246 | +{ | ||
247 | + return bdrv_getlength(bs->file->bs); | ||
248 | +} | ||
249 | + | ||
250 | +static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, | ||
251 | + uint64_t offset, uint64_t bytes, | ||
252 | + QEMUIOVector *qiov, int flags) | ||
253 | +{ | ||
254 | + | ||
255 | + ThrottleGroupMember *tgm = bs->opaque; | ||
256 | + throttle_group_co_io_limits_intercept(tgm, bytes, false); | ||
257 | + | ||
258 | + return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); | ||
259 | +} | ||
260 | + | ||
261 | +static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, | ||
262 | + uint64_t offset, uint64_t bytes, | ||
263 | + QEMUIOVector *qiov, int flags) | ||
264 | +{ | ||
265 | + ThrottleGroupMember *tgm = bs->opaque; | ||
266 | + throttle_group_co_io_limits_intercept(tgm, bytes, true); | ||
267 | + | ||
268 | + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); | ||
269 | +} | ||
270 | + | ||
271 | +static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs, | ||
272 | + int64_t offset, int bytes, | ||
273 | + BdrvRequestFlags flags) | ||
274 | +{ | ||
275 | + ThrottleGroupMember *tgm = bs->opaque; | ||
276 | + throttle_group_co_io_limits_intercept(tgm, bytes, true); | ||
277 | + | ||
278 | + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); | ||
279 | +} | ||
280 | + | ||
281 | +static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, | ||
282 | + int64_t offset, int bytes) | ||
283 | +{ | ||
284 | + ThrottleGroupMember *tgm = bs->opaque; | ||
285 | + throttle_group_co_io_limits_intercept(tgm, bytes, true); | ||
286 | + | ||
287 | + return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||
288 | +} | ||
289 | + | ||
290 | +static int throttle_co_flush(BlockDriverState *bs) | ||
291 | +{ | ||
292 | + return bdrv_co_flush(bs->file->bs); | ||
293 | +} | ||
294 | + | ||
295 | +static void throttle_detach_aio_context(BlockDriverState *bs) | ||
296 | +{ | ||
297 | + ThrottleGroupMember *tgm = bs->opaque; | ||
298 | + throttle_group_detach_aio_context(tgm); | ||
299 | +} | ||
300 | + | ||
301 | +static void throttle_attach_aio_context(BlockDriverState *bs, | ||
302 | + AioContext *new_context) | ||
303 | +{ | ||
304 | + ThrottleGroupMember *tgm = bs->opaque; | ||
305 | + throttle_group_attach_aio_context(tgm, new_context); | ||
306 | +} | ||
307 | + | ||
308 | +static int throttle_reopen_prepare(BDRVReopenState *reopen_state, | ||
309 | + BlockReopenQueue *queue, Error **errp) | ||
310 | +{ | ||
311 | + ThrottleGroupMember *tgm; | ||
312 | + | ||
313 | + assert(reopen_state != NULL); | ||
314 | + assert(reopen_state->bs != NULL); | ||
315 | + | ||
316 | + reopen_state->opaque = g_new0(ThrottleGroupMember, 1); | ||
317 | + tgm = reopen_state->opaque; | ||
318 | + | ||
319 | + return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options, | ||
320 | + errp); | ||
321 | +} | ||
322 | + | ||
323 | +static void throttle_reopen_commit(BDRVReopenState *reopen_state) | ||
324 | +{ | ||
325 | + ThrottleGroupMember *old_tgm = reopen_state->bs->opaque; | ||
326 | + ThrottleGroupMember *new_tgm = reopen_state->opaque; | ||
327 | + | ||
328 | + throttle_group_unregister_tgm(old_tgm); | ||
329 | + g_free(old_tgm); | ||
330 | + reopen_state->bs->opaque = new_tgm; | ||
331 | + reopen_state->opaque = NULL; | ||
332 | +} | ||
333 | + | ||
334 | +static void throttle_reopen_abort(BDRVReopenState *reopen_state) | ||
335 | +{ | ||
336 | + ThrottleGroupMember *tgm = reopen_state->opaque; | ||
337 | + | ||
338 | + throttle_group_unregister_tgm(tgm); | ||
339 | + g_free(tgm); | ||
340 | + reopen_state->opaque = NULL; | ||
341 | +} | ||
342 | + | ||
343 | +static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs, | ||
344 | + BlockDriverState *candidate) | ||
345 | +{ | ||
346 | + return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); | ||
347 | +} | ||
348 | + | ||
349 | +static BlockDriver bdrv_throttle = { | ||
350 | + .format_name = "throttle", | ||
351 | + .protocol_name = "throttle", | ||
352 | + .instance_size = sizeof(ThrottleGroupMember), | ||
353 | + | ||
354 | + .bdrv_file_open = throttle_open, | ||
355 | + .bdrv_close = throttle_close, | ||
356 | + .bdrv_co_flush = throttle_co_flush, | ||
357 | + | ||
358 | + .bdrv_child_perm = bdrv_filter_default_perms, | ||
359 | + | ||
360 | + .bdrv_getlength = throttle_getlength, | ||
361 | + | ||
362 | + .bdrv_co_preadv = throttle_co_preadv, | ||
363 | + .bdrv_co_pwritev = throttle_co_pwritev, | ||
364 | + | ||
365 | + .bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes, | ||
366 | + .bdrv_co_pdiscard = throttle_co_pdiscard, | ||
367 | + | ||
368 | + .bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter, | ||
369 | + | ||
370 | + .bdrv_attach_aio_context = throttle_attach_aio_context, | ||
371 | + .bdrv_detach_aio_context = throttle_detach_aio_context, | ||
372 | + | ||
373 | + .bdrv_reopen_prepare = throttle_reopen_prepare, | ||
374 | + .bdrv_reopen_commit = throttle_reopen_commit, | ||
375 | + .bdrv_reopen_abort = throttle_reopen_abort, | ||
376 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file, | ||
377 | + | ||
378 | + .is_filter = true, | ||
379 | +}; | ||
380 | + | ||
381 | +static void bdrv_throttle_init(void) | ||
382 | +{ | ||
383 | + bdrv_register(&bdrv_throttle); | ||
384 | +} | ||
385 | + | ||
386 | +block_init(bdrv_throttle_init); | ||
387 | diff --git a/block/Makefile.objs b/block/Makefile.objs | ||
388 | index XXXXXXX..XXXXXXX 100644 | ||
389 | --- a/block/Makefile.objs | ||
390 | +++ b/block/Makefile.objs | ||
391 | @@ -XXX,XX +XXX,XX @@ block-obj-y += accounting.o dirty-bitmap.o | ||
392 | block-obj-y += write-threshold.o | ||
393 | block-obj-y += backup.o | ||
394 | block-obj-$(CONFIG_REPLICATION) += replication.o | ||
395 | +block-obj-y += throttle.o | ||
396 | |||
397 | block-obj-y += crypto.o | ||
398 | |||
399 | -- | 118 | -- |
400 | 2.13.5 | 119 | 2.13.6 |
401 | 120 | ||
402 | 121 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Fleecing scheme works as follows: we want a kind of temporary snapshot | ||
4 | of active drive A. We create temporary image B, with B->backing = A. | ||
5 | Then we start backup(sync=none) from A to B. From this point, B reads | ||
6 | as point-in-time snapshot of A (A continues to be active drive, | ||
7 | accepting guest IO). | ||
8 | |||
9 | This scheme needs some additional synchronization between reads from B | ||
10 | and backup COW operations, otherwise, the following situation is | ||
11 | theoretically possible: | ||
12 | |||
13 | (assume B is qcow2, client is NBD client, reading from B) | ||
14 | |||
15 | 1. client starts reading and take qcow2 mutex in qcow2_co_preadv, and | ||
16 | goes up to l2 table loading (assume cache miss) | ||
17 | |||
18 | 2) guest write => backup COW => qcow2 write => | ||
19 | try to take qcow2 mutex => waiting | ||
20 | |||
21 | 3. l2 table loaded, we see that cluster is UNALLOCATED, go to | ||
22 | "case QCOW2_CLUSTER_UNALLOCATED" and unlock mutex before | ||
23 | bdrv_co_preadv(bs->backing, ...) | ||
24 | |||
25 | 4) aha, mutex unlocked, backup COW continues, and we finally finish | ||
26 | guest write and change cluster in our active disk A | ||
27 | |||
28 | 5. actually, do bdrv_co_preadv(bs->backing, ...) and read | ||
29 | _new updated_ data. | ||
30 | |||
31 | To avoid this, let's make backup writes serializing, to not intersect | ||
32 | with reads from B. | ||
33 | |||
34 | Note: we expand range of handled cases from (sync=none and | ||
35 | B->backing = A) to just (A in backing chain of B), to finally allow | ||
36 | safe reading from B during backup for all cases when A in backing chain | ||
37 | of B, i.e. B formally looks like point-in-time snapshot of A. | ||
38 | |||
39 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
40 | Reviewed-by: Fam Zheng <famz@redhat.com> | ||
41 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
42 | --- | ||
43 | block/backup.c | 20 ++++++++++++++------ | ||
44 | 1 file changed, 14 insertions(+), 6 deletions(-) | ||
45 | |||
46 | diff --git a/block/backup.c b/block/backup.c | ||
47 | index XXXXXXX..XXXXXXX 100644 | ||
48 | --- a/block/backup.c | ||
49 | +++ b/block/backup.c | ||
50 | @@ -XXX,XX +XXX,XX @@ typedef struct BackupBlockJob { | ||
51 | HBitmap *copy_bitmap; | ||
52 | bool use_copy_range; | ||
53 | int64_t copy_range_size; | ||
54 | + | ||
55 | + bool serialize_target_writes; | ||
56 | } BackupBlockJob; | ||
57 | |||
58 | static const BlockJobDriver backup_job_driver; | ||
59 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, | ||
60 | QEMUIOVector qiov; | ||
61 | BlockBackend *blk = job->common.blk; | ||
62 | int nbytes; | ||
63 | + int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; | ||
64 | + int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; | ||
65 | |||
66 | hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); | ||
67 | nbytes = MIN(job->cluster_size, job->len - start); | ||
68 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, | ||
69 | iov.iov_len = nbytes; | ||
70 | qemu_iovec_init_external(&qiov, &iov, 1); | ||
71 | |||
72 | - ret = blk_co_preadv(blk, start, qiov.size, &qiov, | ||
73 | - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); | ||
74 | + ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); | ||
75 | if (ret < 0) { | ||
76 | trace_backup_do_cow_read_fail(job, start, ret); | ||
77 | if (error_is_read) { | ||
78 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, | ||
79 | |||
80 | if (qemu_iovec_is_zero(&qiov)) { | ||
81 | ret = blk_co_pwrite_zeroes(job->target, start, | ||
82 | - qiov.size, BDRV_REQ_MAY_UNMAP); | ||
83 | + qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); | ||
84 | } else { | ||
85 | ret = blk_co_pwritev(job->target, start, | ||
86 | - qiov.size, &qiov, | ||
87 | - job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); | ||
88 | + qiov.size, &qiov, write_flags | | ||
89 | + (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); | ||
90 | } | ||
91 | if (ret < 0) { | ||
92 | trace_backup_do_cow_write_fail(job, start, ret); | ||
93 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, | ||
94 | int nr_clusters; | ||
95 | BlockBackend *blk = job->common.blk; | ||
96 | int nbytes; | ||
97 | + int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; | ||
98 | + int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; | ||
99 | |||
100 | assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); | ||
101 | nbytes = MIN(job->copy_range_size, end - start); | ||
102 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, | ||
103 | hbitmap_reset(job->copy_bitmap, start / job->cluster_size, | ||
104 | nr_clusters); | ||
105 | ret = blk_co_copy_range(blk, start, job->target, start, nbytes, | ||
106 | - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0, 0); | ||
107 | + read_flags, write_flags); | ||
108 | if (ret < 0) { | ||
109 | trace_backup_do_cow_copy_range_fail(job, start, ret); | ||
110 | hbitmap_set(job->copy_bitmap, start / job->cluster_size, | ||
111 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
112 | sync_bitmap : NULL; | ||
113 | job->compress = compress; | ||
114 | |||
115 | + /* Detect image-fleecing (and similar) schemes */ | ||
116 | + job->serialize_target_writes = bdrv_chain_contains(target, bs); | ||
117 | + | ||
118 | /* If there is no backing file on the target, we cannot rely on COW if our | ||
119 | * backup cluster size is smaller than the target cluster size. Even for | ||
120 | * targets with a backing file, try to avoid COW if possible. */ | ||
121 | -- | ||
122 | 2.13.6 | ||
123 | |||
124 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Ari Sundholm <ari@tuxera.com> |
---|---|---|---|
2 | 2 | ||
3 | ThrottleGroup is converted to an object. This will allow the future | 3 | This was accidentally omitted. Thanks to Eric Blake for spotting this. |
4 | throttle block filter drive easy creation and configuration of throttle | ||
5 | groups in QMP and cli. | ||
6 | 4 | ||
7 | A new QAPI struct, ThrottleLimits, is introduced to provide a shared | 5 | Signed-off-by: Ari Sundholm <ari@tuxera.com> |
8 | struct for all throttle configuration needs in QMP. | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
9 | |||
10 | ThrottleGroups can be created via CLI as | ||
11 | -object throttle-group,id=foo,x-iops-total=100,x-.. | ||
12 | where x-* are individual limit properties. Since we can't add non-scalar | ||
13 | properties in -object this interface must be used instead. However, | ||
14 | setting these properties must be disabled after initialization because | ||
15 | certain combinations of limits are forbidden and thus configuration | ||
16 | changes should be done in one transaction. The individual properties | ||
17 | will go away when support for non-scalar values in CLI is implemented | ||
18 | and thus are marked as experimental. | ||
19 | |||
20 | ThrottleGroup also has a `limits` property that uses the ThrottleLimits | ||
21 | struct. It can be used to create ThrottleGroups or set the | ||
22 | configuration in existing groups as follows: | ||
23 | |||
24 | { "execute": "object-add", | ||
25 | "arguments": { | ||
26 | "qom-type": "throttle-group", | ||
27 | "id": "foo", | ||
28 | "props" : { | ||
29 | "limits": { | ||
30 | "iops-total": 100 | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | { "execute" : "qom-set", | ||
36 | "arguments" : { | ||
37 | "path" : "foo", | ||
38 | "property" : "limits", | ||
39 | "value" : { | ||
40 | "iops-total" : 99 | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | This also means a group's configuration can be fetched with qom-get. | ||
46 | |||
47 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
48 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
49 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
50 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
51 | --- | 8 | --- |
52 | qapi/block-core.json | 48 +++++ | 9 | qapi/block-core.json | 2 ++ |
53 | include/block/throttle-groups.h | 3 + | 10 | 1 file changed, 2 insertions(+) |
54 | include/qemu/throttle-options.h | 59 ++++-- | ||
55 | include/qemu/throttle.h | 3 + | ||
56 | block/throttle-groups.c | 424 ++++++++++++++++++++++++++++++++++++---- | ||
57 | tests/test-throttle.c | 1 + | ||
58 | util/throttle.c | 151 ++++++++++++++ | ||
59 | 7 files changed, 628 insertions(+), 61 deletions(-) | ||
60 | 11 | ||
61 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 12 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
62 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
63 | --- a/qapi/block-core.json | 14 | --- a/qapi/block-core.json |
64 | +++ b/qapi/block-core.json | 15 | +++ b/qapi/block-core.json |
65 | @@ -XXX,XX +XXX,XX @@ | 16 | @@ -XXX,XX +XXX,XX @@ |
66 | '*iops_size': 'int', '*group': 'str' } } | 17 | # @log-sector-size: sector size used in logging writes to @file, determines |
67 | 18 | # granularity of offsets and sizes of writes (default: 512) | |
68 | ## | 19 | # |
69 | +# @ThrottleLimits: | 20 | +# @log-append: append to an existing log (default: false) |
70 | +# | 21 | +# |
71 | +# Limit parameters for throttling. | 22 | # @log-super-update-interval: interval of write requests after which the log |
72 | +# Since some limit combinations are illegal, limits should always be set in one | 23 | # super block is updated to disk (default: 4096) |
73 | +# transaction. All fields are optional. When setting limits, if a field is | ||
74 | +# missing the current value is not changed. | ||
75 | +# | ||
76 | +# @iops-total: limit total I/O operations per second | ||
77 | +# @iops-total-max: I/O operations burst | ||
78 | +# @iops-total-max-length: length of the iops-total-max burst period, in seconds | ||
79 | +# It must only be set if @iops-total-max is set as well. | ||
80 | +# @iops-read: limit read operations per second | ||
81 | +# @iops-read-max: I/O operations read burst | ||
82 | +# @iops-read-max-length: length of the iops-read-max burst period, in seconds | ||
83 | +# It must only be set if @iops-read-max is set as well. | ||
84 | +# @iops-write: limit write operations per second | ||
85 | +# @iops-write-max: I/O operations write burst | ||
86 | +# @iops-write-max-length: length of the iops-write-max burst period, in seconds | ||
87 | +# It must only be set if @iops-write-max is set as well. | ||
88 | +# @bps-total: limit total bytes per second | ||
89 | +# @bps-total-max: total bytes burst | ||
90 | +# @bps-total-max-length: length of the bps-total-max burst period, in seconds. | ||
91 | +# It must only be set if @bps-total-max is set as well. | ||
92 | +# @bps-read: limit read bytes per second | ||
93 | +# @bps-read-max: total bytes read burst | ||
94 | +# @bps-read-max-length: length of the bps-read-max burst period, in seconds | ||
95 | +# It must only be set if @bps-read-max is set as well. | ||
96 | +# @bps-write: limit write bytes per second | ||
97 | +# @bps-write-max: total bytes write burst | ||
98 | +# @bps-write-max-length: length of the bps-write-max burst period, in seconds | ||
99 | +# It must only be set if @bps-write-max is set as well. | ||
100 | +# @iops-size: when limiting by iops max size of an I/O in bytes | ||
101 | +# | ||
102 | +# Since: 2.11 | ||
103 | +## | ||
104 | +{ 'struct': 'ThrottleLimits', | ||
105 | + 'data': { '*iops-total' : 'int', '*iops-total-max' : 'int', | ||
106 | + '*iops-total-max-length' : 'int', '*iops-read' : 'int', | ||
107 | + '*iops-read-max' : 'int', '*iops-read-max-length' : 'int', | ||
108 | + '*iops-write' : 'int', '*iops-write-max' : 'int', | ||
109 | + '*iops-write-max-length' : 'int', '*bps-total' : 'int', | ||
110 | + '*bps-total-max' : 'int', '*bps-total-max-length' : 'int', | ||
111 | + '*bps-read' : 'int', '*bps-read-max' : 'int', | ||
112 | + '*bps-read-max-length' : 'int', '*bps-write' : 'int', | ||
113 | + '*bps-write-max' : 'int', '*bps-write-max-length' : 'int', | ||
114 | + '*iops-size' : 'int' } } | ||
115 | + | ||
116 | +## | ||
117 | # @block-stream: | ||
118 | # | 24 | # |
119 | # Copy data from a backing file into a block device. | ||
120 | diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h | ||
121 | index XXXXXXX..XXXXXXX 100644 | ||
122 | --- a/include/block/throttle-groups.h | ||
123 | +++ b/include/block/throttle-groups.h | ||
124 | @@ -XXX,XX +XXX,XX @@ typedef struct ThrottleGroupMember { | ||
125 | |||
126 | } ThrottleGroupMember; | ||
127 | |||
128 | +#define TYPE_THROTTLE_GROUP "throttle-group" | ||
129 | +#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), TYPE_THROTTLE_GROUP) | ||
130 | + | ||
131 | const char *throttle_group_get_name(ThrottleGroupMember *tgm); | ||
132 | |||
133 | ThrottleState *throttle_group_incref(const char *name); | ||
134 | diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h | ||
135 | index XXXXXXX..XXXXXXX 100644 | ||
136 | --- a/include/qemu/throttle-options.h | ||
137 | +++ b/include/qemu/throttle-options.h | ||
138 | @@ -XXX,XX +XXX,XX @@ | ||
139 | #ifndef THROTTLE_OPTIONS_H | ||
140 | #define THROTTLE_OPTIONS_H | ||
141 | |||
142 | +#define QEMU_OPT_IOPS_TOTAL "iops-total" | ||
143 | +#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max" | ||
144 | +#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length" | ||
145 | +#define QEMU_OPT_IOPS_READ "iops-read" | ||
146 | +#define QEMU_OPT_IOPS_READ_MAX "iops-read-max" | ||
147 | +#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length" | ||
148 | +#define QEMU_OPT_IOPS_WRITE "iops-write" | ||
149 | +#define QEMU_OPT_IOPS_WRITE_MAX "iops-write-max" | ||
150 | +#define QEMU_OPT_IOPS_WRITE_MAX_LENGTH "iops-write-max-length" | ||
151 | +#define QEMU_OPT_BPS_TOTAL "bps-total" | ||
152 | +#define QEMU_OPT_BPS_TOTAL_MAX "bps-total-max" | ||
153 | +#define QEMU_OPT_BPS_TOTAL_MAX_LENGTH "bps-total-max-length" | ||
154 | +#define QEMU_OPT_BPS_READ "bps-read" | ||
155 | +#define QEMU_OPT_BPS_READ_MAX "bps-read-max" | ||
156 | +#define QEMU_OPT_BPS_READ_MAX_LENGTH "bps-read-max-length" | ||
157 | +#define QEMU_OPT_BPS_WRITE "bps-write" | ||
158 | +#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max" | ||
159 | +#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length" | ||
160 | +#define QEMU_OPT_IOPS_SIZE "iops-size" | ||
161 | + | ||
162 | +#define THROTTLE_OPT_PREFIX "throttling." | ||
163 | #define THROTTLE_OPTS \ | ||
164 | { \ | ||
165 | - .name = "throttling.iops-total",\ | ||
166 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\ | ||
167 | .type = QEMU_OPT_NUMBER,\ | ||
168 | .help = "limit total I/O operations per second",\ | ||
169 | },{ \ | ||
170 | - .name = "throttling.iops-read",\ | ||
171 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\ | ||
172 | .type = QEMU_OPT_NUMBER,\ | ||
173 | .help = "limit read operations per second",\ | ||
174 | },{ \ | ||
175 | - .name = "throttling.iops-write",\ | ||
176 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\ | ||
177 | .type = QEMU_OPT_NUMBER,\ | ||
178 | .help = "limit write operations per second",\ | ||
179 | },{ \ | ||
180 | - .name = "throttling.bps-total",\ | ||
181 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\ | ||
182 | .type = QEMU_OPT_NUMBER,\ | ||
183 | .help = "limit total bytes per second",\ | ||
184 | },{ \ | ||
185 | - .name = "throttling.bps-read",\ | ||
186 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\ | ||
187 | .type = QEMU_OPT_NUMBER,\ | ||
188 | .help = "limit read bytes per second",\ | ||
189 | },{ \ | ||
190 | - .name = "throttling.bps-write",\ | ||
191 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\ | ||
192 | .type = QEMU_OPT_NUMBER,\ | ||
193 | .help = "limit write bytes per second",\ | ||
194 | },{ \ | ||
195 | - .name = "throttling.iops-total-max",\ | ||
196 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\ | ||
197 | .type = QEMU_OPT_NUMBER,\ | ||
198 | .help = "I/O operations burst",\ | ||
199 | },{ \ | ||
200 | - .name = "throttling.iops-read-max",\ | ||
201 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\ | ||
202 | .type = QEMU_OPT_NUMBER,\ | ||
203 | .help = "I/O operations read burst",\ | ||
204 | },{ \ | ||
205 | - .name = "throttling.iops-write-max",\ | ||
206 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\ | ||
207 | .type = QEMU_OPT_NUMBER,\ | ||
208 | .help = "I/O operations write burst",\ | ||
209 | },{ \ | ||
210 | - .name = "throttling.bps-total-max",\ | ||
211 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\ | ||
212 | .type = QEMU_OPT_NUMBER,\ | ||
213 | .help = "total bytes burst",\ | ||
214 | },{ \ | ||
215 | - .name = "throttling.bps-read-max",\ | ||
216 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\ | ||
217 | .type = QEMU_OPT_NUMBER,\ | ||
218 | .help = "total bytes read burst",\ | ||
219 | },{ \ | ||
220 | - .name = "throttling.bps-write-max",\ | ||
221 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\ | ||
222 | .type = QEMU_OPT_NUMBER,\ | ||
223 | .help = "total bytes write burst",\ | ||
224 | },{ \ | ||
225 | - .name = "throttling.iops-total-max-length",\ | ||
226 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,\ | ||
227 | .type = QEMU_OPT_NUMBER,\ | ||
228 | .help = "length of the iops-total-max burst period, in seconds",\ | ||
229 | },{ \ | ||
230 | - .name = "throttling.iops-read-max-length",\ | ||
231 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,\ | ||
232 | .type = QEMU_OPT_NUMBER,\ | ||
233 | .help = "length of the iops-read-max burst period, in seconds",\ | ||
234 | },{ \ | ||
235 | - .name = "throttling.iops-write-max-length",\ | ||
236 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,\ | ||
237 | .type = QEMU_OPT_NUMBER,\ | ||
238 | .help = "length of the iops-write-max burst period, in seconds",\ | ||
239 | },{ \ | ||
240 | - .name = "throttling.bps-total-max-length",\ | ||
241 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,\ | ||
242 | .type = QEMU_OPT_NUMBER,\ | ||
243 | .help = "length of the bps-total-max burst period, in seconds",\ | ||
244 | },{ \ | ||
245 | - .name = "throttling.bps-read-max-length",\ | ||
246 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,\ | ||
247 | .type = QEMU_OPT_NUMBER,\ | ||
248 | .help = "length of the bps-read-max burst period, in seconds",\ | ||
249 | },{ \ | ||
250 | - .name = "throttling.bps-write-max-length",\ | ||
251 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,\ | ||
252 | .type = QEMU_OPT_NUMBER,\ | ||
253 | .help = "length of the bps-write-max burst period, in seconds",\ | ||
254 | },{ \ | ||
255 | - .name = "throttling.iops-size",\ | ||
256 | + .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,\ | ||
257 | .type = QEMU_OPT_NUMBER,\ | ||
258 | .help = "when limiting by iops max size of an I/O in bytes",\ | ||
259 | } | ||
260 | diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h | ||
261 | index XXXXXXX..XXXXXXX 100644 | ||
262 | --- a/include/qemu/throttle.h | ||
263 | +++ b/include/qemu/throttle.h | ||
264 | @@ -XXX,XX +XXX,XX @@ bool throttle_schedule_timer(ThrottleState *ts, | ||
265 | bool is_write); | ||
266 | |||
267 | void throttle_account(ThrottleState *ts, bool is_write, uint64_t size); | ||
268 | +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg, | ||
269 | + Error **errp); | ||
270 | +void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var); | ||
271 | |||
272 | #endif | ||
273 | diff --git a/block/throttle-groups.c b/block/throttle-groups.c | ||
274 | index XXXXXXX..XXXXXXX 100644 | ||
275 | --- a/block/throttle-groups.c | ||
276 | +++ b/block/throttle-groups.c | ||
277 | @@ -XXX,XX +XXX,XX @@ | ||
278 | #include "qemu/osdep.h" | ||
279 | #include "sysemu/block-backend.h" | ||
280 | #include "block/throttle-groups.h" | ||
281 | +#include "qemu/throttle-options.h" | ||
282 | #include "qemu/queue.h" | ||
283 | #include "qemu/thread.h" | ||
284 | #include "sysemu/qtest.h" | ||
285 | +#include "qapi/error.h" | ||
286 | +#include "qapi-visit.h" | ||
287 | +#include "qom/object.h" | ||
288 | +#include "qom/object_interfaces.h" | ||
289 | + | ||
290 | +static void throttle_group_obj_init(Object *obj); | ||
291 | +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp); | ||
292 | |||
293 | /* The ThrottleGroup structure (with its ThrottleState) is shared | ||
294 | * among different ThrottleGroupMembers and it's independent from | ||
295 | @@ -XXX,XX +XXX,XX @@ | ||
296 | * that ThrottleGroupMember has throttled requests in the queue. | ||
297 | */ | ||
298 | typedef struct ThrottleGroup { | ||
299 | + Object parent_obj; | ||
300 | + | ||
301 | + /* refuse individual property change if initialization is complete */ | ||
302 | + bool is_initialized; | ||
303 | char *name; /* This is constant during the lifetime of the group */ | ||
304 | |||
305 | QemuMutex lock; /* This lock protects the following four fields */ | ||
306 | @@ -XXX,XX +XXX,XX @@ typedef struct ThrottleGroup { | ||
307 | bool any_timer_armed[2]; | ||
308 | QEMUClockType clock_type; | ||
309 | |||
310 | - /* These two are protected by the global throttle_groups_lock */ | ||
311 | - unsigned refcount; | ||
312 | + /* This field is protected by the global QEMU mutex */ | ||
313 | QTAILQ_ENTRY(ThrottleGroup) list; | ||
314 | } ThrottleGroup; | ||
315 | |||
316 | -static QemuMutex throttle_groups_lock; | ||
317 | +/* This is protected by the global QEMU mutex */ | ||
318 | static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = | ||
319 | QTAILQ_HEAD_INITIALIZER(throttle_groups); | ||
320 | |||
321 | + | ||
322 | +/* This function reads throttle_groups and must be called under the global | ||
323 | + * mutex. | ||
324 | + */ | ||
325 | +static ThrottleGroup *throttle_group_by_name(const char *name) | ||
326 | +{ | ||
327 | + ThrottleGroup *iter; | ||
328 | + | ||
329 | + /* Look for an existing group with that name */ | ||
330 | + QTAILQ_FOREACH(iter, &throttle_groups, list) { | ||
331 | + if (!g_strcmp0(name, iter->name)) { | ||
332 | + return iter; | ||
333 | + } | ||
334 | + } | ||
335 | + | ||
336 | + return NULL; | ||
337 | +} | ||
338 | + | ||
339 | /* Increments the reference count of a ThrottleGroup given its name. | ||
340 | * | ||
341 | * If no ThrottleGroup is found with the given name a new one is | ||
342 | * created. | ||
343 | * | ||
344 | + * This function edits throttle_groups and must be called under the global | ||
345 | + * mutex. | ||
346 | + * | ||
347 | * @name: the name of the ThrottleGroup | ||
348 | * @ret: the ThrottleState member of the ThrottleGroup | ||
349 | */ | ||
350 | ThrottleState *throttle_group_incref(const char *name) | ||
351 | { | ||
352 | ThrottleGroup *tg = NULL; | ||
353 | - ThrottleGroup *iter; | ||
354 | - | ||
355 | - qemu_mutex_lock(&throttle_groups_lock); | ||
356 | |||
357 | /* Look for an existing group with that name */ | ||
358 | - QTAILQ_FOREACH(iter, &throttle_groups, list) { | ||
359 | - if (!strcmp(name, iter->name)) { | ||
360 | - tg = iter; | ||
361 | - break; | ||
362 | - } | ||
363 | - } | ||
364 | - | ||
365 | - /* Create a new one if not found */ | ||
366 | - if (!tg) { | ||
367 | - tg = g_new0(ThrottleGroup, 1); | ||
368 | + tg = throttle_group_by_name(name); | ||
369 | + | ||
370 | + if (tg) { | ||
371 | + object_ref(OBJECT(tg)); | ||
372 | + } else { | ||
373 | + /* Create a new one if not found */ | ||
374 | + /* new ThrottleGroup obj will have a refcnt = 1 */ | ||
375 | + tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP)); | ||
376 | tg->name = g_strdup(name); | ||
377 | - tg->clock_type = QEMU_CLOCK_REALTIME; | ||
378 | - | ||
379 | - if (qtest_enabled()) { | ||
380 | - /* For testing block IO throttling only */ | ||
381 | - tg->clock_type = QEMU_CLOCK_VIRTUAL; | ||
382 | - } | ||
383 | - qemu_mutex_init(&tg->lock); | ||
384 | - throttle_init(&tg->ts); | ||
385 | - QLIST_INIT(&tg->head); | ||
386 | - | ||
387 | - QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); | ||
388 | + throttle_group_obj_complete(USER_CREATABLE(tg), &error_abort); | ||
389 | } | ||
390 | |||
391 | - tg->refcount++; | ||
392 | - | ||
393 | - qemu_mutex_unlock(&throttle_groups_lock); | ||
394 | - | ||
395 | return &tg->ts; | ||
396 | } | ||
397 | |||
398 | @@ -XXX,XX +XXX,XX @@ ThrottleState *throttle_group_incref(const char *name) | ||
399 | * When the reference count reaches zero the ThrottleGroup is | ||
400 | * destroyed. | ||
401 | * | ||
402 | + * This function edits throttle_groups and must be called under the global | ||
403 | + * mutex. | ||
404 | + * | ||
405 | * @ts: The ThrottleGroup to unref, given by its ThrottleState member | ||
406 | */ | ||
407 | void throttle_group_unref(ThrottleState *ts) | ||
408 | { | ||
409 | ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||
410 | - | ||
411 | - qemu_mutex_lock(&throttle_groups_lock); | ||
412 | - if (--tg->refcount == 0) { | ||
413 | - QTAILQ_REMOVE(&throttle_groups, tg, list); | ||
414 | - qemu_mutex_destroy(&tg->lock); | ||
415 | - g_free(tg->name); | ||
416 | - g_free(tg); | ||
417 | - } | ||
418 | - qemu_mutex_unlock(&throttle_groups_lock); | ||
419 | + object_unref(OBJECT(tg)); | ||
420 | } | ||
421 | |||
422 | /* Get the name from a ThrottleGroupMember's group. The name (and the pointer) | ||
423 | @@ -XXX,XX +XXX,XX @@ static void write_timer_cb(void *opaque) | ||
424 | * its timers and updating its throttle_state pointer to point to it. If a | ||
425 | * throttling group with that name does not exist yet, it will be created. | ||
426 | * | ||
427 | + * This function edits throttle_groups and must be called under the global | ||
428 | + * mutex. | ||
429 | + * | ||
430 | * @tgm: the ThrottleGroupMember to insert | ||
431 | * @groupname: the name of the group | ||
432 | * @ctx: the AioContext to use | ||
433 | @@ -XXX,XX +XXX,XX @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm) | ||
434 | tgm->aio_context = NULL; | ||
435 | } | ||
436 | |||
437 | +#undef THROTTLE_OPT_PREFIX | ||
438 | +#define THROTTLE_OPT_PREFIX "x-" | ||
439 | + | ||
440 | +/* Helper struct and array for QOM property setter/getter */ | ||
441 | +typedef struct { | ||
442 | + const char *name; | ||
443 | + BucketType type; | ||
444 | + enum { | ||
445 | + AVG, | ||
446 | + MAX, | ||
447 | + BURST_LENGTH, | ||
448 | + IOPS_SIZE, | ||
449 | + } category; | ||
450 | +} ThrottleParamInfo; | ||
451 | + | ||
452 | +static ThrottleParamInfo properties[] = { | ||
453 | + { | ||
454 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL, | ||
455 | + THROTTLE_OPS_TOTAL, AVG, | ||
456 | + }, | ||
457 | + { | ||
458 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX, | ||
459 | + THROTTLE_OPS_TOTAL, MAX, | ||
460 | + }, | ||
461 | + { | ||
462 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH, | ||
463 | + THROTTLE_OPS_TOTAL, BURST_LENGTH, | ||
464 | + }, | ||
465 | + { | ||
466 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ, | ||
467 | + THROTTLE_OPS_READ, AVG, | ||
468 | + }, | ||
469 | + { | ||
470 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX, | ||
471 | + THROTTLE_OPS_READ, MAX, | ||
472 | + }, | ||
473 | + { | ||
474 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH, | ||
475 | + THROTTLE_OPS_READ, BURST_LENGTH, | ||
476 | + }, | ||
477 | + { | ||
478 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE, | ||
479 | + THROTTLE_OPS_WRITE, AVG, | ||
480 | + }, | ||
481 | + { | ||
482 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX, | ||
483 | + THROTTLE_OPS_WRITE, MAX, | ||
484 | + }, | ||
485 | + { | ||
486 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH, | ||
487 | + THROTTLE_OPS_WRITE, BURST_LENGTH, | ||
488 | + }, | ||
489 | + { | ||
490 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL, | ||
491 | + THROTTLE_BPS_TOTAL, AVG, | ||
492 | + }, | ||
493 | + { | ||
494 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX, | ||
495 | + THROTTLE_BPS_TOTAL, MAX, | ||
496 | + }, | ||
497 | + { | ||
498 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH, | ||
499 | + THROTTLE_BPS_TOTAL, BURST_LENGTH, | ||
500 | + }, | ||
501 | + { | ||
502 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ, | ||
503 | + THROTTLE_BPS_READ, AVG, | ||
504 | + }, | ||
505 | + { | ||
506 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX, | ||
507 | + THROTTLE_BPS_READ, MAX, | ||
508 | + }, | ||
509 | + { | ||
510 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH, | ||
511 | + THROTTLE_BPS_READ, BURST_LENGTH, | ||
512 | + }, | ||
513 | + { | ||
514 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE, | ||
515 | + THROTTLE_BPS_WRITE, AVG, | ||
516 | + }, | ||
517 | + { | ||
518 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX, | ||
519 | + THROTTLE_BPS_WRITE, MAX, | ||
520 | + }, | ||
521 | + { | ||
522 | + THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH, | ||
523 | + THROTTLE_BPS_WRITE, BURST_LENGTH, | ||
524 | + }, | ||
525 | + { | ||
526 | + THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE, | ||
527 | + 0, IOPS_SIZE, | ||
528 | + } | ||
529 | +}; | ||
530 | + | ||
531 | +/* This function edits throttle_groups and must be called under the global | ||
532 | + * mutex */ | ||
533 | +static void throttle_group_obj_init(Object *obj) | ||
534 | +{ | ||
535 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
536 | + | ||
537 | + tg->clock_type = QEMU_CLOCK_REALTIME; | ||
538 | + if (qtest_enabled()) { | ||
539 | + /* For testing block IO throttling only */ | ||
540 | + tg->clock_type = QEMU_CLOCK_VIRTUAL; | ||
541 | + } | ||
542 | + tg->is_initialized = false; | ||
543 | + qemu_mutex_init(&tg->lock); | ||
544 | + throttle_init(&tg->ts); | ||
545 | + QLIST_INIT(&tg->head); | ||
546 | +} | ||
547 | + | ||
548 | +/* This function edits throttle_groups and must be called under the global | ||
549 | + * mutex */ | ||
550 | +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp) | ||
551 | +{ | ||
552 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
553 | + ThrottleConfig cfg; | ||
554 | + | ||
555 | + /* set group name to object id if it exists */ | ||
556 | + if (!tg->name && tg->parent_obj.parent) { | ||
557 | + tg->name = object_get_canonical_path_component(OBJECT(obj)); | ||
558 | + } | ||
559 | + /* We must have a group name at this point */ | ||
560 | + assert(tg->name); | ||
561 | + | ||
562 | + /* error if name is duplicate */ | ||
563 | + if (throttle_group_by_name(tg->name) != NULL) { | ||
564 | + error_setg(errp, "A group with this name already exists"); | ||
565 | + return; | ||
566 | + } | ||
567 | + | ||
568 | + /* check validity */ | ||
569 | + throttle_get_config(&tg->ts, &cfg); | ||
570 | + if (!throttle_is_valid(&cfg, errp)) { | ||
571 | + return; | ||
572 | + } | ||
573 | + throttle_config(&tg->ts, tg->clock_type, &cfg); | ||
574 | + QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); | ||
575 | + tg->is_initialized = true; | ||
576 | +} | ||
577 | + | ||
578 | +/* This function edits throttle_groups and must be called under the global | ||
579 | + * mutex */ | ||
580 | +static void throttle_group_obj_finalize(Object *obj) | ||
581 | +{ | ||
582 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
583 | + if (tg->is_initialized) { | ||
584 | + QTAILQ_REMOVE(&throttle_groups, tg, list); | ||
585 | + } | ||
586 | + qemu_mutex_destroy(&tg->lock); | ||
587 | + g_free(tg->name); | ||
588 | +} | ||
589 | + | ||
590 | +static void throttle_group_set(Object *obj, Visitor *v, const char * name, | ||
591 | + void *opaque, Error **errp) | ||
592 | + | ||
593 | +{ | ||
594 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
595 | + ThrottleConfig *cfg; | ||
596 | + ThrottleParamInfo *info = opaque; | ||
597 | + Error *local_err = NULL; | ||
598 | + int64_t value; | ||
599 | + | ||
600 | + /* If we have finished initialization, don't accept individual property | ||
601 | + * changes through QOM. Throttle configuration limits must be set in one | ||
602 | + * transaction, as certain combinations are invalid. | ||
603 | + */ | ||
604 | + if (tg->is_initialized) { | ||
605 | + error_setg(&local_err, "Property cannot be set after initialization"); | ||
606 | + goto ret; | ||
607 | + } | ||
608 | + | ||
609 | + visit_type_int64(v, name, &value, &local_err); | ||
610 | + if (local_err) { | ||
611 | + goto ret; | ||
612 | + } | ||
613 | + if (value < 0) { | ||
614 | + error_setg(&local_err, "Property values cannot be negative"); | ||
615 | + goto ret; | ||
616 | + } | ||
617 | + | ||
618 | + cfg = &tg->ts.cfg; | ||
619 | + switch (info->category) { | ||
620 | + case AVG: | ||
621 | + cfg->buckets[info->type].avg = value; | ||
622 | + break; | ||
623 | + case MAX: | ||
624 | + cfg->buckets[info->type].max = value; | ||
625 | + break; | ||
626 | + case BURST_LENGTH: | ||
627 | + if (value > UINT_MAX) { | ||
628 | + error_setg(&local_err, "%s value must be in the" | ||
629 | + "range [0, %u]", info->name, UINT_MAX); | ||
630 | + goto ret; | ||
631 | + } | ||
632 | + cfg->buckets[info->type].burst_length = value; | ||
633 | + break; | ||
634 | + case IOPS_SIZE: | ||
635 | + cfg->op_size = value; | ||
636 | + break; | ||
637 | + } | ||
638 | + | ||
639 | +ret: | ||
640 | + error_propagate(errp, local_err); | ||
641 | + return; | ||
642 | + | ||
643 | +} | ||
644 | + | ||
645 | +static void throttle_group_get(Object *obj, Visitor *v, const char *name, | ||
646 | + void *opaque, Error **errp) | ||
647 | +{ | ||
648 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
649 | + ThrottleConfig cfg; | ||
650 | + ThrottleParamInfo *info = opaque; | ||
651 | + int64_t value; | ||
652 | + | ||
653 | + throttle_get_config(&tg->ts, &cfg); | ||
654 | + switch (info->category) { | ||
655 | + case AVG: | ||
656 | + value = cfg.buckets[info->type].avg; | ||
657 | + break; | ||
658 | + case MAX: | ||
659 | + value = cfg.buckets[info->type].max; | ||
660 | + break; | ||
661 | + case BURST_LENGTH: | ||
662 | + value = cfg.buckets[info->type].burst_length; | ||
663 | + break; | ||
664 | + case IOPS_SIZE: | ||
665 | + value = cfg.op_size; | ||
666 | + break; | ||
667 | + } | ||
668 | + | ||
669 | + visit_type_int64(v, name, &value, errp); | ||
670 | +} | ||
671 | + | ||
672 | +static void throttle_group_set_limits(Object *obj, Visitor *v, | ||
673 | + const char *name, void *opaque, | ||
674 | + Error **errp) | ||
675 | + | ||
676 | +{ | ||
677 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
678 | + ThrottleConfig cfg; | ||
679 | + ThrottleLimits arg = { 0 }; | ||
680 | + ThrottleLimits *argp = &arg; | ||
681 | + Error *local_err = NULL; | ||
682 | + | ||
683 | + visit_type_ThrottleLimits(v, name, &argp, &local_err); | ||
684 | + if (local_err) { | ||
685 | + goto ret; | ||
686 | + } | ||
687 | + qemu_mutex_lock(&tg->lock); | ||
688 | + throttle_get_config(&tg->ts, &cfg); | ||
689 | + throttle_limits_to_config(argp, &cfg, &local_err); | ||
690 | + if (local_err) { | ||
691 | + goto unlock; | ||
692 | + } | ||
693 | + throttle_config(&tg->ts, tg->clock_type, &cfg); | ||
694 | + | ||
695 | +unlock: | ||
696 | + qemu_mutex_unlock(&tg->lock); | ||
697 | +ret: | ||
698 | + error_propagate(errp, local_err); | ||
699 | + return; | ||
700 | +} | ||
701 | + | ||
702 | +static void throttle_group_get_limits(Object *obj, Visitor *v, | ||
703 | + const char *name, void *opaque, | ||
704 | + Error **errp) | ||
705 | +{ | ||
706 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | ||
707 | + ThrottleConfig cfg; | ||
708 | + ThrottleLimits arg = { 0 }; | ||
709 | + ThrottleLimits *argp = &arg; | ||
710 | + | ||
711 | + qemu_mutex_lock(&tg->lock); | ||
712 | + throttle_get_config(&tg->ts, &cfg); | ||
713 | + qemu_mutex_unlock(&tg->lock); | ||
714 | + | ||
715 | + throttle_config_to_limits(&cfg, argp); | ||
716 | + | ||
717 | + visit_type_ThrottleLimits(v, name, &argp, errp); | ||
718 | +} | ||
719 | + | ||
720 | +static bool throttle_group_can_be_deleted(UserCreatable *uc) | ||
721 | +{ | ||
722 | + return OBJECT(uc)->ref == 1; | ||
723 | +} | ||
724 | + | ||
725 | +static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data) | ||
726 | +{ | ||
727 | + size_t i = 0; | ||
728 | + UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); | ||
729 | + | ||
730 | + ucc->complete = throttle_group_obj_complete; | ||
731 | + ucc->can_be_deleted = throttle_group_can_be_deleted; | ||
732 | + | ||
733 | + /* individual properties */ | ||
734 | + for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) { | ||
735 | + object_class_property_add(klass, | ||
736 | + properties[i].name, | ||
737 | + "int", | ||
738 | + throttle_group_get, | ||
739 | + throttle_group_set, | ||
740 | + NULL, &properties[i], | ||
741 | + &error_abort); | ||
742 | + } | ||
743 | + | ||
744 | + /* ThrottleLimits */ | ||
745 | + object_class_property_add(klass, | ||
746 | + "limits", "ThrottleLimits", | ||
747 | + throttle_group_get_limits, | ||
748 | + throttle_group_set_limits, | ||
749 | + NULL, NULL, | ||
750 | + &error_abort); | ||
751 | +} | ||
752 | + | ||
753 | +static const TypeInfo throttle_group_info = { | ||
754 | + .name = TYPE_THROTTLE_GROUP, | ||
755 | + .parent = TYPE_OBJECT, | ||
756 | + .class_init = throttle_group_obj_class_init, | ||
757 | + .instance_size = sizeof(ThrottleGroup), | ||
758 | + .instance_init = throttle_group_obj_init, | ||
759 | + .instance_finalize = throttle_group_obj_finalize, | ||
760 | + .interfaces = (InterfaceInfo[]) { | ||
761 | + { TYPE_USER_CREATABLE }, | ||
762 | + { } | ||
763 | + }, | ||
764 | +}; | ||
765 | + | ||
766 | static void throttle_groups_init(void) | ||
767 | { | ||
768 | - qemu_mutex_init(&throttle_groups_lock); | ||
769 | + type_register_static(&throttle_group_info); | ||
770 | } | ||
771 | |||
772 | -block_init(throttle_groups_init); | ||
773 | +type_init(throttle_groups_init); | ||
774 | diff --git a/tests/test-throttle.c b/tests/test-throttle.c | ||
775 | index XXXXXXX..XXXXXXX 100644 | ||
776 | --- a/tests/test-throttle.c | ||
777 | +++ b/tests/test-throttle.c | ||
778 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
779 | qemu_init_main_loop(&error_fatal); | ||
780 | ctx = qemu_get_aio_context(); | ||
781 | bdrv_init(); | ||
782 | + module_call_init(MODULE_INIT_QOM); | ||
783 | |||
784 | do {} while (g_main_context_iteration(NULL, false)); | ||
785 | |||
786 | diff --git a/util/throttle.c b/util/throttle.c | ||
787 | index XXXXXXX..XXXXXXX 100644 | ||
788 | --- a/util/throttle.c | ||
789 | +++ b/util/throttle.c | ||
790 | @@ -XXX,XX +XXX,XX @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size) | ||
791 | } | ||
792 | } | ||
793 | |||
794 | +/* return a ThrottleConfig based on the options in a ThrottleLimits | ||
795 | + * | ||
796 | + * @arg: the ThrottleLimits object to read from | ||
797 | + * @cfg: the ThrottleConfig to edit | ||
798 | + * @errp: error object | ||
799 | + */ | ||
800 | +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg, | ||
801 | + Error **errp) | ||
802 | +{ | ||
803 | + if (arg->has_bps_total) { | ||
804 | + cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total; | ||
805 | + } | ||
806 | + if (arg->has_bps_read) { | ||
807 | + cfg->buckets[THROTTLE_BPS_READ].avg = arg->bps_read; | ||
808 | + } | ||
809 | + if (arg->has_bps_write) { | ||
810 | + cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write; | ||
811 | + } | ||
812 | + | ||
813 | + if (arg->has_iops_total) { | ||
814 | + cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total; | ||
815 | + } | ||
816 | + if (arg->has_iops_read) { | ||
817 | + cfg->buckets[THROTTLE_OPS_READ].avg = arg->iops_read; | ||
818 | + } | ||
819 | + if (arg->has_iops_write) { | ||
820 | + cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write; | ||
821 | + } | ||
822 | + | ||
823 | + if (arg->has_bps_total_max) { | ||
824 | + cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max; | ||
825 | + } | ||
826 | + if (arg->has_bps_read_max) { | ||
827 | + cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max; | ||
828 | + } | ||
829 | + if (arg->has_bps_write_max) { | ||
830 | + cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max; | ||
831 | + } | ||
832 | + if (arg->has_iops_total_max) { | ||
833 | + cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max; | ||
834 | + } | ||
835 | + if (arg->has_iops_read_max) { | ||
836 | + cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max; | ||
837 | + } | ||
838 | + if (arg->has_iops_write_max) { | ||
839 | + cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max; | ||
840 | + } | ||
841 | + | ||
842 | + if (arg->has_bps_total_max_length) { | ||
843 | + if (arg->bps_total_max_length > UINT_MAX) { | ||
844 | + error_setg(errp, "bps-total-max-length value must be in" | ||
845 | + " the range [0, %u]", UINT_MAX); | ||
846 | + return; | ||
847 | + } | ||
848 | + cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_total_max_length; | ||
849 | + } | ||
850 | + if (arg->has_bps_read_max_length) { | ||
851 | + if (arg->bps_read_max_length > UINT_MAX) { | ||
852 | + error_setg(errp, "bps-read-max-length value must be in" | ||
853 | + " the range [0, %u]", UINT_MAX); | ||
854 | + return; | ||
855 | + } | ||
856 | + cfg->buckets[THROTTLE_BPS_READ].burst_length = arg->bps_read_max_length; | ||
857 | + } | ||
858 | + if (arg->has_bps_write_max_length) { | ||
859 | + if (arg->bps_write_max_length > UINT_MAX) { | ||
860 | + error_setg(errp, "bps-write-max-length value must be in" | ||
861 | + " the range [0, %u]", UINT_MAX); | ||
862 | + return; | ||
863 | + } | ||
864 | + cfg->buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_write_max_length; | ||
865 | + } | ||
866 | + if (arg->has_iops_total_max_length) { | ||
867 | + if (arg->iops_total_max_length > UINT_MAX) { | ||
868 | + error_setg(errp, "iops-total-max-length value must be in" | ||
869 | + " the range [0, %u]", UINT_MAX); | ||
870 | + return; | ||
871 | + } | ||
872 | + cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_total_max_length; | ||
873 | + } | ||
874 | + if (arg->has_iops_read_max_length) { | ||
875 | + if (arg->iops_read_max_length > UINT_MAX) { | ||
876 | + error_setg(errp, "iops-read-max-length value must be in" | ||
877 | + " the range [0, %u]", UINT_MAX); | ||
878 | + return; | ||
879 | + } | ||
880 | + cfg->buckets[THROTTLE_OPS_READ].burst_length = arg->iops_read_max_length; | ||
881 | + } | ||
882 | + if (arg->has_iops_write_max_length) { | ||
883 | + if (arg->iops_write_max_length > UINT_MAX) { | ||
884 | + error_setg(errp, "iops-write-max-length value must be in" | ||
885 | + " the range [0, %u]", UINT_MAX); | ||
886 | + return; | ||
887 | + } | ||
888 | + cfg->buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_write_max_length; | ||
889 | + } | ||
890 | + | ||
891 | + if (arg->has_iops_size) { | ||
892 | + cfg->op_size = arg->iops_size; | ||
893 | + } | ||
894 | + | ||
895 | + throttle_is_valid(cfg, errp); | ||
896 | +} | ||
897 | + | ||
898 | +/* write the options of a ThrottleConfig to a ThrottleLimits | ||
899 | + * | ||
900 | + * @cfg: the ThrottleConfig to read from | ||
901 | + * @var: the ThrottleLimits to write to | ||
902 | + */ | ||
903 | +void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var) | ||
904 | +{ | ||
905 | + var->bps_total = cfg->buckets[THROTTLE_BPS_TOTAL].avg; | ||
906 | + var->bps_read = cfg->buckets[THROTTLE_BPS_READ].avg; | ||
907 | + var->bps_write = cfg->buckets[THROTTLE_BPS_WRITE].avg; | ||
908 | + var->iops_total = cfg->buckets[THROTTLE_OPS_TOTAL].avg; | ||
909 | + var->iops_read = cfg->buckets[THROTTLE_OPS_READ].avg; | ||
910 | + var->iops_write = cfg->buckets[THROTTLE_OPS_WRITE].avg; | ||
911 | + var->bps_total_max = cfg->buckets[THROTTLE_BPS_TOTAL].max; | ||
912 | + var->bps_read_max = cfg->buckets[THROTTLE_BPS_READ].max; | ||
913 | + var->bps_write_max = cfg->buckets[THROTTLE_BPS_WRITE].max; | ||
914 | + var->iops_total_max = cfg->buckets[THROTTLE_OPS_TOTAL].max; | ||
915 | + var->iops_read_max = cfg->buckets[THROTTLE_OPS_READ].max; | ||
916 | + var->iops_write_max = cfg->buckets[THROTTLE_OPS_WRITE].max; | ||
917 | + var->bps_total_max_length = cfg->buckets[THROTTLE_BPS_TOTAL].burst_length; | ||
918 | + var->bps_read_max_length = cfg->buckets[THROTTLE_BPS_READ].burst_length; | ||
919 | + var->bps_write_max_length = cfg->buckets[THROTTLE_BPS_WRITE].burst_length; | ||
920 | + var->iops_total_max_length = cfg->buckets[THROTTLE_OPS_TOTAL].burst_length; | ||
921 | + var->iops_read_max_length = cfg->buckets[THROTTLE_OPS_READ].burst_length; | ||
922 | + var->iops_write_max_length = cfg->buckets[THROTTLE_OPS_WRITE].burst_length; | ||
923 | + var->iops_size = cfg->op_size; | ||
924 | + | ||
925 | + var->has_bps_total = true; | ||
926 | + var->has_bps_read = true; | ||
927 | + var->has_bps_write = true; | ||
928 | + var->has_iops_total = true; | ||
929 | + var->has_iops_read = true; | ||
930 | + var->has_iops_write = true; | ||
931 | + var->has_bps_total_max = true; | ||
932 | + var->has_bps_read_max = true; | ||
933 | + var->has_bps_write_max = true; | ||
934 | + var->has_iops_total_max = true; | ||
935 | + var->has_iops_read_max = true; | ||
936 | + var->has_iops_write_max = true; | ||
937 | + var->has_bps_read_max_length = true; | ||
938 | + var->has_bps_total_max_length = true; | ||
939 | + var->has_bps_write_max_length = true; | ||
940 | + var->has_iops_total_max_length = true; | ||
941 | + var->has_iops_read_max_length = true; | ||
942 | + var->has_iops_write_max_length = true; | ||
943 | + var->has_iops_size = true; | ||
944 | +} | ||
945 | -- | 25 | -- |
946 | 2.13.5 | 26 | 2.13.6 |
947 | 27 | ||
948 | 28 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Ari Sundholm <ari@tuxera.com> | ||
1 | 2 | ||
3 | The sector size needs to be large enough to accommodate the data | ||
4 | structures for the log super block and log write entries. This was | ||
5 | previously not properly checked, which made it possible to cause | ||
6 | QEMU to badly misbehave. | ||
7 | |||
8 | Signed-off-by: Ari Sundholm <ari@tuxera.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | block/blklogwrites.c | 5 ++++- | ||
12 | 1 file changed, 4 insertions(+), 1 deletion(-) | ||
13 | |||
14 | diff --git a/block/blklogwrites.c b/block/blklogwrites.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/blklogwrites.c | ||
17 | +++ b/block/blklogwrites.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static inline uint32_t blk_log_writes_log2(uint32_t value) | ||
19 | |||
20 | static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size) | ||
21 | { | ||
22 | - return sector_size < (1ull << 24) && is_power_of_2(sector_size); | ||
23 | + return is_power_of_2(sector_size) && | ||
24 | + sector_size >= sizeof(struct log_write_super) && | ||
25 | + sector_size >= sizeof(struct log_write_entry) && | ||
26 | + sector_size < (1ull << 24); | ||
27 | } | ||
28 | |||
29 | static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, | ||
30 | -- | ||
31 | 2.13.6 | ||
32 | |||
33 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Cornelia Huck <cohuck@redhat.com> | ||
1 | 2 | ||
3 | This reverts commit 6266e900b8083945cb766b45c124fb3c42932cb3. | ||
4 | |||
5 | Some deprecated -drive options were still in use by libvirt, only | ||
6 | fixed with libvirt commit b340c6c614 ("qemu: format serial and geometry | ||
7 | on frontend disk device"), which is not yet in any released version | ||
8 | of libvirt. | ||
9 | |||
10 | So let's hold off removing the deprecated options for one more QEMU | ||
11 | release. | ||
12 | |||
13 | Reported-by: Christian Borntraeger <borntraeger@de.ibm.com> | ||
14 | Signed-off-by: Cornelia Huck <cohuck@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | --- | ||
17 | blockdev.c | 12 ++++++++++++ | ||
18 | 1 file changed, 12 insertions(+) | ||
19 | |||
20 | diff --git a/blockdev.c b/blockdev.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/blockdev.c | ||
23 | +++ b/blockdev.c | ||
24 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
25 | const char *filename; | ||
26 | Error *local_err = NULL; | ||
27 | int i; | ||
28 | + const char *deprecated[] = { | ||
29 | + }; | ||
30 | |||
31 | /* Change legacy command line options into QMP ones */ | ||
32 | static const struct { | ||
33 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
34 | goto fail; | ||
35 | } | ||
36 | |||
37 | + /* Other deprecated options */ | ||
38 | + if (!qtest_enabled()) { | ||
39 | + for (i = 0; i < ARRAY_SIZE(deprecated); i++) { | ||
40 | + if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) { | ||
41 | + error_report("'%s' is deprecated, please use the corresponding " | ||
42 | + "option of '-device' instead", deprecated[i]); | ||
43 | + } | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | /* Media type */ | ||
48 | value = qemu_opt_get(legacy_opts, "media"); | ||
49 | if (value) { | ||
50 | -- | ||
51 | 2.13.6 | ||
52 | |||
53 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Cornelia Huck <cohuck@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Move the CoMutex and CoQueue inits inside throttle_group_register_tgm() | 3 | This reverts commit b0083267444a5e0f28391f6c2831a539f878d424. |
4 | which is called whenever a ThrottleGroupMember is initialized. There's | 4 | |
5 | no need for them to be separate. | 5 | Hold off removing this for one more QEMU release (current libvirt |
6 | 6 | release still uses it.) | |
7 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 7 | |
8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 8 | Signed-off-by: Cornelia Huck <cohuck@redhat.com> |
9 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 10 | --- |
12 | block/block-backend.c | 3 --- | 11 | include/hw/block/block.h | 1 + |
13 | block/throttle-groups.c | 3 +++ | 12 | include/sysemu/blockdev.h | 1 + |
14 | 2 files changed, 3 insertions(+), 3 deletions(-) | 13 | block/block-backend.c | 1 + |
15 | 14 | blockdev.c | 10 ++++++++++ | |
15 | hw/block/block.c | 13 +++++++++++++ | ||
16 | hw/block/nvme.c | 1 + | ||
17 | hw/block/virtio-blk.c | 1 + | ||
18 | hw/ide/qdev.c | 1 + | ||
19 | hw/scsi/scsi-disk.c | 1 + | ||
20 | hw/usb/dev-storage.c | 1 + | ||
21 | tests/ahci-test.c | 6 +++--- | ||
22 | tests/ide-test.c | 8 ++++---- | ||
23 | qemu-doc.texi | 5 +++++ | ||
24 | qemu-options.hx | 6 +++++- | ||
25 | 14 files changed, 48 insertions(+), 8 deletions(-) | ||
26 | |||
27 | diff --git a/include/hw/block/block.h b/include/hw/block/block.h | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/include/hw/block/block.h | ||
30 | +++ b/include/hw/block/block.h | ||
31 | @@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) | ||
32 | |||
33 | /* Configuration helpers */ | ||
34 | |||
35 | +void blkconf_serial(BlockConf *conf, char **serial); | ||
36 | bool blkconf_geometry(BlockConf *conf, int *trans, | ||
37 | unsigned cyls_max, unsigned heads_max, unsigned secs_max, | ||
38 | Error **errp); | ||
39 | diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/include/sysemu/blockdev.h | ||
42 | +++ b/include/sysemu/blockdev.h | ||
43 | @@ -XXX,XX +XXX,XX @@ struct DriveInfo { | ||
44 | bool is_default; /* Added by default_drive() ? */ | ||
45 | int media_cd; | ||
46 | QemuOpts *opts; | ||
47 | + char *serial; | ||
48 | QTAILQ_ENTRY(DriveInfo) next; | ||
49 | }; | ||
50 | |||
16 | diff --git a/block/block-backend.c b/block/block-backend.c | 51 | diff --git a/block/block-backend.c b/block/block-backend.c |
17 | index XXXXXXX..XXXXXXX 100644 | 52 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/block-backend.c | 53 | --- a/block/block-backend.c |
19 | +++ b/block/block-backend.c | 54 | +++ b/block/block-backend.c |
20 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | 55 | @@ -XXX,XX +XXX,XX @@ static void drive_info_del(DriveInfo *dinfo) |
21 | blk->shared_perm = shared_perm; | 56 | return; |
22 | blk_set_enable_write_cache(blk, true); | 57 | } |
23 | 58 | qemu_opts_del(dinfo->opts); | |
24 | - qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock); | 59 | + g_free(dinfo->serial); |
25 | - qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]); | 60 | g_free(dinfo); |
26 | - qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]); | ||
27 | block_acct_init(&blk->stats); | ||
28 | |||
29 | notifier_list_init(&blk->remove_bs_notifiers); | ||
30 | diff --git a/block/throttle-groups.c b/block/throttle-groups.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | ||
32 | --- a/block/throttle-groups.c | ||
33 | +++ b/block/throttle-groups.c | ||
34 | @@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, | ||
35 | read_timer_cb, | ||
36 | write_timer_cb, | ||
37 | tgm); | ||
38 | + qemu_co_mutex_init(&tgm->throttled_reqs_lock); | ||
39 | + qemu_co_queue_init(&tgm->throttled_reqs[0]); | ||
40 | + qemu_co_queue_init(&tgm->throttled_reqs[1]); | ||
41 | |||
42 | qemu_mutex_unlock(&tg->lock); | ||
43 | } | 61 | } |
62 | |||
63 | diff --git a/blockdev.c b/blockdev.c | ||
64 | index XXXXXXX..XXXXXXX 100644 | ||
65 | --- a/blockdev.c | ||
66 | +++ b/blockdev.c | ||
67 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { | ||
68 | .type = QEMU_OPT_STRING, | ||
69 | .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", | ||
70 | },{ | ||
71 | + .name = "serial", | ||
72 | + .type = QEMU_OPT_STRING, | ||
73 | + .help = "disk serial number", | ||
74 | + },{ | ||
75 | .name = "file", | ||
76 | .type = QEMU_OPT_STRING, | ||
77 | .help = "file name", | ||
78 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
79 | const char *werror, *rerror; | ||
80 | bool read_only = false; | ||
81 | bool copy_on_read; | ||
82 | + const char *serial; | ||
83 | const char *filename; | ||
84 | Error *local_err = NULL; | ||
85 | int i; | ||
86 | const char *deprecated[] = { | ||
87 | + "serial" | ||
88 | }; | ||
89 | |||
90 | /* Change legacy command line options into QMP ones */ | ||
91 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
92 | goto fail; | ||
93 | } | ||
94 | |||
95 | + /* Serial number */ | ||
96 | + serial = qemu_opt_get(legacy_opts, "serial"); | ||
97 | + | ||
98 | /* no id supplied -> create one */ | ||
99 | if (qemu_opts_id(all_opts) == NULL) { | ||
100 | char *new_id; | ||
101 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
102 | dinfo->type = type; | ||
103 | dinfo->bus = bus_id; | ||
104 | dinfo->unit = unit_id; | ||
105 | + dinfo->serial = g_strdup(serial); | ||
106 | |||
107 | blk_set_legacy_dinfo(blk, dinfo); | ||
108 | |||
109 | diff --git a/hw/block/block.c b/hw/block/block.c | ||
110 | index XXXXXXX..XXXXXXX 100644 | ||
111 | --- a/hw/block/block.c | ||
112 | +++ b/hw/block/block.c | ||
113 | @@ -XXX,XX +XXX,XX @@ | ||
114 | #include "qapi/qapi-types-block.h" | ||
115 | #include "qemu/error-report.h" | ||
116 | |||
117 | +void blkconf_serial(BlockConf *conf, char **serial) | ||
118 | +{ | ||
119 | + DriveInfo *dinfo; | ||
120 | + | ||
121 | + if (!*serial) { | ||
122 | + /* try to fall back to value set with legacy -drive serial=... */ | ||
123 | + dinfo = blk_legacy_dinfo(conf->blk); | ||
124 | + if (dinfo) { | ||
125 | + *serial = g_strdup(dinfo->serial); | ||
126 | + } | ||
127 | + } | ||
128 | +} | ||
129 | + | ||
130 | void blkconf_blocksizes(BlockConf *conf) | ||
131 | { | ||
132 | BlockBackend *blk = conf->blk; | ||
133 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | ||
134 | index XXXXXXX..XXXXXXX 100644 | ||
135 | --- a/hw/block/nvme.c | ||
136 | +++ b/hw/block/nvme.c | ||
137 | @@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | + blkconf_serial(&n->conf, &n->serial); | ||
142 | if (!n->serial) { | ||
143 | error_setg(errp, "serial property not set"); | ||
144 | return; | ||
145 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
146 | index XXXXXXX..XXXXXXX 100644 | ||
147 | --- a/hw/block/virtio-blk.c | ||
148 | +++ b/hw/block/virtio-blk.c | ||
149 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | + blkconf_serial(&conf->conf, &conf->serial); | ||
154 | if (!blkconf_apply_backend_options(&conf->conf, | ||
155 | blk_is_read_only(conf->conf.blk), true, | ||
156 | errp)) { | ||
157 | diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c | ||
158 | index XXXXXXX..XXXXXXX 100644 | ||
159 | --- a/hw/ide/qdev.c | ||
160 | +++ b/hw/ide/qdev.c | ||
161 | @@ -XXX,XX +XXX,XX @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) | ||
162 | return; | ||
163 | } | ||
164 | |||
165 | + blkconf_serial(&dev->conf, &dev->serial); | ||
166 | if (kind != IDE_CD) { | ||
167 | if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, | ||
168 | errp)) { | ||
169 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | ||
170 | index XXXXXXX..XXXXXXX 100644 | ||
171 | --- a/hw/scsi/scsi-disk.c | ||
172 | +++ b/hw/scsi/scsi-disk.c | ||
173 | @@ -XXX,XX +XXX,XX @@ static void scsi_realize(SCSIDevice *dev, Error **errp) | ||
174 | return; | ||
175 | } | ||
176 | |||
177 | + blkconf_serial(&s->qdev.conf, &s->serial); | ||
178 | blkconf_blocksizes(&s->qdev.conf); | ||
179 | |||
180 | if (s->qdev.conf.logical_block_size > | ||
181 | diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c | ||
182 | index XXXXXXX..XXXXXXX 100644 | ||
183 | --- a/hw/usb/dev-storage.c | ||
184 | +++ b/hw/usb/dev-storage.c | ||
185 | @@ -XXX,XX +XXX,XX @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) | ||
186 | return; | ||
187 | } | ||
188 | |||
189 | + blkconf_serial(&s->conf, &dev->serial); | ||
190 | blkconf_blocksizes(&s->conf); | ||
191 | if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, | ||
192 | errp)) { | ||
193 | diff --git a/tests/ahci-test.c b/tests/ahci-test.c | ||
194 | index XXXXXXX..XXXXXXX 100644 | ||
195 | --- a/tests/ahci-test.c | ||
196 | +++ b/tests/ahci-test.c | ||
197 | @@ -XXX,XX +XXX,XX @@ static AHCIQState *ahci_boot(const char *cli, ...) | ||
198 | s = ahci_vboot(cli, ap); | ||
199 | va_end(ap); | ||
200 | } else { | ||
201 | - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" | ||
202 | + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" | ||
203 | + ",format=%s" | ||
204 | " -M q35 " | ||
205 | "-device ide-hd,drive=drive0 " | ||
206 | - "-global ide-hd.serial=%s " | ||
207 | "-global ide-hd.ver=%s"; | ||
208 | - s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); | ||
209 | + s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); | ||
210 | } | ||
211 | |||
212 | return s; | ||
213 | diff --git a/tests/ide-test.c b/tests/ide-test.c | ||
214 | index XXXXXXX..XXXXXXX 100644 | ||
215 | --- a/tests/ide-test.c | ||
216 | +++ b/tests/ide-test.c | ||
217 | @@ -XXX,XX +XXX,XX @@ static void test_bmdma_no_busmaster(void) | ||
218 | static void test_bmdma_setup(void) | ||
219 | { | ||
220 | ide_test_start( | ||
221 | - "-drive file=%s,if=ide,cache=writeback,format=raw " | ||
222 | - "-global ide-hd.serial=%s -global ide-hd.ver=%s", | ||
223 | + "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " | ||
224 | + "-global ide-hd.ver=%s", | ||
225 | tmp_path, "testdisk", "version"); | ||
226 | qtest_irq_intercept_in(global_qtest, "ioapic"); | ||
227 | } | ||
228 | @@ -XXX,XX +XXX,XX @@ static void test_identify(void) | ||
229 | int ret; | ||
230 | |||
231 | ide_test_start( | ||
232 | - "-drive file=%s,if=ide,cache=writeback,format=raw " | ||
233 | - "-global ide-hd.serial=%s -global ide-hd.ver=%s", | ||
234 | + "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " | ||
235 | + "-global ide-hd.ver=%s", | ||
236 | tmp_path, "testdisk", "version"); | ||
237 | |||
238 | dev = get_pci_device(&bmdma_bar, &ide_bar); | ||
239 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
240 | index XXXXXXX..XXXXXXX 100644 | ||
241 | --- a/qemu-doc.texi | ||
242 | +++ b/qemu-doc.texi | ||
243 | @@ -XXX,XX +XXX,XX @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' | ||
244 | (for embedded NICs). The new syntax allows different settings to be | ||
245 | provided per NIC. | ||
246 | |||
247 | +@subsection -drive serial=... (since 2.10.0) | ||
248 | + | ||
249 | +The drive serial argument is replaced by the the serial argument | ||
250 | +that can be specified with the ``-device'' parameter. | ||
251 | + | ||
252 | @subsection -usbdevice (since 2.10.0) | ||
253 | |||
254 | The ``-usbdevice DEV'' argument is now a synonym for setting | ||
255 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
256 | index XXXXXXX..XXXXXXX 100644 | ||
257 | --- a/qemu-options.hx | ||
258 | +++ b/qemu-options.hx | ||
259 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
260 | DEF("drive", HAS_ARG, QEMU_OPTION_drive, | ||
261 | "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" | ||
262 | " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" | ||
263 | - " [,snapshot=on|off][,rerror=ignore|stop|report]\n" | ||
264 | + " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" | ||
265 | " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||
266 | " [,readonly=on|off][,copy-on-read=on|off]\n" | ||
267 | " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" | ||
268 | @@ -XXX,XX +XXX,XX @@ The default mode is @option{cache=writeback}. | ||
269 | Specify which disk @var{format} will be used rather than detecting | ||
270 | the format. Can be used to specify format=raw to avoid interpreting | ||
271 | an untrusted format header. | ||
272 | +@item serial=@var{serial} | ||
273 | +This option specifies the serial number to assign to the device. This | ||
274 | +parameter is deprecated, use the corresponding parameter of @code{-device} | ||
275 | +instead. | ||
276 | @item werror=@var{action},rerror=@var{action} | ||
277 | Specify which @var{action} to take on write and read errors. Valid actions are: | ||
278 | "ignore" (ignore the error and try to continue), "stop" (pause QEMU), | ||
44 | -- | 279 | -- |
45 | 2.13.5 | 280 | 2.13.6 |
46 | 281 | ||
47 | 282 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Cornelia Huck <cohuck@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The following functions fail if bs->drv is a filter and does not | 3 | This reverts commit eae3bd1eb7c6b105d30ec06008b3bc3dfc5f45bb. |
4 | implement them: | ||
5 | 4 | ||
6 | bdrv_probe_blocksizes | 5 | Reverted to avoid conflicts for geometry options revert. |
7 | bdrv_probe_geometry | ||
8 | bdrv_truncate | ||
9 | bdrv_has_zero_init | ||
10 | bdrv_get_info | ||
11 | 6 | ||
12 | Instead, the call should be passed to bs->file if it exists, to allow | 7 | Signed-off-by: Cornelia Huck <cohuck@redhat.com> |
13 | filter drivers to support those methods without implementing them. This | ||
14 | commit makes `drv->is_filter = true` imply that these callbacks will be | ||
15 | forwarded to bs->file by default, so disabling support for these | ||
16 | functions must be done explicitly. | ||
17 | |||
18 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
19 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
20 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
22 | --- | 9 | --- |
23 | include/block/block_int.h | 6 +++++- | 10 | include/sysemu/blockdev.h | 1 + |
24 | block.c | 21 +++++++++++++++++++-- | 11 | blockdev.c | 17 ++++++++++++++++- |
25 | 2 files changed, 24 insertions(+), 3 deletions(-) | 12 | device-hotplug.c | 4 ++++ |
13 | qemu-doc.texi | 5 +++++ | ||
14 | qemu-options.hx | 5 ++++- | ||
15 | 5 files changed, 30 insertions(+), 2 deletions(-) | ||
26 | 16 | ||
27 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 17 | diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h |
28 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/include/block/block_int.h | 19 | --- a/include/sysemu/blockdev.h |
30 | +++ b/include/block/block_int.h | 20 | +++ b/include/sysemu/blockdev.h |
31 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 21 | @@ -XXX,XX +XXX,XX @@ typedef enum { |
32 | const char *format_name; | 22 | } BlockInterfaceType; |
33 | int instance_size; | 23 | |
34 | 24 | struct DriveInfo { | |
35 | - /* set to true if the BlockDriver is a block filter */ | 25 | + const char *devaddr; |
36 | + /* set to true if the BlockDriver is a block filter. Block filters pass | 26 | BlockInterfaceType type; |
37 | + * certain callbacks that refer to data (see block.c) to their bs->file if | 27 | int bus; |
38 | + * the driver doesn't implement them. Drivers that do not wish to forward | 28 | int unit; |
39 | + * must implement them and return -ENOTSUP. | 29 | diff --git a/blockdev.c b/blockdev.c |
40 | + */ | ||
41 | bool is_filter; | ||
42 | /* for snapshots block filter like Quorum can implement the | ||
43 | * following recursive callback. | ||
44 | diff --git a/block.c b/block.c | ||
45 | index XXXXXXX..XXXXXXX 100644 | 30 | index XXXXXXX..XXXXXXX 100644 |
46 | --- a/block.c | 31 | --- a/blockdev.c |
47 | +++ b/block.c | 32 | +++ b/blockdev.c |
48 | @@ -XXX,XX +XXX,XX @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) | 33 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { |
49 | 34 | .type = QEMU_OPT_STRING, | |
50 | if (drv && drv->bdrv_probe_blocksizes) { | 35 | .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", |
51 | return drv->bdrv_probe_blocksizes(bs, bsz); | 36 | },{ |
52 | + } else if (drv && drv->is_filter && bs->file) { | 37 | + .name = "addr", |
53 | + return bdrv_probe_blocksizes(bs->file->bs, bsz); | 38 | + .type = QEMU_OPT_STRING, |
39 | + .help = "pci address (virtio only)", | ||
40 | + },{ | ||
41 | .name = "serial", | ||
42 | .type = QEMU_OPT_STRING, | ||
43 | .help = "disk serial number", | ||
44 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
45 | DriveMediaType media = MEDIA_DISK; | ||
46 | BlockInterfaceType type; | ||
47 | int max_devs, bus_id, unit_id, index; | ||
48 | + const char *devaddr; | ||
49 | const char *werror, *rerror; | ||
50 | bool read_only = false; | ||
51 | bool copy_on_read; | ||
52 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
53 | Error *local_err = NULL; | ||
54 | int i; | ||
55 | const char *deprecated[] = { | ||
56 | - "serial" | ||
57 | + "serial", "addr" | ||
58 | }; | ||
59 | |||
60 | /* Change legacy command line options into QMP ones */ | ||
61 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
54 | } | 62 | } |
55 | 63 | ||
56 | return -ENOTSUP; | 64 | /* Add virtio block device */ |
57 | @@ -XXX,XX +XXX,XX @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | 65 | + devaddr = qemu_opt_get(legacy_opts, "addr"); |
58 | 66 | + if (devaddr && type != IF_VIRTIO) { | |
59 | if (drv && drv->bdrv_probe_geometry) { | 67 | + error_report("addr is not supported by this bus type"); |
60 | return drv->bdrv_probe_geometry(bs, geo); | 68 | + goto fail; |
61 | + } else if (drv && drv->is_filter && bs->file) { | 69 | + } |
62 | + return bdrv_probe_geometry(bs->file->bs, geo); | 70 | + |
71 | if (type == IF_VIRTIO) { | ||
72 | QemuOpts *devopts; | ||
73 | devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, | ||
74 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
75 | } | ||
76 | qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), | ||
77 | &error_abort); | ||
78 | + if (devaddr) { | ||
79 | + qemu_opt_set(devopts, "addr", devaddr, &error_abort); | ||
80 | + } | ||
63 | } | 81 | } |
64 | 82 | ||
65 | return -ENOTSUP; | 83 | filename = qemu_opt_get(legacy_opts, "file"); |
66 | @@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, | 84 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) |
67 | 85 | dinfo->type = type; | |
68 | assert(child->perm & BLK_PERM_RESIZE); | 86 | dinfo->bus = bus_id; |
69 | 87 | dinfo->unit = unit_id; | |
70 | + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ | 88 | + dinfo->devaddr = devaddr; |
71 | if (!drv) { | 89 | dinfo->serial = g_strdup(serial); |
72 | error_setg(errp, "No medium inserted"); | 90 | |
73 | return -ENOMEDIUM; | 91 | blk_set_legacy_dinfo(blk, dinfo); |
92 | diff --git a/device-hotplug.c b/device-hotplug.c | ||
93 | index XXXXXXX..XXXXXXX 100644 | ||
94 | --- a/device-hotplug.c | ||
95 | +++ b/device-hotplug.c | ||
96 | @@ -XXX,XX +XXX,XX @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) | ||
97 | if (!dinfo) { | ||
98 | goto err; | ||
74 | } | 99 | } |
75 | if (!drv->bdrv_truncate) { | 100 | + if (dinfo->devaddr) { |
76 | + if (bs->file && drv->is_filter) { | 101 | + monitor_printf(mon, "Parameter addr not supported\n"); |
77 | + return bdrv_truncate(bs->file, offset, prealloc, errp); | 102 | + goto err; |
78 | + } | ||
79 | error_setg(errp, "Image format driver does not support resize"); | ||
80 | return -ENOTSUP; | ||
81 | } | ||
82 | @@ -XXX,XX +XXX,XX @@ int bdrv_has_zero_init(BlockDriverState *bs) | ||
83 | if (bs->drv->bdrv_has_zero_init) { | ||
84 | return bs->drv->bdrv_has_zero_init(bs); | ||
85 | } | ||
86 | + if (bs->file && bs->drv->is_filter) { | ||
87 | + return bdrv_has_zero_init(bs->file->bs); | ||
88 | + } | 103 | + } |
89 | 104 | ||
90 | /* safe default */ | 105 | switch (dinfo->type) { |
91 | return 0; | 106 | case IF_NONE: |
92 | @@ -XXX,XX +XXX,XX @@ void bdrv_get_backing_filename(BlockDriverState *bs, | 107 | diff --git a/qemu-doc.texi b/qemu-doc.texi |
93 | int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | 108 | index XXXXXXX..XXXXXXX 100644 |
94 | { | 109 | --- a/qemu-doc.texi |
95 | BlockDriver *drv = bs->drv; | 110 | +++ b/qemu-doc.texi |
96 | - if (!drv) | 111 | @@ -XXX,XX +XXX,XX @@ provided per NIC. |
97 | + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ | 112 | The drive serial argument is replaced by the the serial argument |
98 | + if (!drv) { | 113 | that can be specified with the ``-device'' parameter. |
99 | return -ENOMEDIUM; | 114 | |
100 | - if (!drv->bdrv_get_info) | 115 | +@subsection -drive addr=... (since 2.10.0) |
101 | + } | 116 | + |
102 | + if (!drv->bdrv_get_info) { | 117 | +The drive addr argument is replaced by the the addr argument |
103 | + if (bs->file && drv->is_filter) { | 118 | +that can be specified with the ``-device'' parameter. |
104 | + return bdrv_get_info(bs->file->bs, bdi); | 119 | + |
105 | + } | 120 | @subsection -usbdevice (since 2.10.0) |
106 | return -ENOTSUP; | 121 | |
107 | + } | 122 | The ``-usbdevice DEV'' argument is now a synonym for setting |
108 | memset(bdi, 0, sizeof(*bdi)); | 123 | diff --git a/qemu-options.hx b/qemu-options.hx |
109 | return drv->bdrv_get_info(bs, bdi); | 124 | index XXXXXXX..XXXXXXX 100644 |
110 | } | 125 | --- a/qemu-options.hx |
126 | +++ b/qemu-options.hx | ||
127 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
128 | DEF("drive", HAS_ARG, QEMU_OPTION_drive, | ||
129 | "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" | ||
130 | " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" | ||
131 | - " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" | ||
132 | + " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||
133 | " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||
134 | " [,readonly=on|off][,copy-on-read=on|off]\n" | ||
135 | " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" | ||
136 | @@ -XXX,XX +XXX,XX @@ an untrusted format header. | ||
137 | This option specifies the serial number to assign to the device. This | ||
138 | parameter is deprecated, use the corresponding parameter of @code{-device} | ||
139 | instead. | ||
140 | +@item addr=@var{addr} | ||
141 | +Specify the controller's PCI address (if=virtio only). This parameter is | ||
142 | +deprecated, use the corresponding parameter of @code{-device} instead. | ||
143 | @item werror=@var{action},rerror=@var{action} | ||
144 | Specify which @var{action} to take on write and read errors. Valid actions are: | ||
145 | "ignore" (ignore the error and try to continue), "stop" (pause QEMU), | ||
111 | -- | 146 | -- |
112 | 2.13.5 | 147 | 2.13.6 |
113 | 148 | ||
114 | 149 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Cornelia Huck <cohuck@redhat.com> | |
2 | |||
3 | This reverts commit a7aff6dd10b16b67e8b142d0c94c5d92c3fe88f6. | ||
4 | |||
5 | Hold off removing this for one more QEMU release (current libvirt | ||
6 | release still uses it.) | ||
7 | |||
8 | Signed-off-by: Cornelia Huck <cohuck@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | include/sysemu/blockdev.h | 1 + | ||
12 | blockdev.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++- | ||
13 | hw/block/block.c | 14 +++++++++ | ||
14 | tests/hd-geo-test.c | 37 ++++++++++++++++++----- | ||
15 | hmp-commands.hx | 1 + | ||
16 | qemu-doc.texi | 5 ++++ | ||
17 | qemu-options.hx | 7 ++++- | ||
18 | 7 files changed, 131 insertions(+), 9 deletions(-) | ||
19 | |||
20 | diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/include/sysemu/blockdev.h | ||
23 | +++ b/include/sysemu/blockdev.h | ||
24 | @@ -XXX,XX +XXX,XX @@ struct DriveInfo { | ||
25 | int auto_del; /* see blockdev_mark_auto_del() */ | ||
26 | bool is_default; /* Added by default_drive() ? */ | ||
27 | int media_cd; | ||
28 | + int cyls, heads, secs, trans; | ||
29 | QemuOpts *opts; | ||
30 | char *serial; | ||
31 | QTAILQ_ENTRY(DriveInfo) next; | ||
32 | diff --git a/blockdev.c b/blockdev.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/blockdev.c | ||
35 | +++ b/blockdev.c | ||
36 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { | ||
37 | .type = QEMU_OPT_STRING, | ||
38 | .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", | ||
39 | },{ | ||
40 | + .name = "cyls", | ||
41 | + .type = QEMU_OPT_NUMBER, | ||
42 | + .help = "number of cylinders (ide disk geometry)", | ||
43 | + },{ | ||
44 | + .name = "heads", | ||
45 | + .type = QEMU_OPT_NUMBER, | ||
46 | + .help = "number of heads (ide disk geometry)", | ||
47 | + },{ | ||
48 | + .name = "secs", | ||
49 | + .type = QEMU_OPT_NUMBER, | ||
50 | + .help = "number of sectors (ide disk geometry)", | ||
51 | + },{ | ||
52 | + .name = "trans", | ||
53 | + .type = QEMU_OPT_STRING, | ||
54 | + .help = "chs translation (auto, lba, none)", | ||
55 | + },{ | ||
56 | .name = "addr", | ||
57 | .type = QEMU_OPT_STRING, | ||
58 | .help = "pci address (virtio only)", | ||
59 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
60 | QemuOpts *legacy_opts; | ||
61 | DriveMediaType media = MEDIA_DISK; | ||
62 | BlockInterfaceType type; | ||
63 | + int cyls, heads, secs, translation; | ||
64 | int max_devs, bus_id, unit_id, index; | ||
65 | const char *devaddr; | ||
66 | const char *werror, *rerror; | ||
67 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
68 | Error *local_err = NULL; | ||
69 | int i; | ||
70 | const char *deprecated[] = { | ||
71 | - "serial", "addr" | ||
72 | + "serial", "trans", "secs", "heads", "cyls", "addr" | ||
73 | }; | ||
74 | |||
75 | /* Change legacy command line options into QMP ones */ | ||
76 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
77 | type = block_default_type; | ||
78 | } | ||
79 | |||
80 | + /* Geometry */ | ||
81 | + cyls = qemu_opt_get_number(legacy_opts, "cyls", 0); | ||
82 | + heads = qemu_opt_get_number(legacy_opts, "heads", 0); | ||
83 | + secs = qemu_opt_get_number(legacy_opts, "secs", 0); | ||
84 | + | ||
85 | + if (cyls || heads || secs) { | ||
86 | + if (cyls < 1) { | ||
87 | + error_report("invalid physical cyls number"); | ||
88 | + goto fail; | ||
89 | + } | ||
90 | + if (heads < 1) { | ||
91 | + error_report("invalid physical heads number"); | ||
92 | + goto fail; | ||
93 | + } | ||
94 | + if (secs < 1) { | ||
95 | + error_report("invalid physical secs number"); | ||
96 | + goto fail; | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + translation = BIOS_ATA_TRANSLATION_AUTO; | ||
101 | + value = qemu_opt_get(legacy_opts, "trans"); | ||
102 | + if (value != NULL) { | ||
103 | + if (!cyls) { | ||
104 | + error_report("'%s' trans must be used with cyls, heads and secs", | ||
105 | + value); | ||
106 | + goto fail; | ||
107 | + } | ||
108 | + if (!strcmp(value, "none")) { | ||
109 | + translation = BIOS_ATA_TRANSLATION_NONE; | ||
110 | + } else if (!strcmp(value, "lba")) { | ||
111 | + translation = BIOS_ATA_TRANSLATION_LBA; | ||
112 | + } else if (!strcmp(value, "large")) { | ||
113 | + translation = BIOS_ATA_TRANSLATION_LARGE; | ||
114 | + } else if (!strcmp(value, "rechs")) { | ||
115 | + translation = BIOS_ATA_TRANSLATION_RECHS; | ||
116 | + } else if (!strcmp(value, "auto")) { | ||
117 | + translation = BIOS_ATA_TRANSLATION_AUTO; | ||
118 | + } else { | ||
119 | + error_report("'%s' invalid translation type", value); | ||
120 | + goto fail; | ||
121 | + } | ||
122 | + } | ||
123 | + | ||
124 | + if (media == MEDIA_CDROM) { | ||
125 | + if (cyls || secs || heads) { | ||
126 | + error_report("CHS can't be set with media=cdrom"); | ||
127 | + goto fail; | ||
128 | + } | ||
129 | + } | ||
130 | + | ||
131 | /* Device address specified by bus/unit or index. | ||
132 | * If none was specified, try to find the first free one. */ | ||
133 | bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); | ||
134 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
135 | dinfo = g_malloc0(sizeof(*dinfo)); | ||
136 | dinfo->opts = all_opts; | ||
137 | |||
138 | + dinfo->cyls = cyls; | ||
139 | + dinfo->heads = heads; | ||
140 | + dinfo->secs = secs; | ||
141 | + dinfo->trans = translation; | ||
142 | + | ||
143 | dinfo->type = type; | ||
144 | dinfo->bus = bus_id; | ||
145 | dinfo->unit = unit_id; | ||
146 | diff --git a/hw/block/block.c b/hw/block/block.c | ||
147 | index XXXXXXX..XXXXXXX 100644 | ||
148 | --- a/hw/block/block.c | ||
149 | +++ b/hw/block/block.c | ||
150 | @@ -XXX,XX +XXX,XX @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, | ||
151 | unsigned cyls_max, unsigned heads_max, unsigned secs_max, | ||
152 | Error **errp) | ||
153 | { | ||
154 | + DriveInfo *dinfo; | ||
155 | + | ||
156 | + if (!conf->cyls && !conf->heads && !conf->secs) { | ||
157 | + /* try to fall back to value set with legacy -drive cyls=... */ | ||
158 | + dinfo = blk_legacy_dinfo(conf->blk); | ||
159 | + if (dinfo) { | ||
160 | + conf->cyls = dinfo->cyls; | ||
161 | + conf->heads = dinfo->heads; | ||
162 | + conf->secs = dinfo->secs; | ||
163 | + if (ptrans) { | ||
164 | + *ptrans = dinfo->trans; | ||
165 | + } | ||
166 | + } | ||
167 | + } | ||
168 | if (!conf->cyls && !conf->heads && !conf->secs) { | ||
169 | hd_geometry_guess(conf->blk, | ||
170 | &conf->cyls, &conf->heads, &conf->secs, | ||
171 | diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c | ||
172 | index XXXXXXX..XXXXXXX 100644 | ||
173 | --- a/tests/hd-geo-test.c | ||
174 | +++ b/tests/hd-geo-test.c | ||
175 | @@ -XXX,XX +XXX,XX @@ static void setup_mbr(int img_idx, MBRcontents mbr) | ||
176 | |||
177 | static int setup_ide(int argc, char *argv[], int argv_sz, | ||
178 | int ide_idx, const char *dev, int img_idx, | ||
179 | - MBRcontents mbr) | ||
180 | + MBRcontents mbr, const char *opts) | ||
181 | { | ||
182 | char *s1, *s2, *s3; | ||
183 | |||
184 | @@ -XXX,XX +XXX,XX @@ static int setup_ide(int argc, char *argv[], int argv_sz, | ||
185 | s3 = g_strdup(",media=cdrom"); | ||
186 | } | ||
187 | argc = append_arg(argc, argv, argv_sz, | ||
188 | - g_strdup_printf("%s%s%s", s1, s2, s3)); | ||
189 | + g_strdup_printf("%s%s%s%s", s1, s2, s3, opts)); | ||
190 | g_free(s1); | ||
191 | g_free(s2); | ||
192 | g_free(s3); | ||
193 | @@ -XXX,XX +XXX,XX @@ static void test_ide_mbr(bool use_device, MBRcontents mbr) | ||
194 | for (i = 0; i < backend_last; i++) { | ||
195 | cur_ide[i] = &hd_chst[i][mbr]; | ||
196 | dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; | ||
197 | - argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); | ||
198 | + argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr, ""); | ||
199 | } | ||
200 | args = g_strjoinv(" ", argv); | ||
201 | qtest_start(args); | ||
202 | @@ -XXX,XX +XXX,XX @@ static void test_ide_drive_user(const char *dev, bool trans) | ||
203 | const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; | ||
204 | |||
205 | argc = setup_common(argv, ARGV_SIZE); | ||
206 | - opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", | ||
207 | - dev, trans ? "bios-chs-trans=lba," : "", | ||
208 | + opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d", | ||
209 | + dev ?: "", | ||
210 | + trans && dev ? "bios-chs-" : "", | ||
211 | + trans ? "trans=lba," : "", | ||
212 | expected_chst.cyls, expected_chst.heads, | ||
213 | expected_chst.secs); | ||
214 | cur_ide[0] = &expected_chst; | ||
215 | - argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); | ||
216 | + argc = setup_ide(argc, argv, ARGV_SIZE, | ||
217 | + 0, dev ? opts : NULL, backend_small, mbr_chs, | ||
218 | + dev ? "" : opts); | ||
219 | g_free(opts); | ||
220 | args = g_strjoinv(" ", argv); | ||
221 | qtest_start(args); | ||
222 | @@ -XXX,XX +XXX,XX @@ static void test_ide_drive_user(const char *dev, bool trans) | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | + * Test case: IDE device (if=ide) with explicit CHS | ||
227 | + */ | ||
228 | +static void test_ide_drive_user_chs(void) | ||
229 | +{ | ||
230 | + test_ide_drive_user(NULL, false); | ||
231 | +} | ||
232 | + | ||
233 | +/* | ||
234 | + * Test case: IDE device (if=ide) with explicit CHS and translation | ||
235 | + */ | ||
236 | +static void test_ide_drive_user_chst(void) | ||
237 | +{ | ||
238 | + test_ide_drive_user(NULL, true); | ||
239 | +} | ||
240 | + | ||
241 | +/* | ||
242 | * Test case: IDE device (if=none) with explicit CHS | ||
243 | */ | ||
244 | static void test_ide_device_user_chs(void) | ||
245 | @@ -XXX,XX +XXX,XX @@ static void test_ide_drive_cd_0(void) | ||
246 | for (i = 0; i <= backend_empty; i++) { | ||
247 | ide_idx = backend_empty - i; | ||
248 | cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; | ||
249 | - argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); | ||
250 | + argc = setup_ide(argc, argv, ARGV_SIZE, | ||
251 | + ide_idx, NULL, i, mbr_blank, ""); | ||
252 | } | ||
253 | args = g_strjoinv(" ", argv); | ||
254 | qtest_start(args); | ||
255 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
256 | qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); | ||
257 | qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); | ||
258 | qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); | ||
259 | + qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs); | ||
260 | + qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst); | ||
261 | qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); | ||
262 | qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); | ||
263 | qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); | ||
264 | diff --git a/hmp-commands.hx b/hmp-commands.hx | ||
265 | index XXXXXXX..XXXXXXX 100644 | ||
266 | --- a/hmp-commands.hx | ||
267 | +++ b/hmp-commands.hx | ||
268 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
269 | .params = "[-n] [[<domain>:]<bus>:]<slot>\n" | ||
270 | "[file=file][,if=type][,bus=n]\n" | ||
271 | "[,unit=m][,media=d][,index=i]\n" | ||
272 | + "[,cyls=c,heads=h,secs=s[,trans=t]]\n" | ||
273 | "[,snapshot=on|off][,cache=on|off]\n" | ||
274 | "[,readonly=on|off][,copy-on-read=on|off]", | ||
275 | .help = "add drive to PCI storage controller", | ||
276 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
277 | index XXXXXXX..XXXXXXX 100644 | ||
278 | --- a/qemu-doc.texi | ||
279 | +++ b/qemu-doc.texi | ||
280 | @@ -XXX,XX +XXX,XX @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' | ||
281 | (for embedded NICs). The new syntax allows different settings to be | ||
282 | provided per NIC. | ||
283 | |||
284 | +@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) | ||
285 | + | ||
286 | +The drive geometry arguments are replaced by the the geometry arguments | ||
287 | +that can be specified with the ``-device'' parameter. | ||
288 | + | ||
289 | @subsection -drive serial=... (since 2.10.0) | ||
290 | |||
291 | The drive serial argument is replaced by the the serial argument | ||
292 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
293 | index XXXXXXX..XXXXXXX 100644 | ||
294 | --- a/qemu-options.hx | ||
295 | +++ b/qemu-options.hx | ||
296 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
297 | |||
298 | DEF("drive", HAS_ARG, QEMU_OPTION_drive, | ||
299 | "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" | ||
300 | + " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" | ||
301 | " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" | ||
302 | - " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||
303 | + " [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||
304 | " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||
305 | " [,readonly=on|off][,copy-on-read=on|off]\n" | ||
306 | " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" | ||
307 | @@ -XXX,XX +XXX,XX @@ This option defines where is connected the drive by using an index in the list | ||
308 | of available connectors of a given interface type. | ||
309 | @item media=@var{media} | ||
310 | This option defines the type of the media: disk or cdrom. | ||
311 | +@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] | ||
312 | +Force disk physical geometry and the optional BIOS translation (trans=none or | ||
313 | +lba). These parameters are deprecated, use the corresponding parameters | ||
314 | +of @code{-device} instead. | ||
315 | @item snapshot=@var{snapshot} | ||
316 | @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive | ||
317 | (see @option{-snapshot}). | ||
318 | -- | ||
319 | 2.13.6 | ||
320 | |||
321 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fam Zheng <famz@redhat.com> | ||
1 | 2 | ||
3 | With in one module, trace points usually have a common prefix named | ||
4 | after the module name. paio_submit and paio_submit_co are the only two | ||
5 | trace points so far in the two file protocol drivers. As we are adding | ||
6 | more, having a common prefix here is better so that trace points can be | ||
7 | enabled with a glob. Rename them. | ||
8 | |||
9 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | --- | ||
13 | block/file-posix.c | 2 +- | ||
14 | block/file-win32.c | 2 +- | ||
15 | block/trace-events | 4 ++-- | ||
16 | 3 files changed, 4 insertions(+), 4 deletions(-) | ||
17 | |||
18 | diff --git a/block/file-posix.c b/block/file-posix.c | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/block/file-posix.c | ||
21 | +++ b/block/file-posix.c | ||
22 | @@ -XXX,XX +XXX,XX @@ static int paio_submit_co_full(BlockDriverState *bs, int fd, | ||
23 | assert(qiov->size == bytes); | ||
24 | } | ||
25 | |||
26 | - trace_paio_submit_co(offset, bytes, type); | ||
27 | + trace_file_paio_submit_co(offset, bytes, type); | ||
28 | pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||
29 | return thread_pool_submit_co(pool, aio_worker, acb); | ||
30 | } | ||
31 | diff --git a/block/file-win32.c b/block/file-win32.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block/file-win32.c | ||
34 | +++ b/block/file-win32.c | ||
35 | @@ -XXX,XX +XXX,XX @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, | ||
36 | acb->aio_nbytes = count; | ||
37 | acb->aio_offset = offset; | ||
38 | |||
39 | - trace_paio_submit(acb, opaque, offset, count, type); | ||
40 | + trace_file_paio_submit(acb, opaque, offset, count, type); | ||
41 | pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||
42 | return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); | ||
43 | } | ||
44 | diff --git a/block/trace-events b/block/trace-events | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/block/trace-events | ||
47 | +++ b/block/trace-events | ||
48 | @@ -XXX,XX +XXX,XX @@ qmp_block_stream(void *bs, void *job) "bs %p job %p" | ||
49 | |||
50 | # block/file-win32.c | ||
51 | # block/file-posix.c | ||
52 | -paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" | ||
53 | -paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" | ||
54 | +file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" | ||
55 | +file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" | ||
56 | |||
57 | # block/qcow2.c | ||
58 | qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" | ||
59 | -- | ||
60 | 2.13.6 | ||
61 | |||
62 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fam Zheng <famz@redhat.com> | ||
1 | 2 | ||
3 | A few trace points that can help reveal what is happening in a copy | ||
4 | offloading I/O path. | ||
5 | |||
6 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | block/file-posix.c | 2 ++ | ||
10 | block/io.c | 4 ++++ | ||
11 | block/iscsi.c | 3 +++ | ||
12 | block/trace-events | 6 ++++++ | ||
13 | 4 files changed, 15 insertions(+) | ||
14 | |||
15 | diff --git a/block/file-posix.c b/block/file-posix.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block/file-posix.c | ||
18 | +++ b/block/file-posix.c | ||
19 | @@ -XXX,XX +XXX,XX @@ static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) | ||
20 | ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, | ||
21 | aiocb->aio_fd2, &out_off, | ||
22 | bytes, 0); | ||
23 | + trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, | ||
24 | + aiocb->aio_fd2, out_off, bytes, 0, ret); | ||
25 | if (ret == 0) { | ||
26 | /* No progress (e.g. when beyond EOF), let the caller fall back to | ||
27 | * buffer I/O. */ | ||
28 | diff --git a/block/io.c b/block/io.c | ||
29 | index XXXXXXX..XXXXXXX 100644 | ||
30 | --- a/block/io.c | ||
31 | +++ b/block/io.c | ||
32 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, | ||
33 | BdrvRequestFlags read_flags, | ||
34 | BdrvRequestFlags write_flags) | ||
35 | { | ||
36 | + trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, | ||
37 | + read_flags, write_flags); | ||
38 | return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, | ||
39 | bytes, read_flags, write_flags, true); | ||
40 | } | ||
41 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, | ||
42 | BdrvRequestFlags read_flags, | ||
43 | BdrvRequestFlags write_flags) | ||
44 | { | ||
45 | + trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, | ||
46 | + read_flags, write_flags); | ||
47 | return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, | ||
48 | bytes, read_flags, write_flags, false); | ||
49 | } | ||
50 | diff --git a/block/iscsi.c b/block/iscsi.c | ||
51 | index XXXXXXX..XXXXXXX 100644 | ||
52 | --- a/block/iscsi.c | ||
53 | +++ b/block/iscsi.c | ||
54 | @@ -XXX,XX +XXX,XX @@ | ||
55 | #include "qapi/qmp/qstring.h" | ||
56 | #include "crypto/secret.h" | ||
57 | #include "scsi/utils.h" | ||
58 | +#include "trace.h" | ||
59 | |||
60 | /* Conflict between scsi/utils.h and libiscsi! :( */ | ||
61 | #define SCSI_XFER_NONE ISCSI_XFER_NONE | ||
62 | @@ -XXX,XX +XXX,XX @@ retry: | ||
63 | } | ||
64 | |||
65 | out_unlock: | ||
66 | + | ||
67 | + trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r); | ||
68 | g_free(iscsi_task.task); | ||
69 | qemu_mutex_unlock(&dst_lun->mutex); | ||
70 | g_free(iscsi_task.err_str); | ||
71 | diff --git a/block/trace-events b/block/trace-events | ||
72 | index XXXXXXX..XXXXXXX 100644 | ||
73 | --- a/block/trace-events | ||
74 | +++ b/block/trace-events | ||
75 | @@ -XXX,XX +XXX,XX @@ bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs | ||
76 | bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" | ||
77 | bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" | ||
78 | bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 | ||
79 | +bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" | ||
80 | +bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" | ||
81 | |||
82 | # block/stream.c | ||
83 | stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" | ||
84 | @@ -XXX,XX +XXX,XX @@ qmp_block_stream(void *bs, void *job) "bs %p job %p" | ||
85 | # block/file-posix.c | ||
86 | file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" | ||
87 | file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" | ||
88 | +file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 | ||
89 | |||
90 | # block/qcow2.c | ||
91 | qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" | ||
92 | @@ -XXX,XX +XXX,XX @@ nvme_free_req_queue_wait(void *q) "q %p" | ||
93 | nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" | ||
94 | nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 | ||
95 | nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" | ||
96 | + | ||
97 | +# block/iscsi.c | ||
98 | +iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d" | ||
99 | -- | ||
100 | 2.13.6 | ||
101 | |||
102 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | bdrv_co_get_block_status_from_file() and | 3 | Other I/O functions are already using a BdrvChild pointer in the API, so |
4 | bdrv_co_get_block_status_from_backing() set *file to bs->file and | 4 | make discard do the same. It makes it possible to initiate the same |
5 | bs->backing respectively, so that bdrv_co_get_block_status() can recurse | 5 | permission checks before doing I/O, and much easier to share the |
6 | to them. Future block drivers won't have to duplicate code to implement | 6 | helper functions for this, which will be added and used by write, |
7 | this. | 7 | truncate and copy range paths. |
8 | 8 | ||
9 | Reviewed-by: Fam Zheng <famz@redhat.com> | 9 | Signed-off-by: Fam Zheng <famz@redhat.com> |
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | --- | 11 | --- |
16 | include/block/block_int.h | 18 ++++++++++++++++++ | 12 | include/block/block.h | 4 ++-- |
17 | block/blkdebug.c | 12 +----------- | 13 | block/blkdebug.c | 2 +- |
18 | block/commit.c | 12 +----------- | 14 | block/blklogwrites.c | 2 +- |
19 | block/io.c | 26 ++++++++++++++++++++++++++ | 15 | block/blkreplay.c | 2 +- |
20 | block/mirror.c | 12 +----------- | 16 | block/block-backend.c | 2 +- |
21 | 5 files changed, 47 insertions(+), 33 deletions(-) | 17 | block/copy-on-read.c | 2 +- |
22 | 18 | block/io.c | 18 +++++++++--------- | |
23 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 19 | block/mirror.c | 2 +- |
24 | index XXXXXXX..XXXXXXX 100644 | 20 | block/qcow2-refcount.c | 2 +- |
25 | --- a/include/block/block_int.h | 21 | block/raw-format.c | 2 +- |
26 | +++ b/include/block/block_int.h | 22 | block/throttle.c | 2 +- |
27 | @@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | 23 | 11 files changed, 20 insertions(+), 20 deletions(-) |
28 | uint64_t perm, uint64_t shared, | 24 | |
29 | uint64_t *nperm, uint64_t *nshared); | 25 | diff --git a/include/block/block.h b/include/block/block.h |
30 | 26 | index XXXXXXX..XXXXXXX 100644 | |
31 | +/* | 27 | --- a/include/block/block.h |
32 | + * Default implementation for drivers to pass bdrv_co_get_block_status() to | 28 | +++ b/include/block/block.h |
33 | + * their file. | 29 | @@ -XXX,XX +XXX,XX @@ AioWait *bdrv_get_aio_wait(BlockDriverState *bs); |
34 | + */ | 30 | bdrv_get_aio_context(bs_), \ |
35 | +int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs, | 31 | cond); }) |
36 | + int64_t sector_num, | 32 | |
37 | + int nb_sectors, | 33 | -int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); |
38 | + int *pnum, | 34 | -int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); |
39 | + BlockDriverState **file); | 35 | +int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes); |
40 | +/* | 36 | +int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes); |
41 | + * Default implementation for drivers to pass bdrv_co_get_block_status() to | 37 | int bdrv_has_zero_init_1(BlockDriverState *bs); |
42 | + * their backing file. | 38 | int bdrv_has_zero_init(BlockDriverState *bs); |
43 | + */ | 39 | bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); |
44 | +int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | ||
45 | + int64_t sector_num, | ||
46 | + int nb_sectors, | ||
47 | + int *pnum, | ||
48 | + BlockDriverState **file); | ||
49 | const char *bdrv_get_parent_name(const BlockDriverState *bs); | ||
50 | void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); | ||
51 | bool blk_dev_has_removable_media(BlockBackend *blk); | ||
52 | diff --git a/block/blkdebug.c b/block/blkdebug.c | 40 | diff --git a/block/blkdebug.c b/block/blkdebug.c |
53 | index XXXXXXX..XXXXXXX 100644 | 41 | index XXXXXXX..XXXXXXX 100644 |
54 | --- a/block/blkdebug.c | 42 | --- a/block/blkdebug.c |
55 | +++ b/block/blkdebug.c | 43 | +++ b/block/blkdebug.c |
56 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | 44 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, |
57 | return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | 45 | return err; |
58 | } | 46 | } |
59 | 47 | ||
60 | -static int64_t coroutine_fn blkdebug_co_get_block_status( | 48 | - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); |
61 | - BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | 49 | + return bdrv_co_pdiscard(bs->file, offset, bytes); |
62 | - BlockDriverState **file) | 50 | } |
63 | -{ | 51 | |
64 | - *pnum = nb_sectors; | 52 | static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, |
65 | - *file = bs->file->bs; | 53 | diff --git a/block/blklogwrites.c b/block/blklogwrites.c |
66 | - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | 54 | index XXXXXXX..XXXXXXX 100644 |
67 | - (sector_num << BDRV_SECTOR_BITS); | 55 | --- a/block/blklogwrites.c |
68 | -} | 56 | +++ b/block/blklogwrites.c |
69 | - | 57 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) |
70 | static void blkdebug_close(BlockDriverState *bs) | 58 | static int coroutine_fn |
71 | { | 59 | blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) |
72 | BDRVBlkdebugState *s = bs->opaque; | 60 | { |
73 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | 61 | - return bdrv_co_pdiscard(fr->bs->file->bs, fr->offset, fr->bytes); |
74 | .bdrv_co_flush_to_disk = blkdebug_co_flush, | 62 | + return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes); |
75 | .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes, | 63 | } |
76 | .bdrv_co_pdiscard = blkdebug_co_pdiscard, | 64 | |
77 | - .bdrv_co_get_block_status = blkdebug_co_get_block_status, | 65 | static int coroutine_fn |
78 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file, | 66 | diff --git a/block/blkreplay.c b/block/blkreplay.c |
79 | 67 | index XXXXXXX..XXXXXXX 100755 | |
80 | .bdrv_debug_event = blkdebug_debug_event, | 68 | --- a/block/blkreplay.c |
81 | .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, | 69 | +++ b/block/blkreplay.c |
82 | diff --git a/block/commit.c b/block/commit.c | 70 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, |
83 | index XXXXXXX..XXXXXXX 100644 | 71 | int64_t offset, int bytes) |
84 | --- a/block/commit.c | 72 | { |
85 | +++ b/block/commit.c | 73 | uint64_t reqid = blkreplay_next_id(); |
86 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | 74 | - int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); |
87 | return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); | 75 | + int ret = bdrv_co_pdiscard(bs->file, offset, bytes); |
88 | } | 76 | block_request_create(reqid, bs, qemu_coroutine_self()); |
89 | 77 | qemu_coroutine_yield(); | |
90 | -static int64_t coroutine_fn bdrv_commit_top_get_block_status( | 78 | |
91 | - BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | 79 | diff --git a/block/block-backend.c b/block/block-backend.c |
92 | - BlockDriverState **file) | 80 | index XXXXXXX..XXXXXXX 100644 |
93 | -{ | 81 | --- a/block/block-backend.c |
94 | - *pnum = nb_sectors; | 82 | +++ b/block/block-backend.c |
95 | - *file = bs->backing->bs; | 83 | @@ -XXX,XX +XXX,XX @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) |
96 | - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | 84 | return ret; |
97 | - (sector_num << BDRV_SECTOR_BITS); | 85 | } |
98 | -} | 86 | |
99 | - | 87 | - return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); |
100 | static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) | 88 | + return bdrv_co_pdiscard(blk->root, offset, bytes); |
101 | { | 89 | } |
102 | bdrv_refresh_filename(bs->backing->bs); | 90 | |
103 | @@ -XXX,XX +XXX,XX @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | 91 | int blk_co_flush(BlockBackend *blk) |
104 | static BlockDriver bdrv_commit_top = { | 92 | diff --git a/block/copy-on-read.c b/block/copy-on-read.c |
105 | .format_name = "commit_top", | 93 | index XXXXXXX..XXXXXXX 100644 |
106 | .bdrv_co_preadv = bdrv_commit_top_preadv, | 94 | --- a/block/copy-on-read.c |
107 | - .bdrv_co_get_block_status = bdrv_commit_top_get_block_status, | 95 | +++ b/block/copy-on-read.c |
108 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing, | 96 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, |
109 | .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, | 97 | static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, |
110 | .bdrv_close = bdrv_commit_top_close, | 98 | int64_t offset, int bytes) |
111 | .bdrv_child_perm = bdrv_commit_top_child_perm, | 99 | { |
100 | - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||
101 | + return bdrv_co_pdiscard(bs->file, offset, bytes); | ||
102 | } | ||
103 | |||
104 | |||
112 | diff --git a/block/io.c b/block/io.c | 105 | diff --git a/block/io.c b/block/io.c |
113 | index XXXXXXX..XXXXXXX 100644 | 106 | index XXXXXXX..XXXXXXX 100644 |
114 | --- a/block/io.c | 107 | --- a/block/io.c |
115 | +++ b/block/io.c | 108 | +++ b/block/io.c |
116 | @@ -XXX,XX +XXX,XX @@ typedef struct BdrvCoGetBlockStatusData { | 109 | @@ -XXX,XX +XXX,XX @@ int bdrv_flush(BlockDriverState *bs) |
117 | bool done; | 110 | } |
118 | } BdrvCoGetBlockStatusData; | 111 | |
119 | 112 | typedef struct DiscardCo { | |
120 | +int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs, | 113 | - BlockDriverState *bs; |
121 | + int64_t sector_num, | 114 | + BdrvChild *child; |
122 | + int nb_sectors, | 115 | int64_t offset; |
123 | + int *pnum, | 116 | int bytes; |
124 | + BlockDriverState **file) | 117 | int ret; |
125 | +{ | 118 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque) |
126 | + assert(bs->file && bs->file->bs); | 119 | { |
127 | + *pnum = nb_sectors; | 120 | DiscardCo *rwco = opaque; |
128 | + *file = bs->file->bs; | 121 | |
129 | + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | 122 | - rwco->ret = bdrv_co_pdiscard(rwco->bs, rwco->offset, rwco->bytes); |
130 | + (sector_num << BDRV_SECTOR_BITS); | 123 | + rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes); |
131 | +} | 124 | } |
132 | + | 125 | |
133 | +int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | 126 | -int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, |
134 | + int64_t sector_num, | 127 | - int bytes) |
135 | + int nb_sectors, | 128 | +int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) |
136 | + int *pnum, | 129 | { |
137 | + BlockDriverState **file) | 130 | BdrvTrackedRequest req; |
138 | +{ | 131 | int max_pdiscard, ret; |
139 | + assert(bs->backing && bs->backing->bs); | 132 | int head, tail, align; |
140 | + *pnum = nb_sectors; | 133 | + BlockDriverState *bs = child->bs; |
141 | + *file = bs->backing->bs; | 134 | |
142 | + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | 135 | - if (!bs->drv) { |
143 | + (sector_num << BDRV_SECTOR_BITS); | 136 | + if (!bs || !bs->drv) { |
144 | +} | 137 | return -ENOMEDIUM; |
145 | + | 138 | } |
146 | /* | 139 | |
147 | * Returns the allocation status of the specified sectors. | 140 | @@ -XXX,XX +XXX,XX @@ out: |
148 | * Drivers not implementing the functionality are assumed to not support | 141 | return ret; |
142 | } | ||
143 | |||
144 | -int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) | ||
145 | +int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes) | ||
146 | { | ||
147 | Coroutine *co; | ||
148 | DiscardCo rwco = { | ||
149 | - .bs = bs, | ||
150 | + .child = child, | ||
151 | .offset = offset, | ||
152 | .bytes = bytes, | ||
153 | .ret = NOT_DONE, | ||
154 | @@ -XXX,XX +XXX,XX @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) | ||
155 | bdrv_pdiscard_co_entry(&rwco); | ||
156 | } else { | ||
157 | co = qemu_coroutine_create(bdrv_pdiscard_co_entry, &rwco); | ||
158 | - bdrv_coroutine_enter(bs, co); | ||
159 | - BDRV_POLL_WHILE(bs, rwco.ret == NOT_DONE); | ||
160 | + bdrv_coroutine_enter(child->bs, co); | ||
161 | + BDRV_POLL_WHILE(child->bs, rwco.ret == NOT_DONE); | ||
162 | } | ||
163 | |||
164 | return rwco.ret; | ||
149 | diff --git a/block/mirror.c b/block/mirror.c | 165 | diff --git a/block/mirror.c b/block/mirror.c |
150 | index XXXXXXX..XXXXXXX 100644 | 166 | index XXXXXXX..XXXXXXX 100644 |
151 | --- a/block/mirror.c | 167 | --- a/block/mirror.c |
152 | +++ b/block/mirror.c | 168 | +++ b/block/mirror.c |
153 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) | 169 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, |
154 | return bdrv_co_flush(bs->backing->bs); | 170 | break; |
155 | } | 171 | |
156 | 172 | case MIRROR_METHOD_DISCARD: | |
157 | -static int64_t coroutine_fn bdrv_mirror_top_get_block_status( | 173 | - ret = bdrv_co_pdiscard(bs->backing->bs, offset, bytes); |
158 | - BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | 174 | + ret = bdrv_co_pdiscard(bs->backing, offset, bytes); |
159 | - BlockDriverState **file) | 175 | break; |
160 | -{ | 176 | |
161 | - *pnum = nb_sectors; | 177 | default: |
162 | - *file = bs->backing->bs; | 178 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c |
163 | - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | 179 | index XXXXXXX..XXXXXXX 100644 |
164 | - (sector_num << BDRV_SECTOR_BITS); | 180 | --- a/block/qcow2-refcount.c |
165 | -} | 181 | +++ b/block/qcow2-refcount.c |
166 | - | 182 | @@ -XXX,XX +XXX,XX @@ void qcow2_process_discards(BlockDriverState *bs, int ret) |
167 | static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, | 183 | |
168 | int64_t offset, int bytes, BdrvRequestFlags flags) | 184 | /* Discard is optional, ignore the return value */ |
169 | { | 185 | if (ret >= 0) { |
170 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_mirror_top = { | 186 | - bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); |
171 | .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, | 187 | + bdrv_pdiscard(bs->file, d->offset, d->bytes); |
172 | .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, | 188 | } |
173 | .bdrv_co_flush = bdrv_mirror_top_flush, | 189 | |
174 | - .bdrv_co_get_block_status = bdrv_mirror_top_get_block_status, | 190 | g_free(d); |
175 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing, | 191 | diff --git a/block/raw-format.c b/block/raw-format.c |
176 | .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, | 192 | index XXXXXXX..XXXXXXX 100644 |
177 | .bdrv_close = bdrv_mirror_top_close, | 193 | --- a/block/raw-format.c |
178 | .bdrv_child_perm = bdrv_mirror_top_child_perm, | 194 | +++ b/block/raw-format.c |
195 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, | ||
196 | if (ret) { | ||
197 | return ret; | ||
198 | } | ||
199 | - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||
200 | + return bdrv_co_pdiscard(bs->file, offset, bytes); | ||
201 | } | ||
202 | |||
203 | static int64_t raw_getlength(BlockDriverState *bs) | ||
204 | diff --git a/block/throttle.c b/block/throttle.c | ||
205 | index XXXXXXX..XXXXXXX 100644 | ||
206 | --- a/block/throttle.c | ||
207 | +++ b/block/throttle.c | ||
208 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, | ||
209 | ThrottleGroupMember *tgm = bs->opaque; | ||
210 | throttle_group_co_io_limits_intercept(tgm, bytes, true); | ||
211 | |||
212 | - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||
213 | + return bdrv_co_pdiscard(bs->file, offset, bytes); | ||
214 | } | ||
215 | |||
216 | static int throttle_co_flush(BlockDriverState *bs) | ||
179 | -- | 217 | -- |
180 | 2.13.5 | 218 | 2.13.6 |
181 | 219 | ||
182 | 220 | diff view generated by jsdifflib |
1 | From: "Daniel P. Berrange" <berrange@redhat.com> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 3 | This matches the types used for bytes in the rest parts of block layer. |
4 | Reviewed-by: Eric Blake <eblake@redhat.com> | 4 | In the case of bdrv_co_truncate, new_bytes can be the image size which |
5 | Signed-off-by: Daniel P. Berrange <berrange@redhat.com> | 5 | probably doesn't fit in a 32 bit int. |
6 | |||
7 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | --- | 9 | --- |
8 | include/block/block_int.h | 31 +++++++++++++++++++++++++++++++ | 10 | include/block/block_int.h | 4 ++-- |
9 | 1 file changed, 31 insertions(+) | 11 | block/io.c | 8 +++++--- |
12 | 2 files changed, 7 insertions(+), 5 deletions(-) | ||
10 | 13 | ||
11 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 14 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
12 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/include/block/block_int.h | 16 | --- a/include/block/block_int.h |
14 | +++ b/include/block/block_int.h | 17 | +++ b/include/block/block_int.h |
15 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 18 | @@ -XXX,XX +XXX,XX @@ enum BdrvTrackedRequestType { |
16 | 19 | typedef struct BdrvTrackedRequest { | |
17 | int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs, | 20 | BlockDriverState *bs; |
18 | int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); | 21 | int64_t offset; |
22 | - unsigned int bytes; | ||
23 | + uint64_t bytes; | ||
24 | enum BdrvTrackedRequestType type; | ||
25 | |||
26 | bool serialising; | ||
27 | int64_t overlap_offset; | ||
28 | - unsigned int overlap_bytes; | ||
29 | + uint64_t overlap_bytes; | ||
30 | |||
31 | QLIST_ENTRY(BdrvTrackedRequest) list; | ||
32 | Coroutine *co; /* owner, used for deadlock detection */ | ||
33 | diff --git a/block/io.c b/block/io.c | ||
34 | index XXXXXXX..XXXXXXX 100644 | ||
35 | --- a/block/io.c | ||
36 | +++ b/block/io.c | ||
37 | @@ -XXX,XX +XXX,XX @@ static void tracked_request_end(BdrvTrackedRequest *req) | ||
38 | static void tracked_request_begin(BdrvTrackedRequest *req, | ||
39 | BlockDriverState *bs, | ||
40 | int64_t offset, | ||
41 | - unsigned int bytes, | ||
42 | + uint64_t bytes, | ||
43 | enum BdrvTrackedRequestType type) | ||
44 | { | ||
45 | + assert(bytes <= INT64_MAX && offset <= INT64_MAX - bytes); | ||
19 | + | 46 | + |
20 | + /** | 47 | *req = (BdrvTrackedRequest){ |
21 | + * @offset: position in bytes to read at | 48 | .bs = bs, |
22 | + * @bytes: number of bytes to read | 49 | .offset = offset, |
23 | + * @qiov: the buffers to fill with read data | 50 | @@ -XXX,XX +XXX,XX @@ static void tracked_request_begin(BdrvTrackedRequest *req, |
24 | + * @flags: currently unused, always 0 | 51 | static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) |
25 | + * | 52 | { |
26 | + * @offset and @bytes will be a multiple of 'request_alignment', | 53 | int64_t overlap_offset = req->offset & ~(align - 1); |
27 | + * but the length of individual @qiov elements does not have to | 54 | - unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) |
28 | + * be a multiple. | 55 | + uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align) |
29 | + * | 56 | - overlap_offset; |
30 | + * @bytes will always equal the total size of @qiov, and will be | 57 | |
31 | + * no larger than 'max_transfer'. | 58 | if (!req->serialising) { |
32 | + * | 59 | @@ -XXX,XX +XXX,XX @@ static int bdrv_get_cluster_size(BlockDriverState *bs) |
33 | + * The buffer in @qiov may point directly to guest memory. | 60 | } |
34 | + */ | 61 | |
35 | int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, | 62 | static bool tracked_request_overlaps(BdrvTrackedRequest *req, |
36 | uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); | 63 | - int64_t offset, unsigned int bytes) |
37 | int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, | 64 | + int64_t offset, uint64_t bytes) |
38 | int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); | 65 | { |
39 | int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs, | 66 | /* aaaa bbbb */ |
40 | int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); | 67 | if (offset >= req->overlap_offset + req->overlap_bytes) { |
41 | + /** | ||
42 | + * @offset: position in bytes to write at | ||
43 | + * @bytes: number of bytes to write | ||
44 | + * @qiov: the buffers containing data to write | ||
45 | + * @flags: zero or more bits allowed by 'supported_write_flags' | ||
46 | + * | ||
47 | + * @offset and @bytes will be a multiple of 'request_alignment', | ||
48 | + * but the length of individual @qiov elements does not have to | ||
49 | + * be a multiple. | ||
50 | + * | ||
51 | + * @bytes will always equal the total size of @qiov, and will be | ||
52 | + * no larger than 'max_transfer'. | ||
53 | + * | ||
54 | + * The buffer in @qiov may point directly to guest memory. | ||
55 | + */ | ||
56 | int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs, | ||
57 | uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); | ||
58 | |||
59 | -- | 68 | -- |
60 | 2.13.5 | 69 | 2.13.6 |
61 | 70 | ||
62 | 71 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 3 | As a mechanical refactoring patch, this is the first step towards |
4 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 4 | unified and more correct write code paths. This is helpful because |
5 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 5 | multiple BlockDriverState fields need to be updated after modifying |
6 | image data, and it's hard to maintain in multiple places such as copy | ||
7 | offload, discard and truncate. | ||
8 | |||
9 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | --- | 12 | --- |
8 | tests/qemu-iotests/184 | 205 ++++++++++++++++++++++++++++++ | 13 | block/io.c | 91 +++++++++++++++++++++++++++++++++++++++----------------------- |
9 | tests/qemu-iotests/184.out | 302 +++++++++++++++++++++++++++++++++++++++++++++ | 14 | 1 file changed, 57 insertions(+), 34 deletions(-) |
10 | tests/qemu-iotests/group | 1 + | ||
11 | 3 files changed, 508 insertions(+) | ||
12 | create mode 100755 tests/qemu-iotests/184 | ||
13 | create mode 100644 tests/qemu-iotests/184.out | ||
14 | 15 | ||
15 | diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184 | 16 | diff --git a/block/io.c b/block/io.c |
16 | new file mode 100755 | 17 | index XXXXXXX..XXXXXXX 100644 |
17 | index XXXXXXX..XXXXXXX | 18 | --- a/block/io.c |
18 | --- /dev/null | 19 | +++ b/block/io.c |
19 | +++ b/tests/qemu-iotests/184 | 20 | @@ -XXX,XX +XXX,XX @@ fail: |
20 | @@ -XXX,XX +XXX,XX @@ | 21 | return ret; |
21 | +#!/bin/bash | 22 | } |
22 | +# | 23 | |
23 | +# Test I/O throttle block filter driver interface | 24 | +static inline int coroutine_fn |
24 | +# | 25 | +bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, |
25 | +# Copyright (C) 2017 Manos Pitsidianakis | 26 | + BdrvTrackedRequest *req, int flags) |
26 | +# | 27 | +{ |
27 | +# This program is free software; you can redistribute it and/or modify | 28 | + BlockDriverState *bs = child->bs; |
28 | +# it under the terms of the GNU General Public License as published by | 29 | + bool waited; |
29 | +# the Free Software Foundation; either version 2 of the License, or | 30 | + int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); |
30 | +# (at your option) any later version. | ||
31 | +# | ||
32 | +# This program is distributed in the hope that it will be useful, | ||
33 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
34 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
35 | +# GNU General Public License for more details. | ||
36 | +# | ||
37 | +# You should have received a copy of the GNU General Public License | ||
38 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
39 | +# | ||
40 | + | 31 | + |
41 | +# creator | 32 | + if (bs->read_only) { |
42 | +owner="Manos Pitsidianakis" | 33 | + return -EPERM; |
34 | + } | ||
43 | + | 35 | + |
44 | +seq=`basename $0` | 36 | + /* BDRV_REQ_NO_SERIALISING is only for read operation */ |
45 | +echo "QA output created by $seq" | 37 | + assert(!(flags & BDRV_REQ_NO_SERIALISING)); |
38 | + assert(!(bs->open_flags & BDRV_O_INACTIVE)); | ||
39 | + assert((bs->open_flags & BDRV_O_NO_IO) == 0); | ||
40 | + assert(!(flags & ~BDRV_REQ_MASK)); | ||
46 | + | 41 | + |
47 | +here=`pwd` | 42 | + if (flags & BDRV_REQ_SERIALISING) { |
48 | +status=1 # failure is the default! | 43 | + mark_request_serialising(req, bdrv_get_cluster_size(bs)); |
44 | + } | ||
49 | + | 45 | + |
50 | +_cleanup() | 46 | + waited = wait_serialising_requests(req); |
51 | +{ | ||
52 | + _cleanup_test_img | ||
53 | +} | ||
54 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
55 | + | 47 | + |
56 | +# get standard environment, filters and checks | 48 | + assert(!waited || !req->serialising || |
57 | +. ./common.rc | 49 | + is_request_serialising_and_aligned(req)); |
58 | +. ./common.filter | 50 | + assert(req->overlap_offset <= offset); |
51 | + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); | ||
59 | + | 52 | + |
60 | +_supported_fmt qcow2 | 53 | + if (flags & BDRV_REQ_WRITE_UNCHANGED) { |
61 | +_supported_proto file | 54 | + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); |
62 | +_supported_os Linux | 55 | + } else { |
63 | + | 56 | + assert(child->perm & BLK_PERM_WRITE); |
64 | +function do_run_qemu() | 57 | + } |
65 | +{ | 58 | + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); |
66 | + echo Testing: "$@" | _filter_imgfmt | 59 | + return notifier_with_return_list_notify(&bs->before_write_notifiers, req); |
67 | + $QEMU -nographic -qmp-pretty stdio -serial none "$@" | ||
68 | + echo | ||
69 | +} | 60 | +} |
70 | + | 61 | + |
71 | +function run_qemu() | 62 | +static inline void coroutine_fn |
63 | +bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, | ||
64 | + BdrvTrackedRequest *req, int ret) | ||
72 | +{ | 65 | +{ |
73 | + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ | 66 | + int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); |
74 | + | _filter_qemu_io | _filter_generated_node_ids | 67 | + BlockDriverState *bs = child->bs; |
75 | +} | ||
76 | + | 68 | + |
77 | +_make_test_img 64M | 69 | + atomic_inc(&bs->write_gen); |
78 | +test_throttle=$($QEMU_IMG --help|grep throttle) | 70 | + bdrv_set_dirty(bs, offset, bytes); |
79 | +[ "$test_throttle" = "" ] && _supported_fmt throttle | ||
80 | + | 71 | + |
81 | +echo | 72 | + stat64_max(&bs->wr_highest_offset, offset + bytes); |
82 | +echo "== checking interface ==" | ||
83 | + | 73 | + |
84 | +run_qemu <<EOF | 74 | + if (ret == 0) { |
85 | +{ "execute": "qmp_capabilities" } | 75 | + bs->total_sectors = MAX(bs->total_sectors, end_sector); |
86 | +{ "execute": "blockdev-add", | ||
87 | + "arguments": { | ||
88 | + "driver": "$IMGFMT", | ||
89 | + "node-name": "disk0", | ||
90 | + "file": { | ||
91 | + "driver": "file", | ||
92 | + "filename": "$TEST_IMG" | ||
93 | + } | ||
94 | + } | ||
95 | +} | ||
96 | +{ "execute": "object-add", | ||
97 | + "arguments": { | ||
98 | + "qom-type": "throttle-group", | ||
99 | + "id": "group0", | ||
100 | + "props": { | ||
101 | + "limits" : { | ||
102 | + "iops-total": 1000 | ||
103 | + } | ||
104 | + } | ||
105 | + } | ||
106 | +} | ||
107 | +{ "execute": "blockdev-add", | ||
108 | + "arguments": { | ||
109 | + "driver": "throttle", | ||
110 | + "node-name": "throttle0", | ||
111 | + "throttle-group": "group0", | ||
112 | + "file": "disk0" | ||
113 | + } | ||
114 | +} | ||
115 | +{ "execute": "query-named-block-nodes" } | ||
116 | +{ "execute": "query-block" } | ||
117 | +{ "execute": "quit" } | ||
118 | +EOF | ||
119 | + | ||
120 | +echo | ||
121 | +echo "== property changes in ThrottleGroup ==" | ||
122 | + | ||
123 | +run_qemu <<EOF | ||
124 | +{ "execute": "qmp_capabilities" } | ||
125 | +{ "execute": "object-add", | ||
126 | + "arguments": { | ||
127 | + "qom-type": "throttle-group", | ||
128 | + "id": "group0", | ||
129 | + "props" : { | ||
130 | + "limits": { | ||
131 | + "iops-total": 1000 | ||
132 | + } | ||
133 | + } | ||
134 | + } | ||
135 | +} | ||
136 | +{ "execute" : "qom-get", | ||
137 | + "arguments" : { | ||
138 | + "path" : "group0", | ||
139 | + "property" : "limits" | ||
140 | + } | ||
141 | +} | ||
142 | +{ "execute" : "qom-set", | ||
143 | + "arguments" : { | ||
144 | + "path" : "group0", | ||
145 | + "property" : "limits", | ||
146 | + "value" : { | ||
147 | + "iops-total" : 0 | ||
148 | + } | ||
149 | + } | ||
150 | +} | ||
151 | +{ "execute" : "qom-get", | ||
152 | + "arguments" : { | ||
153 | + "path" : "group0", | ||
154 | + "property" : "limits" | ||
155 | + } | ||
156 | +} | ||
157 | +{ "execute": "quit" } | ||
158 | +EOF | ||
159 | + | ||
160 | +echo | ||
161 | +echo "== object creation/set errors ==" | ||
162 | + | ||
163 | +run_qemu <<EOF | ||
164 | +{ "execute": "qmp_capabilities" } | ||
165 | +{ "execute": "object-add", | ||
166 | + "arguments": { | ||
167 | + "qom-type": "throttle-group", | ||
168 | + "id": "group0", | ||
169 | + "props" : { | ||
170 | + "limits": { | ||
171 | + "iops-total": 1000 | ||
172 | + } | ||
173 | + } | ||
174 | + } | ||
175 | +} | ||
176 | +{ "execute" : "qom-set", | ||
177 | + "arguments" : { | ||
178 | + "path" : "group0", | ||
179 | + "property" : "x-iops-total", | ||
180 | + "value" : 0 | ||
181 | + } | ||
182 | +} | ||
183 | +{ "execute" : "qom-set", | ||
184 | + "arguments" : { | ||
185 | + "path" : "group0", | ||
186 | + "property" : "limits", | ||
187 | + "value" : { | ||
188 | + "iops-total" : 10, | ||
189 | + "iops-read" : 10 | ||
190 | + } | ||
191 | + } | ||
192 | +} | ||
193 | +{ "execute": "quit" } | ||
194 | +EOF | ||
195 | + | ||
196 | +echo | ||
197 | +echo "== don't specify group ==" | ||
198 | + | ||
199 | +run_qemu <<EOF | ||
200 | +{ "execute": "qmp_capabilities" } | ||
201 | +{ "execute": "blockdev-add", | ||
202 | + "arguments": { | ||
203 | + "driver": "$IMGFMT", | ||
204 | + "node-name": "disk0", | ||
205 | + "file": { | ||
206 | + "driver": "file", | ||
207 | + "filename": "$TEST_IMG" | ||
208 | + } | ||
209 | + } | ||
210 | +} | ||
211 | +{ "execute": "blockdev-add", | ||
212 | + "arguments": { | ||
213 | + "driver": "throttle", | ||
214 | + "node-name": "throttle0", | ||
215 | + "file": "disk0" | ||
216 | + } | ||
217 | +} | ||
218 | +{ "execute": "quit" } | ||
219 | +EOF | ||
220 | + | ||
221 | +echo | ||
222 | +# success, all done | ||
223 | +echo "*** done" | ||
224 | +rm -f $seq.full | ||
225 | +status=0 | ||
226 | diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out | ||
227 | new file mode 100644 | ||
228 | index XXXXXXX..XXXXXXX | ||
229 | --- /dev/null | ||
230 | +++ b/tests/qemu-iotests/184.out | ||
231 | @@ -XXX,XX +XXX,XX @@ | ||
232 | +QA output created by 184 | ||
233 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 | ||
234 | + | ||
235 | +== checking interface == | ||
236 | +Testing: | ||
237 | +{ | ||
238 | + QMP_VERSION | ||
239 | +} | ||
240 | +{ | ||
241 | + "return": { | ||
242 | + } | ||
243 | +} | ||
244 | +{ | ||
245 | + "return": { | ||
246 | + } | ||
247 | +} | ||
248 | +{ | ||
249 | + "return": { | ||
250 | + } | ||
251 | +} | ||
252 | +{ | ||
253 | + "return": { | ||
254 | + } | ||
255 | +} | ||
256 | +{ | ||
257 | + "return": [ | ||
258 | + { | ||
259 | + "iops_rd": 0, | ||
260 | + "detect_zeroes": "off", | ||
261 | + "image": { | ||
262 | + "virtual-size": 67108864, | ||
263 | + "filename": "json:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}", | ||
264 | + "cluster-size": 65536, | ||
265 | + "format": "throttle", | ||
266 | + "actual-size": 200704, | ||
267 | + "dirty-flag": false | ||
268 | + }, | ||
269 | + "iops_wr": 0, | ||
270 | + "ro": false, | ||
271 | + "node-name": "throttle0", | ||
272 | + "backing_file_depth": 0, | ||
273 | + "drv": "throttle", | ||
274 | + "iops": 0, | ||
275 | + "bps_wr": 0, | ||
276 | + "write_threshold": 0, | ||
277 | + "encrypted": false, | ||
278 | + "bps": 0, | ||
279 | + "bps_rd": 0, | ||
280 | + "cache": { | ||
281 | + "no-flush": false, | ||
282 | + "direct": false, | ||
283 | + "writeback": true | ||
284 | + }, | ||
285 | + "file": "json:{\"throttle-group\": \"group0\", \"driver\": \"throttle\", \"file\": {\"driver\": \"qcow2\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/t.qcow2\"}}}", | ||
286 | + "encryption_key_missing": false | ||
287 | + }, | ||
288 | + { | ||
289 | + "iops_rd": 0, | ||
290 | + "detect_zeroes": "off", | ||
291 | + "image": { | ||
292 | + "virtual-size": 67108864, | ||
293 | + "filename": "TEST_DIR/t.qcow2", | ||
294 | + "cluster-size": 65536, | ||
295 | + "format": "qcow2", | ||
296 | + "actual-size": 200704, | ||
297 | + "format-specific": { | ||
298 | + "type": "qcow2", | ||
299 | + "data": { | ||
300 | + "compat": "1.1", | ||
301 | + "lazy-refcounts": false, | ||
302 | + "refcount-bits": 16, | ||
303 | + "corrupt": false | ||
304 | + } | ||
305 | + }, | ||
306 | + "dirty-flag": false | ||
307 | + }, | ||
308 | + "iops_wr": 0, | ||
309 | + "ro": false, | ||
310 | + "node-name": "disk0", | ||
311 | + "backing_file_depth": 0, | ||
312 | + "drv": "qcow2", | ||
313 | + "iops": 0, | ||
314 | + "bps_wr": 0, | ||
315 | + "write_threshold": 0, | ||
316 | + "encrypted": false, | ||
317 | + "bps": 0, | ||
318 | + "bps_rd": 0, | ||
319 | + "cache": { | ||
320 | + "no-flush": false, | ||
321 | + "direct": false, | ||
322 | + "writeback": true | ||
323 | + }, | ||
324 | + "file": "TEST_DIR/t.qcow2", | ||
325 | + "encryption_key_missing": false | ||
326 | + }, | ||
327 | + { | ||
328 | + "iops_rd": 0, | ||
329 | + "detect_zeroes": "off", | ||
330 | + "image": { | ||
331 | + "virtual-size": 197120, | ||
332 | + "filename": "TEST_DIR/t.qcow2", | ||
333 | + "format": "file", | ||
334 | + "actual-size": 200704, | ||
335 | + "dirty-flag": false | ||
336 | + }, | ||
337 | + "iops_wr": 0, | ||
338 | + "ro": false, | ||
339 | + "node-name": "NODE_NAME", | ||
340 | + "backing_file_depth": 0, | ||
341 | + "drv": "file", | ||
342 | + "iops": 0, | ||
343 | + "bps_wr": 0, | ||
344 | + "write_threshold": 0, | ||
345 | + "encrypted": false, | ||
346 | + "bps": 0, | ||
347 | + "bps_rd": 0, | ||
348 | + "cache": { | ||
349 | + "no-flush": false, | ||
350 | + "direct": false, | ||
351 | + "writeback": true | ||
352 | + }, | ||
353 | + "file": "TEST_DIR/t.qcow2", | ||
354 | + "encryption_key_missing": false | ||
355 | + } | ||
356 | + ] | ||
357 | +} | ||
358 | +{ | ||
359 | + "return": [ | ||
360 | + ] | ||
361 | +} | ||
362 | +{ | ||
363 | + "return": { | ||
364 | + } | ||
365 | +} | ||
366 | +{ | ||
367 | + "timestamp": { | ||
368 | + "seconds": TIMESTAMP, | ||
369 | + "microseconds": TIMESTAMP | ||
370 | + }, | ||
371 | + "event": "SHUTDOWN", | ||
372 | + "data": { | ||
373 | + "guest": false | ||
374 | + } | 76 | + } |
375 | +} | 77 | +} |
376 | + | 78 | + |
377 | + | 79 | /* |
378 | +== property changes in ThrottleGroup == | 80 | * Forwards an already correctly aligned write request to the BlockDriver, |
379 | +Testing: | 81 | * after possibly fragmenting it. |
380 | +{ | 82 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, |
381 | + QMP_VERSION | 83 | { |
382 | +} | 84 | BlockDriverState *bs = child->bs; |
383 | +{ | 85 | BlockDriver *drv = bs->drv; |
384 | + "return": { | 86 | - bool waited; |
385 | + } | 87 | int ret; |
386 | +} | 88 | |
387 | +{ | 89 | - int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); |
388 | + "return": { | 90 | uint64_t bytes_remaining = bytes; |
389 | + } | 91 | int max_transfer; |
390 | +} | 92 | |
391 | +{ | 93 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, |
392 | + "return": { | 94 | assert((offset & (align - 1)) == 0); |
393 | + "bps-read-max-length": 1, | 95 | assert((bytes & (align - 1)) == 0); |
394 | + "iops-read-max-length": 1, | 96 | assert(!qiov || bytes == qiov->size); |
395 | + "bps-read-max": 0, | 97 | - assert((bs->open_flags & BDRV_O_NO_IO) == 0); |
396 | + "bps-total": 0, | 98 | - assert(!(flags & ~BDRV_REQ_MASK)); |
397 | + "iops-total-max-length": 1, | 99 | max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), |
398 | + "iops-total": 1000, | 100 | align); |
399 | + "iops-write-max": 0, | 101 | |
400 | + "bps-write": 0, | 102 | - /* BDRV_REQ_NO_SERIALISING is only for read operation */ |
401 | + "bps-total-max": 0, | 103 | - assert(!(flags & BDRV_REQ_NO_SERIALISING)); |
402 | + "bps-write-max": 0, | 104 | - |
403 | + "iops-size": 0, | 105 | - if (flags & BDRV_REQ_SERIALISING) { |
404 | + "iops-read": 0, | 106 | - mark_request_serialising(req, bdrv_get_cluster_size(bs)); |
405 | + "iops-write-max-length": 1, | 107 | - } |
406 | + "iops-write": 0, | 108 | - |
407 | + "bps-total-max-length": 1, | 109 | - waited = wait_serialising_requests(req); |
408 | + "iops-read-max": 0, | 110 | - assert(!waited || !req->serialising || |
409 | + "bps-read": 0, | 111 | - is_request_serialising_and_aligned(req)); |
410 | + "bps-write-max-length": 1, | 112 | - assert(req->overlap_offset <= offset); |
411 | + "iops-total-max": 0 | 113 | - assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); |
412 | + } | 114 | - if (flags & BDRV_REQ_WRITE_UNCHANGED) { |
413 | +} | 115 | - assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); |
414 | +{ | 116 | - } else { |
415 | + "return": { | 117 | - assert(child->perm & BLK_PERM_WRITE); |
416 | + } | 118 | - } |
417 | +} | 119 | - assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); |
418 | +{ | 120 | - |
419 | + "return": { | 121 | - ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); |
420 | + "bps-read-max-length": 1, | 122 | + ret = bdrv_co_write_req_prepare(child, offset, bytes, req, flags); |
421 | + "iops-read-max-length": 1, | 123 | |
422 | + "bps-read-max": 0, | 124 | if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && |
423 | + "bps-total": 0, | 125 | !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes && |
424 | + "iops-total-max-length": 1, | 126 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, |
425 | + "iops-total": 0, | 127 | } |
426 | + "iops-write-max": 0, | 128 | bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); |
427 | + "bps-write": 0, | 129 | |
428 | + "bps-total-max": 0, | 130 | - atomic_inc(&bs->write_gen); |
429 | + "bps-write-max": 0, | 131 | - bdrv_set_dirty(bs, offset, bytes); |
430 | + "iops-size": 0, | 132 | - |
431 | + "iops-read": 0, | 133 | - stat64_max(&bs->wr_highest_offset, offset + bytes); |
432 | + "iops-write-max-length": 1, | 134 | - |
433 | + "iops-write": 0, | 135 | if (ret >= 0) { |
434 | + "bps-total-max-length": 1, | 136 | - bs->total_sectors = MAX(bs->total_sectors, end_sector); |
435 | + "iops-read-max": 0, | 137 | ret = 0; |
436 | + "bps-read": 0, | 138 | } |
437 | + "bps-write-max-length": 1, | 139 | + bdrv_co_write_req_finish(child, offset, bytes, req, ret); |
438 | + "iops-total-max": 0 | 140 | |
439 | + } | 141 | return ret; |
440 | +} | 142 | } |
441 | +{ | 143 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, |
442 | + "return": { | 144 | if (!bs->drv) { |
443 | + } | 145 | return -ENOMEDIUM; |
444 | +} | 146 | } |
445 | +{ | 147 | - if (bs->read_only) { |
446 | + "timestamp": { | 148 | - return -EPERM; |
447 | + "seconds": TIMESTAMP, | 149 | - } |
448 | + "microseconds": TIMESTAMP | 150 | - assert(!(bs->open_flags & BDRV_O_INACTIVE)); |
449 | + }, | 151 | |
450 | + "event": "SHUTDOWN", | 152 | ret = bdrv_check_byte_request(bs, offset, bytes); |
451 | + "data": { | 153 | if (ret < 0) { |
452 | + "guest": false | ||
453 | + } | ||
454 | +} | ||
455 | + | ||
456 | + | ||
457 | +== object creation/set errors == | ||
458 | +Testing: | ||
459 | +{ | ||
460 | + QMP_VERSION | ||
461 | +} | ||
462 | +{ | ||
463 | + "return": { | ||
464 | + } | ||
465 | +} | ||
466 | +{ | ||
467 | + "return": { | ||
468 | + } | ||
469 | +} | ||
470 | +{ | ||
471 | + "error": { | ||
472 | + "class": "GenericError", | ||
473 | + "desc": "Property cannot be set after initialization" | ||
474 | + } | ||
475 | +} | ||
476 | +{ | ||
477 | + "error": { | ||
478 | + "class": "GenericError", | ||
479 | + "desc": "bps/iops/max total values and read/write values cannot be used at the same time" | ||
480 | + } | ||
481 | +} | ||
482 | +{ | ||
483 | + "return": { | ||
484 | + } | ||
485 | +} | ||
486 | +{ | ||
487 | + "timestamp": { | ||
488 | + "seconds": TIMESTAMP, | ||
489 | + "microseconds": TIMESTAMP | ||
490 | + }, | ||
491 | + "event": "SHUTDOWN", | ||
492 | + "data": { | ||
493 | + "guest": false | ||
494 | + } | ||
495 | +} | ||
496 | + | ||
497 | + | ||
498 | +== don't specify group == | ||
499 | +Testing: | ||
500 | +{ | ||
501 | + QMP_VERSION | ||
502 | +} | ||
503 | +{ | ||
504 | + "return": { | ||
505 | + } | ||
506 | +} | ||
507 | +{ | ||
508 | + "return": { | ||
509 | + } | ||
510 | +} | ||
511 | +{ | ||
512 | + "error": { | ||
513 | + "class": "GenericError", | ||
514 | + "desc": "Parameter 'throttle-group' is missing" | ||
515 | + } | ||
516 | +} | ||
517 | +{ | ||
518 | + "return": { | ||
519 | + } | ||
520 | +} | ||
521 | +{ | ||
522 | + "timestamp": { | ||
523 | + "seconds": TIMESTAMP, | ||
524 | + "microseconds": TIMESTAMP | ||
525 | + }, | ||
526 | + "event": "SHUTDOWN", | ||
527 | + "data": { | ||
528 | + "guest": false | ||
529 | + } | ||
530 | +} | ||
531 | + | ||
532 | + | ||
533 | +*** done | ||
534 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
535 | index XXXXXXX..XXXXXXX 100644 | ||
536 | --- a/tests/qemu-iotests/group | ||
537 | +++ b/tests/qemu-iotests/group | ||
538 | @@ -XXX,XX +XXX,XX @@ | ||
539 | 181 rw auto migration | ||
540 | 182 rw auto quick | ||
541 | 183 rw auto migration | ||
542 | +184 rw auto quick | ||
543 | 185 rw auto | ||
544 | 186 rw auto | ||
545 | 187 rw auto | ||
546 | -- | 154 | -- |
547 | 2.13.5 | 155 | 2.13.6 |
548 | 156 | ||
549 | 157 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The old signature has an ambiguous meaning for a return of 0: | 3 | Two problems exist when a write request that enlarges the image (i.e. |
4 | either no allocation was requested or necessary, or an error | 4 | write beyond EOF) finishes: |
5 | occurred (but any errno associated with the error is lost to | ||
6 | the caller, which then has to assume EIO). | ||
7 | 5 | ||
8 | Better is to follow the example of qcow2, by changing the | 6 | 1) parent is not notified about size change; |
9 | signature to have a separate return value that cleanly | 7 | 2) dirty bitmap is not resized although we try to set the dirty bits; |
10 | distinguishes between failure and success, along with a | ||
11 | parameter that cleanly holds a 64-bit value. Then update all | ||
12 | callers. | ||
13 | 8 | ||
14 | While auditing that all return paths return a negative errno | 9 | Fix them just like how bdrv_co_truncate works. |
15 | (rather than -1), I also simplified places where we can pass | ||
16 | NULL rather than a local Error that just gets thrown away. | ||
17 | 10 | ||
18 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | 11 | Reported-by: Kevin Wolf <kwolf@redhat.com> |
19 | Signed-off-by: Eric Blake <eblake@redhat.com> | 12 | Signed-off-by: Fam Zheng <famz@redhat.com> |
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
21 | --- | 14 | --- |
22 | block/qcow.c | 123 +++++++++++++++++++++++++++++++++++------------------------ | 15 | block/io.c | 10 +++++++--- |
23 | 1 file changed, 73 insertions(+), 50 deletions(-) | 16 | 1 file changed, 7 insertions(+), 3 deletions(-) |
24 | 17 | ||
25 | diff --git a/block/qcow.c b/block/qcow.c | 18 | diff --git a/block/io.c b/block/io.c |
26 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/block/qcow.c | 20 | --- a/block/io.c |
28 | +++ b/block/qcow.c | 21 | +++ b/block/io.c |
29 | @@ -XXX,XX +XXX,XX @@ static int qcow_reopen_prepare(BDRVReopenState *state, | 22 | @@ -XXX,XX +XXX,XX @@ |
30 | * 'compressed_size'. 'compressed_size' must be > 0 and < | 23 | |
31 | * cluster_size | 24 | static AioWait drain_all_aio_wait; |
32 | * | 25 | |
33 | - * return 0 if not allocated. | 26 | +static void bdrv_parent_cb_resize(BlockDriverState *bs); |
34 | + * return 0 if not allocated, 1 if *result is assigned, and negative | 27 | static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, |
35 | + * errno on failure. | 28 | int64_t offset, int bytes, BdrvRequestFlags flags); |
36 | */ | 29 | |
37 | -static uint64_t get_cluster_offset(BlockDriverState *bs, | 30 | @@ -XXX,XX +XXX,XX @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, |
38 | - uint64_t offset, int allocate, | 31 | BlockDriverState *bs = child->bs; |
39 | - int compressed_size, | 32 | |
40 | - int n_start, int n_end) | 33 | atomic_inc(&bs->write_gen); |
41 | +static int get_cluster_offset(BlockDriverState *bs, | 34 | - bdrv_set_dirty(bs, offset, bytes); |
42 | + uint64_t offset, int allocate, | 35 | |
43 | + int compressed_size, | 36 | stat64_max(&bs->wr_highest_offset, offset + bytes); |
44 | + int n_start, int n_end, uint64_t *result) | 37 | |
45 | { | 38 | - if (ret == 0) { |
46 | BDRVQcowState *s = bs->opaque; | 39 | - bs->total_sectors = MAX(bs->total_sectors, end_sector); |
47 | - int min_index, i, j, l1_index, l2_index; | 40 | + if (ret == 0 && |
48 | + int min_index, i, j, l1_index, l2_index, ret; | 41 | + end_sector > bs->total_sectors) { |
49 | uint64_t l2_offset, *l2_table, cluster_offset, tmp; | 42 | + bs->total_sectors = end_sector; |
50 | uint32_t min_count; | 43 | + bdrv_parent_cb_resize(bs); |
51 | int new_l2_table; | 44 | + bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); |
52 | |||
53 | + *result = 0; | ||
54 | l1_index = offset >> (s->l2_bits + s->cluster_bits); | ||
55 | l2_offset = s->l1_table[l1_index]; | ||
56 | new_l2_table = 0; | ||
57 | @@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||
58 | /* update the L1 entry */ | ||
59 | s->l1_table[l1_index] = l2_offset; | ||
60 | tmp = cpu_to_be64(l2_offset); | ||
61 | - if (bdrv_pwrite_sync(bs->file, | ||
62 | - s->l1_table_offset + l1_index * sizeof(tmp), | ||
63 | - &tmp, sizeof(tmp)) < 0) | ||
64 | - return 0; | ||
65 | + ret = bdrv_pwrite_sync(bs->file, | ||
66 | + s->l1_table_offset + l1_index * sizeof(tmp), | ||
67 | + &tmp, sizeof(tmp)); | ||
68 | + if (ret < 0) { | ||
69 | + return ret; | ||
70 | + } | ||
71 | new_l2_table = 1; | ||
72 | } | 45 | } |
73 | for(i = 0; i < L2_CACHE_SIZE; i++) { | 46 | + bdrv_set_dirty(bs, offset, bytes); |
74 | @@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||
75 | l2_table = s->l2_cache + (min_index << s->l2_bits); | ||
76 | if (new_l2_table) { | ||
77 | memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||
78 | - if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, | ||
79 | - s->l2_size * sizeof(uint64_t)) < 0) | ||
80 | - return 0; | ||
81 | + ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table, | ||
82 | + s->l2_size * sizeof(uint64_t)); | ||
83 | + if (ret < 0) { | ||
84 | + return ret; | ||
85 | + } | ||
86 | } else { | ||
87 | - if (bdrv_pread(bs->file, l2_offset, l2_table, | ||
88 | - s->l2_size * sizeof(uint64_t)) != | ||
89 | - s->l2_size * sizeof(uint64_t)) | ||
90 | - return 0; | ||
91 | + ret = bdrv_pread(bs->file, l2_offset, l2_table, | ||
92 | + s->l2_size * sizeof(uint64_t)); | ||
93 | + if (ret < 0) { | ||
94 | + return ret; | ||
95 | + } | ||
96 | } | ||
97 | s->l2_cache_offsets[min_index] = l2_offset; | ||
98 | s->l2_cache_counts[min_index] = 1; | ||
99 | @@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||
100 | /* if the cluster is already compressed, we must | ||
101 | decompress it in the case it is not completely | ||
102 | overwritten */ | ||
103 | - if (decompress_cluster(bs, cluster_offset) < 0) | ||
104 | - return 0; | ||
105 | + if (decompress_cluster(bs, cluster_offset) < 0) { | ||
106 | + return -EIO; | ||
107 | + } | ||
108 | cluster_offset = bdrv_getlength(bs->file->bs); | ||
109 | cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||
110 | ~(s->cluster_size - 1); | ||
111 | /* write the cluster content */ | ||
112 | - if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, | ||
113 | - s->cluster_size) != | ||
114 | - s->cluster_size) | ||
115 | - return -1; | ||
116 | + ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, | ||
117 | + s->cluster_size); | ||
118 | + if (ret < 0) { | ||
119 | + return ret; | ||
120 | + } | ||
121 | } else { | ||
122 | cluster_offset = bdrv_getlength(bs->file->bs); | ||
123 | if (allocate == 1) { | ||
124 | @@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||
125 | s->cluster_data, | ||
126 | BDRV_SECTOR_SIZE, | ||
127 | NULL) < 0) { | ||
128 | - errno = EIO; | ||
129 | - return -1; | ||
130 | + return -EIO; | ||
131 | + } | ||
132 | + ret = bdrv_pwrite(bs->file, | ||
133 | + cluster_offset + i * 512, | ||
134 | + s->cluster_data, 512); | ||
135 | + if (ret < 0) { | ||
136 | + return ret; | ||
137 | } | ||
138 | - if (bdrv_pwrite(bs->file, | ||
139 | - cluster_offset + i * 512, | ||
140 | - s->cluster_data, 512) != 512) | ||
141 | - return -1; | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | @@ -XXX,XX +XXX,XX @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||
146 | /* update L2 table */ | ||
147 | tmp = cpu_to_be64(cluster_offset); | ||
148 | l2_table[l2_index] = tmp; | ||
149 | - if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), | ||
150 | - &tmp, sizeof(tmp)) < 0) | ||
151 | - return 0; | ||
152 | + ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), | ||
153 | + &tmp, sizeof(tmp)); | ||
154 | + if (ret < 0) { | ||
155 | + return ret; | ||
156 | + } | ||
157 | } | ||
158 | - return cluster_offset; | ||
159 | + *result = cluster_offset; | ||
160 | + return 1; | ||
161 | } | 47 | } |
162 | 48 | ||
163 | static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | 49 | /* |
164 | int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||
165 | { | ||
166 | BDRVQcowState *s = bs->opaque; | ||
167 | - int index_in_cluster, n; | ||
168 | + int index_in_cluster, n, ret; | ||
169 | uint64_t cluster_offset; | ||
170 | |||
171 | qemu_co_mutex_lock(&s->lock); | ||
172 | - cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); | ||
173 | + ret = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0, &cluster_offset); | ||
174 | qemu_co_mutex_unlock(&s->lock); | ||
175 | + if (ret < 0) { | ||
176 | + return ret; | ||
177 | + } | ||
178 | index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||
179 | n = s->cluster_sectors - index_in_cluster; | ||
180 | if (n > nb_sectors) | ||
181 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||
182 | |||
183 | while (nb_sectors != 0) { | ||
184 | /* prepare next request */ | ||
185 | - cluster_offset = get_cluster_offset(bs, sector_num << 9, | ||
186 | - 0, 0, 0, 0); | ||
187 | + ret = get_cluster_offset(bs, sector_num << 9, | ||
188 | + 0, 0, 0, 0, &cluster_offset); | ||
189 | + if (ret < 0) { | ||
190 | + break; | ||
191 | + } | ||
192 | index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||
193 | n = s->cluster_sectors - index_in_cluster; | ||
194 | if (n > nb_sectors) { | ||
195 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||
196 | ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); | ||
197 | qemu_co_mutex_lock(&s->lock); | ||
198 | if (ret < 0) { | ||
199 | - goto fail; | ||
200 | + break; | ||
201 | } | ||
202 | } else { | ||
203 | /* Note: in this case, no need to wait */ | ||
204 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||
205 | } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||
206 | /* add AIO support for compressed blocks ? */ | ||
207 | if (decompress_cluster(bs, cluster_offset) < 0) { | ||
208 | - goto fail; | ||
209 | + ret = -EIO; | ||
210 | + break; | ||
211 | } | ||
212 | memcpy(buf, | ||
213 | s->cluster_cache + index_in_cluster * 512, 512 * n); | ||
214 | } else { | ||
215 | if ((cluster_offset & 511) != 0) { | ||
216 | - goto fail; | ||
217 | + ret = -EIO; | ||
218 | + break; | ||
219 | } | ||
220 | hd_iov.iov_base = (void *)buf; | ||
221 | hd_iov.iov_len = n * 512; | ||
222 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||
223 | assert(s->crypto); | ||
224 | if (qcrypto_block_decrypt(s->crypto, sector_num, buf, | ||
225 | n * BDRV_SECTOR_SIZE, NULL) < 0) { | ||
226 | - goto fail; | ||
227 | + ret = -EIO; | ||
228 | + break; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||
233 | buf += n * 512; | ||
234 | } | ||
235 | |||
236 | -done: | ||
237 | qemu_co_mutex_unlock(&s->lock); | ||
238 | |||
239 | if (qiov->niov > 1) { | ||
240 | @@ -XXX,XX +XXX,XX @@ done: | ||
241 | } | ||
242 | |||
243 | return ret; | ||
244 | - | ||
245 | -fail: | ||
246 | - ret = -EIO; | ||
247 | - goto done; | ||
248 | } | ||
249 | |||
250 | static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
251 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
252 | if (n > nb_sectors) { | ||
253 | n = nb_sectors; | ||
254 | } | ||
255 | - cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, | ||
256 | - index_in_cluster, | ||
257 | - index_in_cluster + n); | ||
258 | + ret = get_cluster_offset(bs, sector_num << 9, 1, 0, | ||
259 | + index_in_cluster, | ||
260 | + index_in_cluster + n, &cluster_offset); | ||
261 | + if (ret < 0) { | ||
262 | + break; | ||
263 | + } | ||
264 | if (!cluster_offset || (cluster_offset & 511) != 0) { | ||
265 | ret = -EIO; | ||
266 | break; | ||
267 | @@ -XXX,XX +XXX,XX @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, | ||
268 | goto success; | ||
269 | } | ||
270 | qemu_co_mutex_lock(&s->lock); | ||
271 | - cluster_offset = get_cluster_offset(bs, offset, 2, out_len, 0, 0); | ||
272 | + ret = get_cluster_offset(bs, offset, 2, out_len, 0, 0, &cluster_offset); | ||
273 | qemu_co_mutex_unlock(&s->lock); | ||
274 | + if (ret < 0) { | ||
275 | + goto fail; | ||
276 | + } | ||
277 | if (cluster_offset == 0) { | ||
278 | ret = -EIO; | ||
279 | goto fail; | ||
280 | -- | 50 | -- |
281 | 2.13.5 | 51 | 2.13.6 |
282 | 52 | ||
283 | 53 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Now that bdrv_truncate is passed to bs->file by default, remove the | 3 | Reuse the new bdrv_co_write_req_prepare/finish helpers. The variation |
4 | callback from block/blkdebug.c and set is_filter to true. is_filter also gives | 4 | here is that discard requests don't affect bs->wr_highest_offset, and it |
5 | access to other callbacks that are forwarded automatically to bs->file for | 5 | cannot extend the image. |
6 | filters. | ||
7 | 6 | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 7 | Signed-off-by: Fam Zheng <famz@redhat.com> |
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 9 | --- |
13 | block/blkdebug.c | 8 +------- | 10 | block/io.c | 33 +++++++++++++++++++++++---------- |
14 | 1 file changed, 1 insertion(+), 7 deletions(-) | 11 | 1 file changed, 23 insertions(+), 10 deletions(-) |
15 | 12 | ||
16 | diff --git a/block/blkdebug.c b/block/blkdebug.c | 13 | diff --git a/block/io.c b/block/io.c |
17 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/blkdebug.c | 15 | --- a/block/io.c |
19 | +++ b/block/blkdebug.c | 16 | +++ b/block/io.c |
20 | @@ -XXX,XX +XXX,XX @@ static int64_t blkdebug_getlength(BlockDriverState *bs) | 17 | @@ -XXX,XX +XXX,XX @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, |
21 | return bdrv_getlength(bs->file->bs); | 18 | |
19 | atomic_inc(&bs->write_gen); | ||
20 | |||
21 | - stat64_max(&bs->wr_highest_offset, offset + bytes); | ||
22 | - | ||
23 | + /* | ||
24 | + * Discard cannot extend the image, but in error handling cases, such as | ||
25 | + * when reverting a qcow2 cluster allocation, the discarded range can pass | ||
26 | + * the end of image file, so we cannot assert about BDRV_TRACKED_DISCARD | ||
27 | + * here. Instead, just skip it, since semantically a discard request | ||
28 | + * beyond EOF cannot expand the image anyway. | ||
29 | + */ | ||
30 | if (ret == 0 && | ||
31 | - end_sector > bs->total_sectors) { | ||
32 | + end_sector > bs->total_sectors && | ||
33 | + req->type != BDRV_TRACKED_DISCARD) { | ||
34 | bs->total_sectors = end_sector; | ||
35 | bdrv_parent_cb_resize(bs); | ||
36 | bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); | ||
37 | } | ||
38 | - bdrv_set_dirty(bs, offset, bytes); | ||
39 | + if (req->bytes) { | ||
40 | + switch (req->type) { | ||
41 | + case BDRV_TRACKED_WRITE: | ||
42 | + stat64_max(&bs->wr_highest_offset, offset + bytes); | ||
43 | + /* fall through, to set dirty bits */ | ||
44 | + case BDRV_TRACKED_DISCARD: | ||
45 | + bdrv_set_dirty(bs, offset, bytes); | ||
46 | + break; | ||
47 | + default: | ||
48 | + break; | ||
49 | + } | ||
50 | + } | ||
22 | } | 51 | } |
23 | 52 | ||
24 | -static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, | 53 | /* |
25 | - PreallocMode prealloc, Error **errp) | 54 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) |
26 | -{ | 55 | ret = bdrv_check_byte_request(bs, offset, bytes); |
27 | - return bdrv_truncate(bs->file, offset, prealloc, errp); | 56 | if (ret < 0) { |
28 | -} | 57 | return ret; |
29 | - | 58 | - } else if (bs->read_only) { |
30 | static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | 59 | - return -EPERM; |
31 | { | 60 | } |
32 | BDRVBlkdebugState *s = bs->opaque; | 61 | - assert(!(bs->open_flags & BDRV_O_INACTIVE)); |
33 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | 62 | |
34 | .format_name = "blkdebug", | 63 | /* Do nothing if disabled. */ |
35 | .protocol_name = "blkdebug", | 64 | if (!(bs->open_flags & BDRV_O_UNMAP)) { |
36 | .instance_size = sizeof(BDRVBlkdebugState), | 65 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) |
37 | + .is_filter = true, | 66 | bdrv_inc_in_flight(bs); |
38 | 67 | tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_DISCARD); | |
39 | .bdrv_parse_filename = blkdebug_parse_filename, | 68 | |
40 | .bdrv_file_open = blkdebug_open, | 69 | - ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req); |
41 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | 70 | + ret = bdrv_co_write_req_prepare(child, offset, bytes, &req, 0); |
42 | .bdrv_child_perm = bdrv_filter_default_perms, | 71 | if (ret < 0) { |
43 | 72 | goto out; | |
44 | .bdrv_getlength = blkdebug_getlength, | 73 | } |
45 | - .bdrv_truncate = blkdebug_truncate, | 74 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) |
46 | .bdrv_refresh_filename = blkdebug_refresh_filename, | 75 | } |
47 | .bdrv_refresh_limits = blkdebug_refresh_limits, | 76 | ret = 0; |
48 | 77 | out: | |
78 | - atomic_inc(&bs->write_gen); | ||
79 | - bdrv_set_dirty(bs, req.offset, req.bytes); | ||
80 | + bdrv_co_write_req_finish(child, req.offset, req.bytes, &req, ret); | ||
81 | tracked_request_end(&req); | ||
82 | bdrv_dec_in_flight(bs); | ||
83 | return ret; | ||
49 | -- | 84 | -- |
50 | 2.13.5 | 85 | 2.13.6 |
51 | 86 | ||
52 | 87 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fam Zheng <famz@redhat.com> | ||
1 | 2 | ||
3 | This brings the request handling logic inline with write and discard, | ||
4 | fixing write_gen, resize_cb, dirty bitmaps and image size refreshing. | ||
5 | The last of these issues broke iotest case 222, which is now fixed. | ||
6 | |||
7 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | block/io.c | 24 ++++++++++-------------- | ||
11 | 1 file changed, 10 insertions(+), 14 deletions(-) | ||
12 | |||
13 | diff --git a/block/io.c b/block/io.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block/io.c | ||
16 | +++ b/block/io.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal( | ||
18 | bdrv_inc_in_flight(dst->bs); | ||
19 | tracked_request_begin(&req, dst->bs, dst_offset, bytes, | ||
20 | BDRV_TRACKED_WRITE); | ||
21 | - | ||
22 | - /* BDRV_REQ_NO_SERIALISING is only for read operation */ | ||
23 | - assert(!(write_flags & BDRV_REQ_NO_SERIALISING)); | ||
24 | - if (write_flags & BDRV_REQ_SERIALISING) { | ||
25 | - mark_request_serialising(&req, bdrv_get_cluster_size(dst->bs)); | ||
26 | - } | ||
27 | - wait_serialising_requests(&req); | ||
28 | - | ||
29 | - ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, | ||
30 | - src, src_offset, | ||
31 | - dst, dst_offset, | ||
32 | - bytes, | ||
33 | - read_flags, write_flags); | ||
34 | - | ||
35 | + ret = bdrv_co_write_req_prepare(dst, dst_offset, bytes, &req, | ||
36 | + write_flags); | ||
37 | + if (!ret) { | ||
38 | + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, | ||
39 | + src, src_offset, | ||
40 | + dst, dst_offset, | ||
41 | + bytes, | ||
42 | + read_flags, write_flags); | ||
43 | + } | ||
44 | + bdrv_co_write_req_finish(dst, dst_offset, bytes, &req, ret); | ||
45 | tracked_request_end(&req); | ||
46 | bdrv_dec_in_flight(dst->bs); | ||
47 | } | ||
48 | -- | ||
49 | 2.13.6 | ||
50 | |||
51 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Omitting the check for whether bdrv_getlength() and bdrv_truncate() | 3 | If we are growing the image and potentially using preallocation for the |
4 | failed meant that it was theoretically possible to return an | 4 | new area, we need to make sure that no write requests are made to the |
5 | incorrect offset to the caller. More likely, conditions for either | 5 | "preallocated" area which is [@old_size, @offset), not |
6 | of these functions to fail would also cause one of our other calls | 6 | [@offset, offset * 2 - @old_size). |
7 | (such as bdrv_pread() or bdrv_pwrite_sync()) to also fail, but | ||
8 | auditing that we are safe is difficult compared to just patching | ||
9 | things to always forward on the error rather than ignoring it. | ||
10 | 7 | ||
11 | Use osdep.h macros instead of open-coded rounding while in the | 8 | Signed-off-by: Fam Zheng <famz@redhat.com> |
12 | area. | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
13 | |||
14 | Reported-by: Markus Armbruster <armbru@redhat.com> | ||
15 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 11 | --- |
18 | block/qcow.c | 30 ++++++++++++++++++++++-------- | 12 | block/io.c | 3 ++- |
19 | 1 file changed, 22 insertions(+), 8 deletions(-) | 13 | 1 file changed, 2 insertions(+), 1 deletion(-) |
20 | 14 | ||
21 | diff --git a/block/qcow.c b/block/qcow.c | 15 | diff --git a/block/io.c b/block/io.c |
22 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/block/qcow.c | 17 | --- a/block/io.c |
24 | +++ b/block/qcow.c | 18 | +++ b/block/io.c |
25 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | 19 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, |
26 | { | 20 | } |
27 | BDRVQcowState *s = bs->opaque; | 21 | |
28 | int min_index, i, j, l1_index, l2_index, ret; | 22 | bdrv_inc_in_flight(bs); |
29 | - uint64_t l2_offset, *l2_table, cluster_offset, tmp; | 23 | - tracked_request_begin(&req, bs, offset, new_bytes, BDRV_TRACKED_TRUNCATE); |
30 | + int64_t l2_offset; | 24 | + tracked_request_begin(&req, bs, offset - new_bytes, new_bytes, |
31 | + uint64_t *l2_table, cluster_offset, tmp; | 25 | + BDRV_TRACKED_TRUNCATE); |
32 | uint32_t min_count; | 26 | |
33 | int new_l2_table; | 27 | /* If we are growing the image and potentially using preallocation for the |
34 | 28 | * new area, we need to make sure that no write requests are made to it | |
35 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
36 | return 0; | ||
37 | /* allocate a new l2 entry */ | ||
38 | l2_offset = bdrv_getlength(bs->file->bs); | ||
39 | + if (l2_offset < 0) { | ||
40 | + return l2_offset; | ||
41 | + } | ||
42 | /* round to cluster size */ | ||
43 | - l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); | ||
44 | + l2_offset = QEMU_ALIGN_UP(l2_offset, s->cluster_size); | ||
45 | /* update the L1 entry */ | ||
46 | s->l1_table[l1_index] = l2_offset; | ||
47 | tmp = cpu_to_be64(l2_offset); | ||
48 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
49 | return -EIO; | ||
50 | } | ||
51 | cluster_offset = bdrv_getlength(bs->file->bs); | ||
52 | - cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||
53 | - ~(s->cluster_size - 1); | ||
54 | + if ((int64_t) cluster_offset < 0) { | ||
55 | + return cluster_offset; | ||
56 | + } | ||
57 | + cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size); | ||
58 | /* write the cluster content */ | ||
59 | ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, | ||
60 | s->cluster_size); | ||
61 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
62 | } | ||
63 | } else { | ||
64 | cluster_offset = bdrv_getlength(bs->file->bs); | ||
65 | + if ((int64_t) cluster_offset < 0) { | ||
66 | + return cluster_offset; | ||
67 | + } | ||
68 | if (allocate == 1) { | ||
69 | /* round to cluster size */ | ||
70 | - cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||
71 | - ~(s->cluster_size - 1); | ||
72 | - bdrv_truncate(bs->file, cluster_offset + s->cluster_size, | ||
73 | - PREALLOC_MODE_OFF, NULL); | ||
74 | + cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size); | ||
75 | + if (cluster_offset + s->cluster_size > INT64_MAX) { | ||
76 | + return -E2BIG; | ||
77 | + } | ||
78 | + ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size, | ||
79 | + PREALLOC_MODE_OFF, NULL); | ||
80 | + if (ret < 0) { | ||
81 | + return ret; | ||
82 | + } | ||
83 | /* if encrypted, we must initialize the cluster | ||
84 | content which won't be written */ | ||
85 | if (bs->encrypted && | ||
86 | -- | 29 | -- |
87 | 2.13.5 | 30 | 2.13.6 |
88 | 31 | ||
89 | 32 | diff view generated by jsdifflib |
1 | From: Pavel Butsykin <pbutsykin@virtuozzo.com> | 1 | From: Fam Zheng <famz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | After calling qcow2_inactivate(), all qcow2 caches must be flushed, but this | 3 | Truncation is the last to convert from open coded req handling to |
4 | may not happen, because the last call qcow2_store_persistent_dirty_bitmaps() | 4 | reusing helpers. This time the permission check in prepare has to adapt |
5 | can lead to marking l2/refcont cache as dirty. | 5 | to the new caller: it checks a different permission bit, and doesn't |
6 | trigger the before write notifier. | ||
6 | 7 | ||
7 | Let's move qcow2_store_persistent_dirty_bitmaps() before the caсhe flushing | 8 | Also, truncation should always trigger a bs->total_sectors update and in |
8 | to fix it. | 9 | turn call parent resize_cb. Update the condition in finish helper, too. |
9 | 10 | ||
10 | Cc: qemu-stable@nongnu.org | 11 | It's intended to do a duplicated bs->read_only check before calling |
11 | Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com> | 12 | bdrv_co_write_req_prepare() so that we can be more informative with the |
13 | error message, as bdrv_co_write_req_prepare() doesn't have Error | ||
14 | parameter. | ||
15 | |||
16 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 18 | --- |
14 | block/qcow2.c | 16 ++++++++-------- | 19 | block/io.c | 55 +++++++++++++++++++++++++++++++++++-------------------- |
15 | 1 file changed, 8 insertions(+), 8 deletions(-) | 20 | 1 file changed, 35 insertions(+), 20 deletions(-) |
16 | 21 | ||
17 | diff --git a/block/qcow2.c b/block/qcow2.c | 22 | diff --git a/block/io.c b/block/io.c |
18 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block/qcow2.c | 24 | --- a/block/io.c |
20 | +++ b/block/qcow2.c | 25 | +++ b/block/io.c |
21 | @@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs) | 26 | @@ -XXX,XX +XXX,XX @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, |
22 | int ret, result = 0; | 27 | is_request_serialising_and_aligned(req)); |
23 | Error *local_err = NULL; | 28 | assert(req->overlap_offset <= offset); |
24 | 29 | assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); | |
25 | + qcow2_store_persistent_dirty_bitmaps(bs, &local_err); | 30 | + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); |
26 | + if (local_err != NULL) { | 31 | |
27 | + result = -EINVAL; | 32 | - if (flags & BDRV_REQ_WRITE_UNCHANGED) { |
28 | + error_report_err(local_err); | 33 | - assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); |
29 | + error_report("Persistent bitmaps are lost for node '%s'", | 34 | - } else { |
30 | + bdrv_get_device_or_node_name(bs)); | 35 | - assert(child->perm & BLK_PERM_WRITE); |
36 | + switch (req->type) { | ||
37 | + case BDRV_TRACKED_WRITE: | ||
38 | + case BDRV_TRACKED_DISCARD: | ||
39 | + if (flags & BDRV_REQ_WRITE_UNCHANGED) { | ||
40 | + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); | ||
41 | + } else { | ||
42 | + assert(child->perm & BLK_PERM_WRITE); | ||
43 | + } | ||
44 | + return notifier_with_return_list_notify(&bs->before_write_notifiers, | ||
45 | + req); | ||
46 | + case BDRV_TRACKED_TRUNCATE: | ||
47 | + assert(child->perm & BLK_PERM_RESIZE); | ||
48 | + return 0; | ||
49 | + default: | ||
50 | + abort(); | ||
51 | } | ||
52 | - assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); | ||
53 | - return notifier_with_return_list_notify(&bs->before_write_notifiers, req); | ||
54 | } | ||
55 | |||
56 | static inline void coroutine_fn | ||
57 | @@ -XXX,XX +XXX,XX @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, | ||
58 | * beyond EOF cannot expand the image anyway. | ||
59 | */ | ||
60 | if (ret == 0 && | ||
61 | - end_sector > bs->total_sectors && | ||
62 | - req->type != BDRV_TRACKED_DISCARD) { | ||
63 | + (req->type == BDRV_TRACKED_TRUNCATE || | ||
64 | + end_sector > bs->total_sectors) && | ||
65 | + req->type != BDRV_TRACKED_DISCARD) { | ||
66 | bs->total_sectors = end_sector; | ||
67 | bdrv_parent_cb_resize(bs); | ||
68 | bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); | ||
69 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, | ||
70 | int64_t old_size, new_bytes; | ||
71 | int ret; | ||
72 | |||
73 | - assert(child->perm & BLK_PERM_RESIZE); | ||
74 | |||
75 | /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ | ||
76 | if (!drv) { | ||
77 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, | ||
78 | * concurrently or they might be overwritten by preallocation. */ | ||
79 | if (new_bytes) { | ||
80 | mark_request_serialising(&req, 1); | ||
81 | - wait_serialising_requests(&req); | ||
31 | + } | 82 | + } |
32 | + | 83 | + if (bs->read_only) { |
33 | ret = qcow2_cache_flush(bs, s->l2_table_cache); | 84 | + error_setg(errp, "Image is read-only"); |
34 | if (ret) { | 85 | + ret = -EACCES; |
35 | result = ret; | 86 | + goto out; |
36 | @@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs) | 87 | + } |
37 | strerror(-ret)); | 88 | + ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req, |
89 | + 0); | ||
90 | + if (ret < 0) { | ||
91 | + error_setg_errno(errp, -ret, | ||
92 | + "Failed to prepare request for truncation"); | ||
93 | + goto out; | ||
38 | } | 94 | } |
39 | 95 | ||
40 | - qcow2_store_persistent_dirty_bitmaps(bs, &local_err); | 96 | if (!drv->bdrv_co_truncate) { |
41 | - if (local_err != NULL) { | 97 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, |
42 | - result = -EINVAL; | 98 | ret = -ENOTSUP; |
43 | - error_report_err(local_err); | 99 | goto out; |
44 | - error_report("Persistent bitmaps are lost for node '%s'", | 100 | } |
45 | - bdrv_get_device_or_node_name(bs)); | 101 | - if (bs->read_only) { |
102 | - error_setg(errp, "Image is read-only"); | ||
103 | - ret = -EACCES; | ||
104 | - goto out; | ||
46 | - } | 105 | - } |
47 | - | 106 | - |
48 | if (result == 0) { | 107 | - assert(!(bs->open_flags & BDRV_O_INACTIVE)); |
49 | qcow2_mark_clean(bs); | 108 | |
109 | ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); | ||
110 | if (ret < 0) { | ||
111 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, | ||
112 | } else { | ||
113 | offset = bs->total_sectors * BDRV_SECTOR_SIZE; | ||
50 | } | 114 | } |
115 | - bdrv_dirty_bitmap_truncate(bs, offset); | ||
116 | - bdrv_parent_cb_resize(bs); | ||
117 | - atomic_inc(&bs->write_gen); | ||
118 | + /* It's possible that truncation succeeded but refresh_total_sectors | ||
119 | + * failed, but the latter doesn't affect how we should finish the request. | ||
120 | + * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. */ | ||
121 | + bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); | ||
122 | |||
123 | out: | ||
124 | tracked_request_end(&req); | ||
51 | -- | 125 | -- |
52 | 2.13.5 | 126 | 2.13.6 |
53 | 127 | ||
54 | 128 | diff view generated by jsdifflib |