1 | The following changes since commit 98bfaac788be0ca63d7d010c8d4ba100ff1d8278: | 1 | The following changes since commit ccdf06c1db192152ac70a1dd974c624f566cb7d4: |
---|---|---|---|
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 | Open 6.1 development tree (2021-04-30 11:15:40 +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 68bf7336533faa6aa90fdd4558edddbf5d8ef814: |
10 | 10 | ||
11 | qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing (2017-09-06 14:40:18 +0200) | 11 | vhost-user-blk: Fail gracefully on too large queue size (2021-04-30 12:27:48 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches | 14 | Block layer patches |
15 | 15 | ||
16 | - Fix permission update order problems with block graph changes | ||
17 | - qemu-img convert: Unshare write permission for source | ||
18 | - vhost-user-blk: Fail gracefully on too large queue size | ||
19 | |||
16 | ---------------------------------------------------------------- | 20 | ---------------------------------------------------------------- |
17 | Daniel P. Berrange (1): | 21 | Kevin Wolf (3): |
18 | block: document semantics of bdrv_co_preadv|pwritev | 22 | block: Add BDRV_O_NO_SHARE for blk_new_open() |
23 | qemu-img convert: Unshare write permission for source | ||
24 | vhost-user-blk: Fail gracefully on too large queue size | ||
19 | 25 | ||
20 | Eric Blake (2): | 26 | Vladimir Sementsov-Ogievskiy (36): |
21 | qcow: Change signature of get_cluster_offset() | 27 | tests/test-bdrv-graph-mod: add test_parallel_exclusive_write |
22 | qcow: Check failure of bdrv_getlength() and bdrv_truncate() | 28 | tests/test-bdrv-graph-mod: add test_parallel_perm_update |
29 | tests/test-bdrv-graph-mod: add test_append_greedy_filter | ||
30 | block: bdrv_append(): don't consume reference | ||
31 | block: BdrvChildClass: add .get_parent_aio_context handler | ||
32 | block: drop ctx argument from bdrv_root_attach_child | ||
33 | block: make bdrv_reopen_{prepare,commit,abort} private | ||
34 | util: add transactions.c | ||
35 | block: bdrv_refresh_perms: check for parents permissions conflict | ||
36 | block: refactor bdrv_child* permission functions | ||
37 | block: rewrite bdrv_child_try_set_perm() using bdrv_refresh_perms() | ||
38 | block: inline bdrv_child_*() permission functions calls | ||
39 | block: use topological sort for permission update | ||
40 | block: add bdrv_drv_set_perm transaction action | ||
41 | block: add bdrv_list_* permission update functions | ||
42 | block: add bdrv_replace_child_safe() transaction action | ||
43 | block: fix bdrv_replace_node_common | ||
44 | block: add bdrv_attach_child_common() transaction action | ||
45 | block: add bdrv_attach_child_noperm() transaction action | ||
46 | block: split out bdrv_replace_node_noperm() | ||
47 | block: adapt bdrv_append() for inserting filters | ||
48 | block: add bdrv_remove_filter_or_cow transaction action | ||
49 | block: introduce bdrv_drop_filter() | ||
50 | block/backup-top: drop .active | ||
51 | block: drop ignore_children for permission update functions | ||
52 | block: make bdrv_unset_inherits_from to be a transaction action | ||
53 | block: make bdrv_refresh_limits() to be a transaction action | ||
54 | block: add bdrv_set_backing_noperm() transaction action | ||
55 | block: bdrv_reopen_multiple(): move bdrv_flush to separate pre-prepare | ||
56 | block: bdrv_reopen_multiple: refresh permissions on updated graph | ||
57 | block: drop unused permission update functions | ||
58 | block: inline bdrv_check_perm_common() | ||
59 | block: inline bdrv_replace_child() | ||
60 | block: refactor bdrv_child_set_perm_safe() transaction action | ||
61 | block: rename bdrv_replace_child_safe() to bdrv_replace_child() | ||
62 | block: refactor bdrv_node_check_perm() | ||
23 | 63 | ||
24 | Manos Pitsidianakis (10): | 64 | include/block/block.h | 14 +- |
25 | block: pass bdrv_* methods to bs->file by default in block filters | 65 | include/block/block_int.h | 8 +- |
26 | block: remove unused bdrv_media_changed | 66 | include/qemu/transactions.h | 63 ++ |
27 | block: remove bdrv_truncate callback in blkdebug | 67 | block.c | 1329 ++++++++++++++++++++------------- |
28 | block: add default implementations for bdrv_co_get_block_status() | 68 | block/backup-top.c | 48 +- |
29 | block: move ThrottleGroup membership to ThrottleGroupMember | 69 | block/block-backend.c | 30 +- |
30 | block: add aio_context field in ThrottleGroupMember | 70 | block/commit.c | 1 + |
31 | block: tidy ThrottleGroupMember initializations | 71 | block/file-posix.c | 91 +-- |
32 | block: convert ThrottleGroup to object with QOM | 72 | block/io.c | 31 +- |
33 | block: add throttle block filter driver | 73 | block/mirror.c | 3 - |
34 | qemu-iotests: add 184 for throttle filter driver | 74 | blockdev.c | 4 - |
75 | blockjob.c | 11 +- | ||
76 | hw/block/vhost-user-blk.c | 5 + | ||
77 | qemu-img.c | 2 +- | ||
78 | tests/unit/test-bdrv-drain.c | 2 +- | ||
79 | tests/unit/test-bdrv-graph-mod.c | 209 +++++- | ||
80 | util/transactions.c | 96 +++ | ||
81 | MAINTAINERS | 6 + | ||
82 | tests/qemu-iotests/245 | 2 +- | ||
83 | tests/qemu-iotests/283.out | 2 +- | ||
84 | tests/qemu-iotests/tests/qsd-jobs.out | 2 +- | ||
85 | util/meson.build | 1 + | ||
86 | 22 files changed, 1280 insertions(+), 680 deletions(-) | ||
87 | create mode 100644 include/qemu/transactions.h | ||
88 | create mode 100644 util/transactions.c | ||
35 | 89 | ||
36 | Pavel Butsykin (1): | ||
37 | qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing | ||
38 | 90 | ||
39 | qapi/block-core.json | 66 +++- | ||
40 | include/block/block.h | 1 - | ||
41 | include/block/block_int.h | 56 ++- | ||
42 | include/block/throttle-groups.h | 52 ++- | ||
43 | include/qemu/throttle-options.h | 60 +++- | ||
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 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Add the test that shows that concept of ignore_children is incomplete. | ||
4 | Actually, when we want to update something, ignoring permission of some | ||
5 | existing BdrvChild, we should ignore also the propagated effect of this | ||
6 | child to the other children. But that's not done. Better approach | ||
7 | (update permissions on already updated graph) will be implemented | ||
8 | later. | ||
9 | |||
10 | Now the test fails, so it's added with -d argument to not break make | ||
11 | check. | ||
12 | |||
13 | Test fails with | ||
14 | |||
15 | "Conflicts with use by fl1 as 'backing', which does not allow 'write' on base" | ||
16 | |||
17 | because when updating permissions we can ignore original top->fl1 | ||
18 | BdrvChild. But we don't ignore exclusive write permission in fl1->base | ||
19 | BdrvChild, which is propagated. Correct thing to do is make graph | ||
20 | change first and then do permission update from the top node. | ||
21 | |||
22 | To run test do | ||
23 | |||
24 | ./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-exclusive-write | ||
25 | |||
26 | from <build-directory>/tests. | ||
27 | |||
28 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
29 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
30 | Message-Id: <20210428151804.439460-2-vsementsov@virtuozzo.com> | ||
31 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
32 | --- | ||
33 | tests/unit/test-bdrv-graph-mod.c | 70 +++++++++++++++++++++++++++++++- | ||
34 | 1 file changed, 69 insertions(+), 1 deletion(-) | ||
35 | |||
36 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | ||
38 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
39 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
40 | @@ -XXX,XX +XXX,XX @@ | ||
41 | /* | ||
42 | * Block node graph modifications tests | ||
43 | * | ||
44 | - * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved. | ||
45 | + * Copyright (c) 2019-2021 Virtuozzo International GmbH. All rights reserved. | ||
46 | * | ||
47 | * This program is free software; you can redistribute it and/or modify | ||
48 | * it under the terms of the GNU General Public License as published by | ||
49 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_no_perm = { | ||
50 | .bdrv_child_perm = no_perm_default_perms, | ||
51 | }; | ||
52 | |||
53 | +static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c, | ||
54 | + BdrvChildRole role, | ||
55 | + BlockReopenQueue *reopen_queue, | ||
56 | + uint64_t perm, uint64_t shared, | ||
57 | + uint64_t *nperm, uint64_t *nshared) | ||
58 | +{ | ||
59 | + *nperm = BLK_PERM_WRITE; | ||
60 | + *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE; | ||
61 | +} | ||
62 | + | ||
63 | +static BlockDriver bdrv_exclusive_writer = { | ||
64 | + .format_name = "exclusive-writer", | ||
65 | + .bdrv_child_perm = exclusive_write_perms, | ||
66 | +}; | ||
67 | + | ||
68 | static BlockDriverState *no_perm_node(const char *name) | ||
69 | { | ||
70 | return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort); | ||
71 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *pass_through_node(const char *name) | ||
72 | BDRV_O_RDWR, &error_abort); | ||
73 | } | ||
74 | |||
75 | +static BlockDriverState *exclusive_writer_node(const char *name) | ||
76 | +{ | ||
77 | + return bdrv_new_open_driver(&bdrv_exclusive_writer, name, | ||
78 | + BDRV_O_RDWR, &error_abort); | ||
79 | +} | ||
80 | + | ||
81 | /* | ||
82 | * test_update_perm_tree | ||
83 | * | ||
84 | @@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void) | ||
85 | blk_unref(root); | ||
86 | } | ||
87 | |||
88 | +/* | ||
89 | + * test_parallel_exclusive_write | ||
90 | + * | ||
91 | + * Check that when we replace node, old permissions of the node being removed | ||
92 | + * doesn't break the replacement. | ||
93 | + */ | ||
94 | +static void test_parallel_exclusive_write(void) | ||
95 | +{ | ||
96 | + BlockDriverState *top = exclusive_writer_node("top"); | ||
97 | + BlockDriverState *base = no_perm_node("base"); | ||
98 | + BlockDriverState *fl1 = pass_through_node("fl1"); | ||
99 | + BlockDriverState *fl2 = pass_through_node("fl2"); | ||
100 | + | ||
101 | + /* | ||
102 | + * bdrv_attach_child() eats child bs reference, so we need two @base | ||
103 | + * references for two filters: | ||
104 | + */ | ||
105 | + bdrv_ref(base); | ||
106 | + | ||
107 | + bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_DATA, | ||
108 | + &error_abort); | ||
109 | + bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, | ||
110 | + &error_abort); | ||
111 | + bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, | ||
112 | + &error_abort); | ||
113 | + | ||
114 | + bdrv_replace_node(fl1, fl2, &error_abort); | ||
115 | + | ||
116 | + bdrv_unref(fl2); | ||
117 | + bdrv_unref(top); | ||
118 | +} | ||
119 | + | ||
120 | int main(int argc, char *argv[]) | ||
121 | { | ||
122 | + int i; | ||
123 | + bool debug = false; | ||
124 | + | ||
125 | + for (i = 1; i < argc; i++) { | ||
126 | + if (!strcmp(argv[i], "-d")) { | ||
127 | + debug = true; | ||
128 | + break; | ||
129 | + } | ||
130 | + } | ||
131 | + | ||
132 | bdrv_init(); | ||
133 | qemu_init_main_loop(&error_abort); | ||
134 | |||
135 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) | ||
136 | g_test_add_func("/bdrv-graph-mod/should-update-child", | ||
137 | test_should_update_child); | ||
138 | |||
139 | + if (debug) { | ||
140 | + g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write", | ||
141 | + test_parallel_exclusive_write); | ||
142 | + } | ||
143 | + | ||
144 | return g_test_run(); | ||
145 | } | ||
146 | -- | ||
147 | 2.30.2 | ||
148 | |||
149 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Add test to show that simple DFS recursion order is not correct for | ||
4 | permission update. Correct order is topological-sort order, which will | ||
5 | be introduced later. | ||
6 | |||
7 | Consider the block driver which has two filter children: one active | ||
8 | with exclusive write access and one inactive with no specific | ||
9 | permissions. | ||
10 | |||
11 | And, these two children has a common base child, like this: | ||
12 | |||
13 | ┌─────┐ ┌──────┐ | ||
14 | │ fl2 │ ◀── │ top │ | ||
15 | └─────┘ └──────┘ | ||
16 | │ │ | ||
17 | │ │ w | ||
18 | │ ▼ | ||
19 | │ ┌──────┐ | ||
20 | │ │ fl1 │ | ||
21 | │ └──────┘ | ||
22 | │ │ | ||
23 | │ │ w | ||
24 | │ ▼ | ||
25 | │ ┌──────┐ | ||
26 | └───────▶ │ base │ | ||
27 | └──────┘ | ||
28 | |||
29 | So, exclusive write is propagated. | ||
30 | |||
31 | Assume, we want to make fl2 active instead of fl1. | ||
32 | So, we set some option for top driver and do permission update. | ||
33 | |||
34 | If permission update (remember, it's DFS) goes first through | ||
35 | top->fl1->base branch it will succeed: it firstly drop exclusive write | ||
36 | permissions and than apply them for another BdrvChildren. | ||
37 | But if permission update goes first through top->fl2->base branch it | ||
38 | will fail, as when we try to update fl2->base child, old not yet | ||
39 | updated fl1->base child will be in conflict. | ||
40 | |||
41 | Now test fails, so it runs only with -d flag. To run do | ||
42 | |||
43 | ./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-perm-update | ||
44 | |||
45 | from <build-directory>/tests. | ||
46 | |||
47 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
48 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
49 | Message-Id: <20210428151804.439460-3-vsementsov@virtuozzo.com> | ||
50 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
51 | --- | ||
52 | tests/unit/test-bdrv-graph-mod.c | 116 +++++++++++++++++++++++++++++++ | ||
53 | 1 file changed, 116 insertions(+) | ||
54 | |||
55 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
56 | index XXXXXXX..XXXXXXX 100644 | ||
57 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
58 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
59 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void) | ||
60 | bdrv_unref(top); | ||
61 | } | ||
62 | |||
63 | +static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c, | ||
64 | + BdrvChildRole role, | ||
65 | + BlockReopenQueue *reopen_queue, | ||
66 | + uint64_t perm, uint64_t shared, | ||
67 | + uint64_t *nperm, uint64_t *nshared) | ||
68 | +{ | ||
69 | + if (bs->file && c == bs->file) { | ||
70 | + *nperm = BLK_PERM_WRITE; | ||
71 | + *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE; | ||
72 | + } else { | ||
73 | + *nperm = 0; | ||
74 | + *nshared = BLK_PERM_ALL; | ||
75 | + } | ||
76 | +} | ||
77 | + | ||
78 | +static BlockDriver bdrv_write_to_file = { | ||
79 | + .format_name = "tricky-perm", | ||
80 | + .bdrv_child_perm = write_to_file_perms, | ||
81 | +}; | ||
82 | + | ||
83 | + | ||
84 | +/* | ||
85 | + * The following test shows that topological-sort order is required for | ||
86 | + * permission update, simple DFS is not enough. | ||
87 | + * | ||
88 | + * Consider the block driver which has two filter children: one active | ||
89 | + * with exclusive write access and one inactive with no specific | ||
90 | + * permissions. | ||
91 | + * | ||
92 | + * And, these two children has a common base child, like this: | ||
93 | + * | ||
94 | + * ┌─────┐ ┌──────┐ | ||
95 | + * │ fl2 │ ◀── │ top │ | ||
96 | + * └─────┘ └──────┘ | ||
97 | + * │ │ | ||
98 | + * │ │ w | ||
99 | + * │ ▼ | ||
100 | + * │ ┌──────┐ | ||
101 | + * │ │ fl1 │ | ||
102 | + * │ └──────┘ | ||
103 | + * │ │ | ||
104 | + * │ │ w | ||
105 | + * │ ▼ | ||
106 | + * │ ┌──────┐ | ||
107 | + * └───────▶ │ base │ | ||
108 | + * └──────┘ | ||
109 | + * | ||
110 | + * So, exclusive write is propagated. | ||
111 | + * | ||
112 | + * Assume, we want to make fl2 active instead of fl1. | ||
113 | + * So, we set some option for top driver and do permission update. | ||
114 | + * | ||
115 | + * With simple DFS, if permission update goes first through | ||
116 | + * top->fl1->base branch it will succeed: it firstly drop exclusive write | ||
117 | + * permissions and than apply them for another BdrvChildren. | ||
118 | + * But if permission update goes first through top->fl2->base branch it | ||
119 | + * will fail, as when we try to update fl2->base child, old not yet | ||
120 | + * updated fl1->base child will be in conflict. | ||
121 | + * | ||
122 | + * With topological-sort order we always update parents before children, so fl1 | ||
123 | + * and fl2 are both updated when we update base and there is no conflict. | ||
124 | + */ | ||
125 | +static void test_parallel_perm_update(void) | ||
126 | +{ | ||
127 | + BlockDriverState *top = no_perm_node("top"); | ||
128 | + BlockDriverState *tricky = | ||
129 | + bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR, | ||
130 | + &error_abort); | ||
131 | + BlockDriverState *base = no_perm_node("base"); | ||
132 | + BlockDriverState *fl1 = pass_through_node("fl1"); | ||
133 | + BlockDriverState *fl2 = pass_through_node("fl2"); | ||
134 | + BdrvChild *c_fl1, *c_fl2; | ||
135 | + | ||
136 | + /* | ||
137 | + * bdrv_attach_child() eats child bs reference, so we need two @base | ||
138 | + * references for two filters: | ||
139 | + */ | ||
140 | + bdrv_ref(base); | ||
141 | + | ||
142 | + bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA, | ||
143 | + &error_abort); | ||
144 | + c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds, | ||
145 | + BDRV_CHILD_FILTERED, &error_abort); | ||
146 | + c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds, | ||
147 | + BDRV_CHILD_FILTERED, &error_abort); | ||
148 | + bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, | ||
149 | + &error_abort); | ||
150 | + bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, | ||
151 | + &error_abort); | ||
152 | + | ||
153 | + /* Select fl1 as first child to be active */ | ||
154 | + tricky->file = c_fl1; | ||
155 | + bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); | ||
156 | + | ||
157 | + assert(c_fl1->perm & BLK_PERM_WRITE); | ||
158 | + assert(!(c_fl2->perm & BLK_PERM_WRITE)); | ||
159 | + | ||
160 | + /* Now, try to switch active child and update permissions */ | ||
161 | + tricky->file = c_fl2; | ||
162 | + bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); | ||
163 | + | ||
164 | + assert(c_fl2->perm & BLK_PERM_WRITE); | ||
165 | + assert(!(c_fl1->perm & BLK_PERM_WRITE)); | ||
166 | + | ||
167 | + /* Switch once more, to not care about real child order in the list */ | ||
168 | + tricky->file = c_fl1; | ||
169 | + bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); | ||
170 | + | ||
171 | + assert(c_fl1->perm & BLK_PERM_WRITE); | ||
172 | + assert(!(c_fl2->perm & BLK_PERM_WRITE)); | ||
173 | + | ||
174 | + bdrv_unref(top); | ||
175 | +} | ||
176 | + | ||
177 | int main(int argc, char *argv[]) | ||
178 | { | ||
179 | int i; | ||
180 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) | ||
181 | if (debug) { | ||
182 | g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write", | ||
183 | test_parallel_exclusive_write); | ||
184 | + g_test_add_func("/bdrv-graph-mod/parallel-perm-update", | ||
185 | + test_parallel_perm_update); | ||
186 | } | ||
187 | |||
188 | return g_test_run(); | ||
189 | -- | ||
190 | 2.30.2 | ||
191 | |||
192 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | bdrv_append() is not quite good for inserting filters: it does extra | ||
4 | permission update in intermediate state, where filter get it filtered | ||
5 | child but is not yet replace it in a backing chain. | ||
6 | |||
7 | Some filters (for example backup-top) may want permissions even when | ||
8 | have no parents. And described intermediate state becomes invalid. | ||
9 | |||
10 | That's (half a) reason, why we need "inactive" state for backup-top | ||
11 | filter. | ||
12 | |||
13 | bdrv_append() will be improved later, now let's add a unit test. | ||
14 | |||
15 | Now test fails, so it runs only with -d flag. To run do | ||
16 | |||
17 | ./test-bdrv-graph-mod -d -p /bdrv-graph-mod/append-greedy-filter | ||
18 | |||
19 | from <build-directory>/tests. | ||
20 | |||
21 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
22 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
23 | Message-Id: <20210428151804.439460-4-vsementsov@virtuozzo.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
25 | --- | ||
26 | tests/unit/test-bdrv-graph-mod.c | 33 ++++++++++++++++++++++++++++++++ | ||
27 | 1 file changed, 33 insertions(+) | ||
28 | |||
29 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
32 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
33 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) | ||
34 | bdrv_unref(top); | ||
35 | } | ||
36 | |||
37 | +/* | ||
38 | + * It's possible that filter required permissions allows to insert it to backing | ||
39 | + * chain, like: | ||
40 | + * | ||
41 | + * 1. [top] -> [filter] -> [base] | ||
42 | + * | ||
43 | + * but doesn't allow to add it as a branch: | ||
44 | + * | ||
45 | + * 2. [filter] --\ | ||
46 | + * v | ||
47 | + * [top] -> [base] | ||
48 | + * | ||
49 | + * So, inserting such filter should do all graph modifications and only then | ||
50 | + * update permissions. If we try to go through intermediate state [2] and update | ||
51 | + * permissions on it we'll fail. | ||
52 | + * | ||
53 | + * Let's check that bdrv_append() can append such a filter. | ||
54 | + */ | ||
55 | +static void test_append_greedy_filter(void) | ||
56 | +{ | ||
57 | + BlockDriverState *top = exclusive_writer_node("top"); | ||
58 | + BlockDriverState *base = no_perm_node("base"); | ||
59 | + BlockDriverState *fl = exclusive_writer_node("fl1"); | ||
60 | + | ||
61 | + bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_COW, | ||
62 | + &error_abort); | ||
63 | + | ||
64 | + bdrv_append(fl, base, &error_abort); | ||
65 | + bdrv_unref(top); | ||
66 | +} | ||
67 | + | ||
68 | int main(int argc, char *argv[]) | ||
69 | { | ||
70 | int i; | ||
71 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) | ||
72 | test_parallel_exclusive_write); | ||
73 | g_test_add_func("/bdrv-graph-mod/parallel-perm-update", | ||
74 | test_parallel_perm_update); | ||
75 | + g_test_add_func("/bdrv-graph-mod/append-greedy-filter", | ||
76 | + test_append_greedy_filter); | ||
77 | } | ||
78 | |||
79 | return g_test_run(); | ||
80 | -- | ||
81 | 2.30.2 | ||
82 | |||
83 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | bdrv_co_get_block_status_from_file() and | 3 | We have too much comments for this feature. It seems better just don't |
4 | bdrv_co_get_block_status_from_backing() set *file to bs->file and | 4 | do it. Most of real users (tests don't count) have to create additional |
5 | bs->backing respectively, so that bdrv_co_get_block_status() can recurse | 5 | reference. |
6 | to them. Future block drivers won't have to duplicate code to implement | ||
7 | this. | ||
8 | 6 | ||
9 | Reviewed-by: Fam Zheng <famz@redhat.com> | 7 | Drop also comment in external_snapshot_prepare: |
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | 8 | - bdrv_append doesn't "remove" old bs in common sense, it sounds |
11 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 9 | strange |
10 | - the fact that bdrv_append can fail is obvious from the context | ||
11 | - the fact that we must rollback all changes in transaction abort is | ||
12 | known (it's the direct role of abort) | ||
13 | |||
14 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
12 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 15 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
13 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 16 | Message-Id: <20210428151804.439460-5-vsementsov@virtuozzo.com> |
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | --- | 18 | --- |
16 | include/block/block_int.h | 18 ++++++++++++++++++ | 19 | block.c | 25 +++---------------------- |
17 | block/blkdebug.c | 12 +----------- | 20 | block/backup-top.c | 1 - |
18 | block/commit.c | 12 +----------- | 21 | block/commit.c | 1 + |
19 | block/io.c | 26 ++++++++++++++++++++++++++ | 22 | block/mirror.c | 3 --- |
20 | block/mirror.c | 12 +----------- | 23 | blockdev.c | 4 ---- |
21 | 5 files changed, 47 insertions(+), 33 deletions(-) | 24 | tests/unit/test-bdrv-drain.c | 2 +- |
25 | tests/unit/test-bdrv-graph-mod.c | 3 +++ | ||
26 | 7 files changed, 8 insertions(+), 31 deletions(-) | ||
22 | 27 | ||
23 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 28 | diff --git a/block.c b/block.c |
24 | index XXXXXXX..XXXXXXX 100644 | 29 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/include/block/block_int.h | 30 | --- a/block.c |
26 | +++ b/include/block/block_int.h | 31 | +++ b/block.c |
27 | @@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | 32 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, |
28 | uint64_t perm, uint64_t shared, | 33 | goto out; |
29 | uint64_t *nperm, uint64_t *nshared); | 34 | } |
30 | 35 | ||
31 | +/* | 36 | - /* bdrv_append() consumes a strong reference to bs_snapshot |
32 | + * Default implementation for drivers to pass bdrv_co_get_block_status() to | 37 | - * (i.e. it will call bdrv_unref() on it) even on error, so in |
33 | + * their file. | 38 | - * order to be able to return one, we have to increase |
34 | + */ | 39 | - * bs_snapshot's refcount here */ |
35 | +int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs, | 40 | - bdrv_ref(bs_snapshot); |
36 | + int64_t sector_num, | 41 | ret = bdrv_append(bs_snapshot, bs, errp); |
37 | + int nb_sectors, | 42 | if (ret < 0) { |
38 | + int *pnum, | 43 | bs_snapshot = NULL; |
39 | + BlockDriverState **file); | 44 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, |
40 | +/* | 45 | * bs_new must not be attached to a BlockBackend. |
41 | + * Default implementation for drivers to pass bdrv_co_get_block_status() to | 46 | * |
42 | + * their backing file. | 47 | * This function does not create any image files. |
43 | + */ | 48 | - * |
44 | +int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | 49 | - * bdrv_append() takes ownership of a bs_new reference and unrefs it because |
45 | + int64_t sector_num, | 50 | - * that's what the callers commonly need. bs_new will be referenced by the old |
46 | + int nb_sectors, | 51 | - * parents of bs_top after bdrv_append() returns. If the caller needs to keep a |
47 | + int *pnum, | 52 | - * reference of its own, it must call bdrv_ref(). |
48 | + BlockDriverState **file); | 53 | */ |
49 | const char *bdrv_get_parent_name(const BlockDriverState *bs); | 54 | int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, |
50 | void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); | 55 | Error **errp) |
51 | bool blk_dev_has_removable_media(BlockBackend *blk); | 56 | { |
52 | diff --git a/block/blkdebug.c b/block/blkdebug.c | 57 | int ret = bdrv_set_backing_hd(bs_new, bs_top, errp); |
58 | if (ret < 0) { | ||
59 | - goto out; | ||
60 | + return ret; | ||
61 | } | ||
62 | |||
63 | ret = bdrv_replace_node(bs_top, bs_new, errp); | ||
64 | if (ret < 0) { | ||
65 | bdrv_set_backing_hd(bs_new, NULL, &error_abort); | ||
66 | - goto out; | ||
67 | + return ret; | ||
68 | } | ||
69 | |||
70 | - ret = 0; | ||
71 | - | ||
72 | -out: | ||
73 | - /* | ||
74 | - * bs_new is now referenced by its new parents, we don't need the | ||
75 | - * additional reference any more. | ||
76 | - */ | ||
77 | - bdrv_unref(bs_new); | ||
78 | - | ||
79 | - return ret; | ||
80 | + return 0; | ||
81 | } | ||
82 | |||
83 | static void bdrv_delete(BlockDriverState *bs) | ||
84 | diff --git a/block/backup-top.c b/block/backup-top.c | ||
53 | index XXXXXXX..XXXXXXX 100644 | 85 | index XXXXXXX..XXXXXXX 100644 |
54 | --- a/block/blkdebug.c | 86 | --- a/block/backup-top.c |
55 | +++ b/block/blkdebug.c | 87 | +++ b/block/backup-top.c |
56 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | 88 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, |
57 | return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | 89 | |
58 | } | 90 | bdrv_drained_begin(source); |
59 | 91 | ||
60 | -static int64_t coroutine_fn blkdebug_co_get_block_status( | 92 | - bdrv_ref(top); |
61 | - BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | 93 | ret = bdrv_append(top, source, errp); |
62 | - BlockDriverState **file) | 94 | if (ret < 0) { |
63 | -{ | 95 | error_prepend(errp, "Cannot append backup-top filter: "); |
64 | - *pnum = nb_sectors; | ||
65 | - *file = bs->file->bs; | ||
66 | - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||
67 | - (sector_num << BDRV_SECTOR_BITS); | ||
68 | -} | ||
69 | - | ||
70 | static void blkdebug_close(BlockDriverState *bs) | ||
71 | { | ||
72 | BDRVBlkdebugState *s = bs->opaque; | ||
73 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | ||
74 | .bdrv_co_flush_to_disk = blkdebug_co_flush, | ||
75 | .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes, | ||
76 | .bdrv_co_pdiscard = blkdebug_co_pdiscard, | ||
77 | - .bdrv_co_get_block_status = blkdebug_co_get_block_status, | ||
78 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file, | ||
79 | |||
80 | .bdrv_debug_event = blkdebug_debug_event, | ||
81 | .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, | ||
82 | diff --git a/block/commit.c b/block/commit.c | 96 | diff --git a/block/commit.c b/block/commit.c |
83 | index XXXXXXX..XXXXXXX 100644 | 97 | index XXXXXXX..XXXXXXX 100644 |
84 | --- a/block/commit.c | 98 | --- a/block/commit.c |
85 | +++ b/block/commit.c | 99 | +++ b/block/commit.c |
86 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | 100 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, |
87 | return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); | 101 | commit_top_bs->total_sectors = top->total_sectors; |
88 | } | 102 | |
89 | 103 | ret = bdrv_append(commit_top_bs, top, errp); | |
90 | -static int64_t coroutine_fn bdrv_commit_top_get_block_status( | 104 | + bdrv_unref(commit_top_bs); /* referenced by new parents or failed */ |
91 | - BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | 105 | if (ret < 0) { |
92 | - BlockDriverState **file) | 106 | commit_top_bs = NULL; |
93 | -{ | 107 | goto fail; |
94 | - *pnum = nb_sectors; | ||
95 | - *file = bs->backing->bs; | ||
96 | - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||
97 | - (sector_num << BDRV_SECTOR_BITS); | ||
98 | -} | ||
99 | - | ||
100 | static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) | ||
101 | { | ||
102 | bdrv_refresh_filename(bs->backing->bs); | ||
103 | @@ -XXX,XX +XXX,XX @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
104 | static BlockDriver bdrv_commit_top = { | ||
105 | .format_name = "commit_top", | ||
106 | .bdrv_co_preadv = bdrv_commit_top_preadv, | ||
107 | - .bdrv_co_get_block_status = bdrv_commit_top_get_block_status, | ||
108 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing, | ||
109 | .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, | ||
110 | .bdrv_close = bdrv_commit_top_close, | ||
111 | .bdrv_child_perm = bdrv_commit_top_child_perm, | ||
112 | diff --git a/block/io.c b/block/io.c | ||
113 | index XXXXXXX..XXXXXXX 100644 | ||
114 | --- a/block/io.c | ||
115 | +++ b/block/io.c | ||
116 | @@ -XXX,XX +XXX,XX @@ typedef struct BdrvCoGetBlockStatusData { | ||
117 | bool done; | ||
118 | } BdrvCoGetBlockStatusData; | ||
119 | |||
120 | +int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs, | ||
121 | + int64_t sector_num, | ||
122 | + int nb_sectors, | ||
123 | + int *pnum, | ||
124 | + BlockDriverState **file) | ||
125 | +{ | ||
126 | + assert(bs->file && bs->file->bs); | ||
127 | + *pnum = nb_sectors; | ||
128 | + *file = bs->file->bs; | ||
129 | + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||
130 | + (sector_num << BDRV_SECTOR_BITS); | ||
131 | +} | ||
132 | + | ||
133 | +int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs, | ||
134 | + int64_t sector_num, | ||
135 | + int nb_sectors, | ||
136 | + int *pnum, | ||
137 | + BlockDriverState **file) | ||
138 | +{ | ||
139 | + assert(bs->backing && bs->backing->bs); | ||
140 | + *pnum = nb_sectors; | ||
141 | + *file = bs->backing->bs; | ||
142 | + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | ||
143 | + (sector_num << BDRV_SECTOR_BITS); | ||
144 | +} | ||
145 | + | ||
146 | /* | ||
147 | * Returns the allocation status of the specified sectors. | ||
148 | * Drivers not implementing the functionality are assumed to not support | ||
149 | diff --git a/block/mirror.c b/block/mirror.c | 108 | diff --git a/block/mirror.c b/block/mirror.c |
150 | index XXXXXXX..XXXXXXX 100644 | 109 | index XXXXXXX..XXXXXXX 100644 |
151 | --- a/block/mirror.c | 110 | --- a/block/mirror.c |
152 | +++ b/block/mirror.c | 111 | +++ b/block/mirror.c |
153 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) | 112 | @@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job( |
154 | return bdrv_co_flush(bs->backing->bs); | 113 | |
114 | bs_opaque->is_commit = target_is_backing; | ||
115 | |||
116 | - /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep | ||
117 | - * it alive until block_job_create() succeeds even if bs has no parent. */ | ||
118 | - bdrv_ref(mirror_top_bs); | ||
119 | bdrv_drained_begin(bs); | ||
120 | ret = bdrv_append(mirror_top_bs, bs, errp); | ||
121 | bdrv_drained_end(bs); | ||
122 | diff --git a/blockdev.c b/blockdev.c | ||
123 | index XXXXXXX..XXXXXXX 100644 | ||
124 | --- a/blockdev.c | ||
125 | +++ b/blockdev.c | ||
126 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common, | ||
127 | goto out; | ||
128 | } | ||
129 | |||
130 | - /* This removes our old bs and adds the new bs. This is an operation that | ||
131 | - * can fail, so we need to do it in .prepare; undoing it for abort is | ||
132 | - * always possible. */ | ||
133 | - bdrv_ref(state->new_bs); | ||
134 | ret = bdrv_append(state->new_bs, state->old_bs, errp); | ||
135 | if (ret < 0) { | ||
136 | goto out; | ||
137 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c | ||
138 | index XXXXXXX..XXXXXXX 100644 | ||
139 | --- a/tests/unit/test-bdrv-drain.c | ||
140 | +++ b/tests/unit/test-bdrv-drain.c | ||
141 | @@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void) | ||
142 | g_assert_cmpint(base_s->drain_count, ==, 1); | ||
143 | g_assert_cmpint(base->in_flight, ==, 0); | ||
144 | |||
145 | - /* Takes ownership of overlay, so we don't have to unref it later */ | ||
146 | bdrv_append(overlay, base, &error_abort); | ||
147 | g_assert_cmpint(base->in_flight, ==, 0); | ||
148 | g_assert_cmpint(overlay->in_flight, ==, 0); | ||
149 | @@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void) | ||
150 | g_assert_cmpint(overlay->quiesce_counter, ==, 0); | ||
151 | g_assert_cmpint(overlay_s->drain_count, ==, 0); | ||
152 | |||
153 | + bdrv_unref(overlay); | ||
154 | bdrv_unref(base); | ||
155 | blk_unref(blk); | ||
155 | } | 156 | } |
156 | 157 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | |
157 | -static int64_t coroutine_fn bdrv_mirror_top_get_block_status( | 158 | index XXXXXXX..XXXXXXX 100644 |
158 | - BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | 159 | --- a/tests/unit/test-bdrv-graph-mod.c |
159 | - BlockDriverState **file) | 160 | +++ b/tests/unit/test-bdrv-graph-mod.c |
160 | -{ | 161 | @@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void) |
161 | - *pnum = nb_sectors; | 162 | ret = bdrv_append(filter, bs, NULL); |
162 | - *file = bs->backing->bs; | 163 | g_assert_cmpint(ret, <, 0); |
163 | - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | | 164 | |
164 | - (sector_num << BDRV_SECTOR_BITS); | 165 | + bdrv_unref(filter); |
165 | -} | 166 | blk_unref(root); |
166 | - | 167 | } |
167 | static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, | 168 | |
168 | int64_t offset, int bytes, BdrvRequestFlags flags) | 169 | @@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void) |
169 | { | 170 | bdrv_append(filter, bs, &error_abort); |
170 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_mirror_top = { | 171 | g_assert(target->backing->bs == bs); |
171 | .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, | 172 | |
172 | .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, | 173 | + bdrv_unref(filter); |
173 | .bdrv_co_flush = bdrv_mirror_top_flush, | 174 | bdrv_unref(bs); |
174 | - .bdrv_co_get_block_status = bdrv_mirror_top_get_block_status, | 175 | blk_unref(root); |
175 | + .bdrv_co_get_block_status = bdrv_co_get_block_status_from_backing, | 176 | } |
176 | .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, | 177 | @@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void) |
177 | .bdrv_close = bdrv_mirror_top_close, | 178 | &error_abort); |
178 | .bdrv_child_perm = bdrv_mirror_top_child_perm, | 179 | |
180 | bdrv_append(fl, base, &error_abort); | ||
181 | + bdrv_unref(fl); | ||
182 | bdrv_unref(top); | ||
183 | } | ||
184 | |||
179 | -- | 185 | -- |
180 | 2.13.5 | 186 | 2.30.2 |
181 | 187 | ||
182 | 188 | 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 function is not used anywhere, so remove it. | 3 | Add new handler to get aio context and implement it in all child |
4 | classes. Add corresponding public interface to be used soon. | ||
4 | 5 | ||
5 | Markus Armbruster adds: | 6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
6 | The i82078 floppy device model used to call bdrv_media_changed() to | 7 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
7 | implement its media change bit when backed by a host floppy. This | 8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
8 | went away in 21fcf36 "fdc: simplify media change handling". | 9 | Message-Id: <20210428151804.439460-6-vsementsov@virtuozzo.com> |
9 | Probably broke host floppy media change. Host floppy pass-through | ||
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 | |||
14 | Reviewed-by: Eric Blake <eblake@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> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | --- | 11 | --- |
19 | include/block/block.h | 1 - | 12 | include/block/block.h | 2 ++ |
20 | include/block/block_int.h | 1 - | 13 | include/block/block_int.h | 2 ++ |
21 | block.c | 14 -------------- | 14 | block.c | 13 +++++++++++++ |
22 | block/raw-format.c | 6 ------ | 15 | block/block-backend.c | 9 +++++++++ |
23 | 4 files changed, 22 deletions(-) | 16 | blockjob.c | 8 ++++++++ |
17 | 5 files changed, 34 insertions(+) | ||
24 | 18 | ||
25 | diff --git a/include/block/block.h b/include/block/block.h | 19 | diff --git a/include/block/block.h b/include/block/block.h |
26 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/include/block/block.h | 21 | --- a/include/block/block.h |
28 | +++ b/include/block/block.h | 22 | +++ b/include/block/block.h |
29 | @@ -XXX,XX +XXX,XX @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, | 23 | @@ -XXX,XX +XXX,XX @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, |
30 | int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); | 24 | GSList **ignore, Error **errp); |
31 | bool bdrv_is_sg(BlockDriverState *bs); | 25 | bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, |
32 | bool bdrv_is_inserted(BlockDriverState *bs); | 26 | GSList **ignore, Error **errp); |
33 | -int bdrv_media_changed(BlockDriverState *bs); | 27 | +AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); |
34 | void bdrv_lock_medium(BlockDriverState *bs, bool locked); | 28 | + |
35 | void bdrv_eject(BlockDriverState *bs, bool eject_flag); | 29 | int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); |
36 | const char *bdrv_get_format_name(BlockDriverState *bs); | 30 | int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); |
31 | |||
37 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 32 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
38 | index XXXXXXX..XXXXXXX 100644 | 33 | index XXXXXXX..XXXXXXX 100644 |
39 | --- a/include/block/block_int.h | 34 | --- a/include/block/block_int.h |
40 | +++ b/include/block/block_int.h | 35 | +++ b/include/block/block_int.h |
41 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 36 | @@ -XXX,XX +XXX,XX @@ struct BdrvChildClass { |
42 | 37 | bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx, | |
43 | /* removable device specific */ | 38 | GSList **ignore, Error **errp); |
44 | bool (*bdrv_is_inserted)(BlockDriverState *bs); | 39 | void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); |
45 | - int (*bdrv_media_changed)(BlockDriverState *bs); | 40 | + |
46 | void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); | 41 | + AioContext *(*get_parent_aio_context)(BdrvChild *child); |
47 | void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); | 42 | }; |
48 | 43 | ||
44 | extern const BdrvChildClass child_of_bds; | ||
49 | diff --git a/block.c b/block.c | 45 | diff --git a/block.c b/block.c |
50 | index XXXXXXX..XXXXXXX 100644 | 46 | index XXXXXXX..XXXXXXX 100644 |
51 | --- a/block.c | 47 | --- a/block.c |
52 | +++ b/block.c | 48 | +++ b/block.c |
53 | @@ -XXX,XX +XXX,XX @@ bool bdrv_is_inserted(BlockDriverState *bs) | 49 | @@ -XXX,XX +XXX,XX @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, |
50 | return 0; | ||
54 | } | 51 | } |
55 | 52 | ||
56 | /** | 53 | +static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c) |
57 | - * Return whether the media changed since the last call to this | 54 | +{ |
58 | - * function, or -ENOTSUP if we don't know. Most drivers don't know. | 55 | + BlockDriverState *bs = c->opaque; |
59 | - */ | 56 | + |
60 | -int bdrv_media_changed(BlockDriverState *bs) | 57 | + return bdrv_get_aio_context(bs); |
61 | -{ | 58 | +} |
62 | - BlockDriver *drv = bs->drv; | 59 | + |
63 | - | 60 | const BdrvChildClass child_of_bds = { |
64 | - if (drv && drv->bdrv_media_changed) { | 61 | .parent_is_bds = true, |
65 | - return drv->bdrv_media_changed(bs); | 62 | .get_parent_desc = bdrv_child_get_parent_desc, |
66 | - } | 63 | @@ -XXX,XX +XXX,XX @@ const BdrvChildClass child_of_bds = { |
67 | - return -ENOTSUP; | 64 | .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx, |
68 | -} | 65 | .set_aio_ctx = bdrv_child_cb_set_aio_ctx, |
69 | - | 66 | .update_filename = bdrv_child_cb_update_filename, |
70 | -/** | 67 | + .get_parent_aio_context = bdrv_child_cb_get_parent_aio_context, |
71 | * If eject_flag is TRUE, eject the media. Otherwise, close the tray | 68 | }; |
72 | */ | 69 | |
73 | void bdrv_eject(BlockDriverState *bs, bool eject_flag) | 70 | +AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c) |
74 | diff --git a/block/raw-format.c b/block/raw-format.c | 71 | +{ |
72 | + return c->klass->get_parent_aio_context(c); | ||
73 | +} | ||
74 | + | ||
75 | static int bdrv_open_flags(BlockDriverState *bs, int flags) | ||
76 | { | ||
77 | int open_flags = flags; | ||
78 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
75 | index XXXXXXX..XXXXXXX 100644 | 79 | index XXXXXXX..XXXXXXX 100644 |
76 | --- a/block/raw-format.c | 80 | --- a/block/block-backend.c |
77 | +++ b/block/raw-format.c | 81 | +++ b/block/block-backend.c |
78 | @@ -XXX,XX +XXX,XX @@ static int raw_truncate(BlockDriverState *bs, int64_t offset, | 82 | @@ -XXX,XX +XXX,XX @@ static void blk_root_detach(BdrvChild *child) |
79 | return bdrv_truncate(bs->file, offset, prealloc, errp); | 83 | } |
80 | } | 84 | } |
81 | 85 | ||
82 | -static int raw_media_changed(BlockDriverState *bs) | 86 | +static AioContext *blk_root_get_parent_aio_context(BdrvChild *c) |
83 | -{ | 87 | +{ |
84 | - return bdrv_media_changed(bs->file->bs); | 88 | + BlockBackend *blk = c->opaque; |
85 | -} | 89 | + |
86 | - | 90 | + return blk_get_aio_context(blk); |
87 | static void raw_eject(BlockDriverState *bs, bool eject_flag) | 91 | +} |
88 | { | 92 | + |
89 | bdrv_eject(bs->file->bs, eject_flag); | 93 | static const BdrvChildClass child_root = { |
90 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = { | 94 | .inherit_options = blk_root_inherit_options, |
91 | .bdrv_refresh_limits = &raw_refresh_limits, | 95 | |
92 | .bdrv_probe_blocksizes = &raw_probe_blocksizes, | 96 | @@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_root = { |
93 | .bdrv_probe_geometry = &raw_probe_geometry, | 97 | |
94 | - .bdrv_media_changed = &raw_media_changed, | 98 | .can_set_aio_ctx = blk_root_can_set_aio_ctx, |
95 | .bdrv_eject = &raw_eject, | 99 | .set_aio_ctx = blk_root_set_aio_ctx, |
96 | .bdrv_lock_medium = &raw_lock_medium, | 100 | + |
97 | .bdrv_co_ioctl = &raw_co_ioctl, | 101 | + .get_parent_aio_context = blk_root_get_parent_aio_context, |
102 | }; | ||
103 | |||
104 | /* | ||
105 | diff --git a/blockjob.c b/blockjob.c | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/blockjob.c | ||
108 | +++ b/blockjob.c | ||
109 | @@ -XXX,XX +XXX,XX @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx, | ||
110 | job->job.aio_context = ctx; | ||
111 | } | ||
112 | |||
113 | +static AioContext *child_job_get_parent_aio_context(BdrvChild *c) | ||
114 | +{ | ||
115 | + BlockJob *job = c->opaque; | ||
116 | + | ||
117 | + return job->job.aio_context; | ||
118 | +} | ||
119 | + | ||
120 | static const BdrvChildClass child_job = { | ||
121 | .get_parent_desc = child_job_get_parent_desc, | ||
122 | .drained_begin = child_job_drained_begin, | ||
123 | @@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_job = { | ||
124 | .can_set_aio_ctx = child_job_can_set_aio_ctx, | ||
125 | .set_aio_ctx = child_job_set_aio_ctx, | ||
126 | .stay_at_node = true, | ||
127 | + .get_parent_aio_context = child_job_get_parent_aio_context, | ||
128 | }; | ||
129 | |||
130 | void block_job_remove_all_bdrv(BlockJob *job) | ||
98 | -- | 131 | -- |
99 | 2.13.5 | 132 | 2.30.2 |
100 | 133 | ||
101 | 134 | diff view generated by jsdifflib |
1 | From: "Daniel P. Berrange" <berrange@redhat.com> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 3 | Passing parent aio context is redundant, as child_class and parent |
4 | Reviewed-by: Eric Blake <eblake@redhat.com> | 4 | opaque pointer are enough to retrieve it. Drop the argument and use new |
5 | Signed-off-by: Daniel P. Berrange <berrange@redhat.com> | 5 | bdrv_child_get_parent_aio_context() interface. |
6 | |||
7 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Message-Id: <20210428151804.439460-7-vsementsov@virtuozzo.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | --- | 12 | --- |
8 | include/block/block_int.h | 31 +++++++++++++++++++++++++++++++ | 13 | include/block/block_int.h | 1 - |
9 | 1 file changed, 31 insertions(+) | 14 | block.c | 8 +++++--- |
15 | block/block-backend.c | 4 ++-- | ||
16 | blockjob.c | 3 +-- | ||
17 | 4 files changed, 8 insertions(+), 8 deletions(-) | ||
10 | 18 | ||
11 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 19 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
12 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/include/block/block_int.h | 21 | --- a/include/block/block_int.h |
14 | +++ b/include/block/block_int.h | 22 | +++ b/include/block/block_int.h |
15 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 23 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, |
16 | 24 | const char *child_name, | |
17 | int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs, | 25 | const BdrvChildClass *child_class, |
18 | int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); | 26 | BdrvChildRole child_role, |
27 | - AioContext *ctx, | ||
28 | uint64_t perm, uint64_t shared_perm, | ||
29 | void *opaque, Error **errp); | ||
30 | void bdrv_root_unref_child(BdrvChild *child); | ||
31 | diff --git a/block.c b/block.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block.c | ||
34 | +++ b/block.c | ||
35 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
36 | const char *child_name, | ||
37 | const BdrvChildClass *child_class, | ||
38 | BdrvChildRole child_role, | ||
39 | - AioContext *ctx, | ||
40 | uint64_t perm, uint64_t shared_perm, | ||
41 | void *opaque, Error **errp) | ||
42 | { | ||
43 | BdrvChild *child; | ||
44 | Error *local_err = NULL; | ||
45 | int ret; | ||
46 | + AioContext *ctx; | ||
47 | |||
48 | ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp); | ||
49 | if (ret < 0) { | ||
50 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
51 | .opaque = opaque, | ||
52 | }; | ||
53 | |||
54 | + ctx = bdrv_child_get_parent_aio_context(child); | ||
19 | + | 55 | + |
20 | + /** | 56 | /* If the AioContexts don't match, first try to move the subtree of |
21 | + * @offset: position in bytes to read at | 57 | * child_bs into the AioContext of the new parent. If this doesn't work, |
22 | + * @bytes: number of bytes to read | 58 | * try moving the parent into the AioContext of child_bs instead. */ |
23 | + * @qiov: the buffers to fill with read data | 59 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, |
24 | + * @flags: currently unused, always 0 | 60 | perm, shared_perm, &perm, &shared_perm); |
25 | + * | 61 | |
26 | + * @offset and @bytes will be a multiple of 'request_alignment', | 62 | child = bdrv_root_attach_child(child_bs, child_name, child_class, |
27 | + * but the length of individual @qiov elements does not have to | 63 | - child_role, bdrv_get_aio_context(parent_bs), |
28 | + * be a multiple. | 64 | - perm, shared_perm, parent_bs, errp); |
29 | + * | 65 | + child_role, perm, shared_perm, parent_bs, |
30 | + * @bytes will always equal the total size of @qiov, and will be | 66 | + errp); |
31 | + * no larger than 'max_transfer'. | 67 | if (child == NULL) { |
32 | + * | 68 | return NULL; |
33 | + * The buffer in @qiov may point directly to guest memory. | 69 | } |
34 | + */ | 70 | diff --git a/block/block-backend.c b/block/block-backend.c |
35 | int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, | 71 | index XXXXXXX..XXXXXXX 100644 |
36 | uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); | 72 | --- a/block/block-backend.c |
37 | int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, | 73 | +++ b/block/block-backend.c |
38 | int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); | 74 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, |
39 | int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs, | 75 | |
40 | int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); | 76 | blk->root = bdrv_root_attach_child(bs, "root", &child_root, |
41 | + /** | 77 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, |
42 | + * @offset: position in bytes to write at | 78 | - blk->ctx, perm, BLK_PERM_ALL, blk, errp); |
43 | + * @bytes: number of bytes to write | 79 | + perm, BLK_PERM_ALL, blk, errp); |
44 | + * @qiov: the buffers containing data to write | 80 | if (!blk->root) { |
45 | + * @flags: zero or more bits allowed by 'supported_write_flags' | 81 | blk_unref(blk); |
46 | + * | 82 | return NULL; |
47 | + * @offset and @bytes will be a multiple of 'request_alignment', | 83 | @@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) |
48 | + * but the length of individual @qiov elements does not have to | 84 | bdrv_ref(bs); |
49 | + * be a multiple. | 85 | blk->root = bdrv_root_attach_child(bs, "root", &child_root, |
50 | + * | 86 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, |
51 | + * @bytes will always equal the total size of @qiov, and will be | 87 | - blk->ctx, blk->perm, blk->shared_perm, |
52 | + * no larger than 'max_transfer'. | 88 | + blk->perm, blk->shared_perm, |
53 | + * | 89 | blk, errp); |
54 | + * The buffer in @qiov may point directly to guest memory. | 90 | if (blk->root == NULL) { |
55 | + */ | 91 | return -EPERM; |
56 | int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs, | 92 | diff --git a/blockjob.c b/blockjob.c |
57 | uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); | 93 | index XXXXXXX..XXXXXXX 100644 |
58 | 94 | --- a/blockjob.c | |
95 | +++ b/blockjob.c | ||
96 | @@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, | ||
97 | if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) { | ||
98 | aio_context_release(job->job.aio_context); | ||
99 | } | ||
100 | - c = bdrv_root_attach_child(bs, name, &child_job, 0, | ||
101 | - job->job.aio_context, perm, shared_perm, job, | ||
102 | + c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, | ||
103 | errp); | ||
104 | if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) { | ||
105 | aio_context_acquire(job->job.aio_context); | ||
59 | -- | 106 | -- |
60 | 2.13.5 | 107 | 2.30.2 |
61 | 108 | ||
62 | 109 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | These functions are called only from bdrv_reopen_multiple() in block.c. | ||
4 | No reason to publish them. | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Message-Id: <20210428151804.439460-8-vsementsov@virtuozzo.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | include/block/block.h | 4 ---- | ||
13 | block.c | 13 +++++++++---- | ||
14 | 2 files changed, 9 insertions(+), 8 deletions(-) | ||
15 | |||
16 | diff --git a/include/block/block.h b/include/block/block.h | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/include/block/block.h | ||
19 | +++ b/include/block/block.h | ||
20 | @@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, | ||
21 | int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); | ||
22 | int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, | ||
23 | Error **errp); | ||
24 | -int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
25 | - BlockReopenQueue *queue, Error **errp); | ||
26 | -void bdrv_reopen_commit(BDRVReopenState *reopen_state); | ||
27 | -void bdrv_reopen_abort(BDRVReopenState *reopen_state); | ||
28 | int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, | ||
29 | int64_t bytes, BdrvRequestFlags flags); | ||
30 | int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); | ||
31 | diff --git a/block.c b/block.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block.c | ||
34 | +++ b/block.c | ||
35 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||
36 | BdrvChildRole child_role, | ||
37 | Error **errp); | ||
38 | |||
39 | +static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue | ||
40 | + *queue, Error **errp); | ||
41 | +static void bdrv_reopen_commit(BDRVReopenState *reopen_state); | ||
42 | +static void bdrv_reopen_abort(BDRVReopenState *reopen_state); | ||
43 | + | ||
44 | /* If non-zero, use only whitelisted block drivers */ | ||
45 | static int use_bdrv_whitelist; | ||
46 | |||
47 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, | ||
48 | * commit() for any other BDS that have been left in a prepare() state | ||
49 | * | ||
50 | */ | ||
51 | -int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, | ||
52 | - Error **errp) | ||
53 | +static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
54 | + BlockReopenQueue *queue, Error **errp) | ||
55 | { | ||
56 | int ret = -1; | ||
57 | int old_flags; | ||
58 | @@ -XXX,XX +XXX,XX @@ error: | ||
59 | * makes them final by swapping the staging BlockDriverState contents into | ||
60 | * the active BlockDriverState contents. | ||
61 | */ | ||
62 | -void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||
63 | +static void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||
64 | { | ||
65 | BlockDriver *drv; | ||
66 | BlockDriverState *bs; | ||
67 | @@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||
68 | * Abort the reopen, and delete and free the staged changes in | ||
69 | * reopen_state | ||
70 | */ | ||
71 | -void bdrv_reopen_abort(BDRVReopenState *reopen_state) | ||
72 | +static void bdrv_reopen_abort(BDRVReopenState *reopen_state) | ||
73 | { | ||
74 | BlockDriver *drv; | ||
75 | |||
76 | -- | ||
77 | 2.30.2 | ||
78 | |||
79 | 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 | Add simple transaction API to use in further update of block graph |
4 | block filter driver. I/O operations are intercepted in the filter's | 4 | operations. |
5 | read/write coroutines, and referred to block/throttle-groups.c | 5 | |
6 | 6 | Supposed usage is: | |
7 | The driver can be used with the syntax | 7 | |
8 | -drive driver=throttle,file.filename=foo.qcow2,throttle-group=bar | 8 | - "prepare" is main function of the action and it should make the main |
9 | 9 | effect of the action to be visible for the following actions, keeping | |
10 | which registers the throttle filter node with the ThrottleGroup 'bar'. The | 10 | possibility of roll-back, saving necessary things in action state, |
11 | given group must be created beforehand with object-add or -object. | 11 | which is prepended to the action list (to do that, prepare func |
12 | 12 | should call tran_add()). So, driver struct doesn't include "prepare" | |
13 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 13 | field, as it is supposed to be called directly. |
14 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 14 | |
15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 15 | - commit/rollback is supposed to be called for the list of action |
16 | states, to commit/rollback all the actions in reverse order | ||
17 | |||
18 | - When possible "commit" should not make visible effect for other | ||
19 | actions, which make possible transparent logical interaction between | ||
20 | actions. | ||
21 | |||
22 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
23 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
24 | Message-Id: <20210428151804.439460-9-vsementsov@virtuozzo.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 25 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 26 | --- |
18 | qapi/block-core.json | 18 ++- | 27 | include/qemu/transactions.h | 63 ++++++++++++++++++++++++ |
19 | include/block/throttle-groups.h | 5 + | 28 | util/transactions.c | 96 +++++++++++++++++++++++++++++++++++++ |
20 | include/qemu/throttle-options.h | 1 + | 29 | MAINTAINERS | 6 +++ |
21 | block/throttle-groups.c | 15 ++- | 30 | util/meson.build | 1 + |
22 | block/throttle.c | 237 ++++++++++++++++++++++++++++++++++++++++ | 31 | 4 files changed, 166 insertions(+) |
23 | block/Makefile.objs | 1 + | 32 | create mode 100644 include/qemu/transactions.h |
24 | 6 files changed, 275 insertions(+), 2 deletions(-) | 33 | create mode 100644 util/transactions.c |
25 | create mode 100644 block/throttle.c | 34 | |
26 | 35 | diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h | |
27 | diff --git a/qapi/block-core.json b/qapi/block-core.json | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/qapi/block-core.json | ||
30 | +++ b/qapi/block-core.json | ||
31 | @@ -XXX,XX +XXX,XX @@ | ||
32 | # Drivers that are supported in block device operations. | ||
33 | # | ||
34 | # @vxhs: Since 2.10 | ||
35 | +# @throttle: Since 2.11 | ||
36 | # | ||
37 | # Since: 2.9 | ||
38 | ## | ||
39 | @@ -XXX,XX +XXX,XX @@ | ||
40 | 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', | ||
41 | 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', | ||
42 | 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', | ||
43 | - 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } | ||
44 | + 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } | ||
45 | |||
46 | ## | ||
47 | # @BlockdevOptionsFile: | ||
48 | @@ -XXX,XX +XXX,XX @@ | ||
49 | '*tls-creds': 'str' } } | ||
50 | |||
51 | ## | ||
52 | +# @BlockdevOptionsThrottle: | ||
53 | +# | ||
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 | ||
79 | --- a/include/block/throttle-groups.h | ||
80 | +++ b/include/block/throttle-groups.h | ||
81 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm | ||
82 | void throttle_group_attach_aio_context(ThrottleGroupMember *tgm, | ||
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 | } | ||
111 | |||
112 | +/* This function reads throttle_groups and must be called under the global | ||
113 | + * mutex. | ||
114 | + */ | ||
115 | +bool throttle_group_exists(const char *name) | ||
116 | +{ | ||
117 | + return throttle_group_by_name(name) != NULL; | ||
118 | +} | ||
119 | + | ||
120 | /* Increments the reference count of a ThrottleGroup given its name. | ||
121 | * | ||
122 | * If no ThrottleGroup is found with the given name a new one is | ||
123 | @@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) | ||
124 | ThrottleGroupMember *token; | ||
125 | int i; | ||
126 | |||
127 | + if (!ts) { | ||
128 | + /* Discard already unregistered tgm */ | ||
129 | + return; | ||
130 | + } | ||
131 | + | ||
132 | assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0); | ||
133 | assert(qemu_co_queue_empty(&tgm->throttled_reqs[0])); | ||
134 | assert(qemu_co_queue_empty(&tgm->throttled_reqs[1])); | ||
135 | @@ -XXX,XX +XXX,XX @@ static void throttle_group_obj_complete(UserCreatable *obj, Error **errp) | ||
136 | assert(tg->name); | ||
137 | |||
138 | /* error if name is duplicate */ | ||
139 | - if (throttle_group_by_name(tg->name) != NULL) { | ||
140 | + if (throttle_group_exists(tg->name)) { | ||
141 | error_setg(errp, "A group with this name already exists"); | ||
142 | return; | ||
143 | } | ||
144 | diff --git a/block/throttle.c b/block/throttle.c | ||
145 | new file mode 100644 | 36 | new file mode 100644 |
146 | index XXXXXXX..XXXXXXX | 37 | index XXXXXXX..XXXXXXX |
147 | --- /dev/null | 38 | --- /dev/null |
148 | +++ b/block/throttle.c | 39 | +++ b/include/qemu/transactions.h |
149 | @@ -XXX,XX +XXX,XX @@ | 40 | @@ -XXX,XX +XXX,XX @@ |
150 | +/* | 41 | +/* |
151 | + * QEMU block throttling filter driver infrastructure | 42 | + * Simple transactions API |
152 | + * | 43 | + * |
153 | + * Copyright (c) 2017 Manos Pitsidianakis | 44 | + * Copyright (c) 2021 Virtuozzo International GmbH. |
154 | + * | 45 | + * |
155 | + * This program is free software; you can redistribute it and/or | 46 | + * Author: |
156 | + * modify it under the terms of the GNU General Public License as | 47 | + * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
157 | + * published by the Free Software Foundation; either version 2 or | 48 | + * |
158 | + * (at your option) version 3 of the License. | 49 | + * This program is free software; you can redistribute it and/or modify |
50 | + * it under the terms of the GNU General Public License as published by | ||
51 | + * the Free Software Foundation; either version 2 of the License, or | ||
52 | + * (at your option) any later version. | ||
159 | + * | 53 | + * |
160 | + * This program is distributed in the hope that it will be useful, | 54 | + * This program is distributed in the hope that it will be useful, |
161 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | 55 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
162 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 56 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
163 | + * GNU General Public License for more details. | 57 | + * GNU General Public License for more details. |
164 | + * | 58 | + * |
165 | + * You should have received a copy of the GNU General Public License | 59 | + * 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/>. | 60 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
61 | + * | ||
62 | + * | ||
63 | + * = Generic transaction API = | ||
64 | + * | ||
65 | + * The intended usage is the following: you create "prepare" functions, which | ||
66 | + * represents the actions. They will usually have Transaction* argument, and | ||
67 | + * call tran_add() to register finalization callbacks. For finalization | ||
68 | + * callbacks, prepare corresponding TransactionActionDrv structures. | ||
69 | + * | ||
70 | + * Then, when you need to make a transaction, create an empty Transaction by | ||
71 | + * tran_create(), call your "prepare" functions on it, and finally call | ||
72 | + * tran_abort() or tran_commit() to finalize the transaction by corresponding | ||
73 | + * finalization actions in reverse order. | ||
167 | + */ | 74 | + */ |
168 | + | 75 | + |
76 | +#ifndef QEMU_TRANSACTIONS_H | ||
77 | +#define QEMU_TRANSACTIONS_H | ||
78 | + | ||
79 | +#include <gmodule.h> | ||
80 | + | ||
81 | +typedef struct TransactionActionDrv { | ||
82 | + void (*abort)(void *opaque); | ||
83 | + void (*commit)(void *opaque); | ||
84 | + void (*clean)(void *opaque); | ||
85 | +} TransactionActionDrv; | ||
86 | + | ||
87 | +typedef struct Transaction Transaction; | ||
88 | + | ||
89 | +Transaction *tran_new(void); | ||
90 | +void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque); | ||
91 | +void tran_abort(Transaction *tran); | ||
92 | +void tran_commit(Transaction *tran); | ||
93 | + | ||
94 | +static inline void tran_finalize(Transaction *tran, int ret) | ||
95 | +{ | ||
96 | + if (ret < 0) { | ||
97 | + tran_abort(tran); | ||
98 | + } else { | ||
99 | + tran_commit(tran); | ||
100 | + } | ||
101 | +} | ||
102 | + | ||
103 | +#endif /* QEMU_TRANSACTIONS_H */ | ||
104 | diff --git a/util/transactions.c b/util/transactions.c | ||
105 | new file mode 100644 | ||
106 | index XXXXXXX..XXXXXXX | ||
107 | --- /dev/null | ||
108 | +++ b/util/transactions.c | ||
109 | @@ -XXX,XX +XXX,XX @@ | ||
110 | +/* | ||
111 | + * Simple transactions API | ||
112 | + * | ||
113 | + * Copyright (c) 2021 Virtuozzo International GmbH. | ||
114 | + * | ||
115 | + * Author: | ||
116 | + * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com> | ||
117 | + * | ||
118 | + * This program is free software; you can redistribute it and/or modify | ||
119 | + * it under the terms of the GNU General Public License as published by | ||
120 | + * the Free Software Foundation; either version 2 of the License, or | ||
121 | + * (at your option) any later version. | ||
122 | + * | ||
123 | + * This program is distributed in the hope that it will be useful, | ||
124 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
125 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
126 | + * GNU General Public License for more details. | ||
127 | + * | ||
128 | + * You should have received a copy of the GNU General Public License | ||
129 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
130 | + */ | ||
131 | + | ||
169 | +#include "qemu/osdep.h" | 132 | +#include "qemu/osdep.h" |
170 | +#include "block/throttle-groups.h" | 133 | + |
171 | +#include "qemu/throttle-options.h" | 134 | +#include "qemu/transactions.h" |
172 | +#include "qapi/error.h" | 135 | +#include "qemu/queue.h" |
173 | + | 136 | + |
174 | +static QemuOptsList throttle_opts = { | 137 | +typedef struct TransactionAction { |
175 | + .name = "throttle", | 138 | + TransactionActionDrv *drv; |
176 | + .head = QTAILQ_HEAD_INITIALIZER(throttle_opts.head), | 139 | + void *opaque; |
177 | + .desc = { | 140 | + QSLIST_ENTRY(TransactionAction) entry; |
178 | + { | 141 | +} TransactionAction; |
179 | + .name = QEMU_OPT_THROTTLE_GROUP_NAME, | 142 | + |
180 | + .type = QEMU_OPT_STRING, | 143 | +struct Transaction { |
181 | + .help = "Name of the throttle group", | 144 | + QSLIST_HEAD(, TransactionAction) actions; |
182 | + }, | ||
183 | + { /* end of list */ } | ||
184 | + }, | ||
185 | +}; | 145 | +}; |
186 | + | 146 | + |
187 | +static int throttle_configure_tgm(BlockDriverState *bs, | 147 | +Transaction *tran_new(void) |
188 | + ThrottleGroupMember *tgm, | 148 | +{ |
189 | + QDict *options, Error **errp) | 149 | + Transaction *tran = g_new(Transaction, 1); |
190 | +{ | 150 | + |
191 | + int ret; | 151 | + QSLIST_INIT(&tran->actions); |
192 | + const char *group_name; | 152 | + |
193 | + Error *local_err = NULL; | 153 | + return tran; |
194 | + QemuOpts *opts = qemu_opts_create(&throttle_opts, NULL, 0, &error_abort); | 154 | +} |
195 | + | 155 | + |
196 | + qemu_opts_absorb_qdict(opts, options, &local_err); | 156 | +void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque) |
197 | + if (local_err) { | 157 | +{ |
198 | + error_propagate(errp, local_err); | 158 | + TransactionAction *act; |
199 | + ret = -EINVAL; | 159 | + |
200 | + goto fin; | 160 | + act = g_new(TransactionAction, 1); |
161 | + *act = (TransactionAction) { | ||
162 | + .drv = drv, | ||
163 | + .opaque = opaque | ||
164 | + }; | ||
165 | + | ||
166 | + QSLIST_INSERT_HEAD(&tran->actions, act, entry); | ||
167 | +} | ||
168 | + | ||
169 | +void tran_abort(Transaction *tran) | ||
170 | +{ | ||
171 | + TransactionAction *act, *next; | ||
172 | + | ||
173 | + QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { | ||
174 | + if (act->drv->abort) { | ||
175 | + act->drv->abort(act->opaque); | ||
176 | + } | ||
177 | + | ||
178 | + if (act->drv->clean) { | ||
179 | + act->drv->clean(act->opaque); | ||
180 | + } | ||
181 | + | ||
182 | + g_free(act); | ||
201 | + } | 183 | + } |
202 | + | 184 | + |
203 | + group_name = qemu_opt_get(opts, QEMU_OPT_THROTTLE_GROUP_NAME); | 185 | + g_free(tran); |
204 | + if (!group_name) { | 186 | +} |
205 | + error_setg(errp, "Please specify a throttle group"); | 187 | + |
206 | + ret = -EINVAL; | 188 | +void tran_commit(Transaction *tran) |
207 | + goto fin; | 189 | +{ |
208 | + } else if (!throttle_group_exists(group_name)) { | 190 | + TransactionAction *act, *next; |
209 | + error_setg(errp, "Throttle group '%s' does not exist", group_name); | 191 | + |
210 | + ret = -EINVAL; | 192 | + QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { |
211 | + goto fin; | 193 | + if (act->drv->commit) { |
194 | + act->drv->commit(act->opaque); | ||
195 | + } | ||
196 | + | ||
197 | + if (act->drv->clean) { | ||
198 | + act->drv->clean(act->opaque); | ||
199 | + } | ||
200 | + | ||
201 | + g_free(act); | ||
212 | + } | 202 | + } |
213 | + | 203 | + |
214 | + /* Register membership to group with name group_name */ | 204 | + g_free(tran); |
215 | + throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs)); | 205 | +} |
216 | + ret = 0; | 206 | diff --git a/MAINTAINERS b/MAINTAINERS |
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 | 207 | index XXXXXXX..XXXXXXX 100644 |
389 | --- a/block/Makefile.objs | 208 | --- a/MAINTAINERS |
390 | +++ b/block/Makefile.objs | 209 | +++ b/MAINTAINERS |
391 | @@ -XXX,XX +XXX,XX @@ block-obj-y += accounting.o dirty-bitmap.o | 210 | @@ -XXX,XX +XXX,XX @@ M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
392 | block-obj-y += write-threshold.o | 211 | S: Maintained |
393 | block-obj-y += backup.o | 212 | F: scripts/simplebench/ |
394 | block-obj-$(CONFIG_REPLICATION) += replication.o | 213 | |
395 | +block-obj-y += throttle.o | 214 | +Transactions helper |
396 | 215 | +M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | |
397 | block-obj-y += crypto.o | 216 | +S: Maintained |
398 | 217 | +F: include/qemu/transactions.h | |
218 | +F: util/transactions.c | ||
219 | + | ||
220 | QAPI | ||
221 | M: Markus Armbruster <armbru@redhat.com> | ||
222 | M: Michael Roth <michael.roth@amd.com> | ||
223 | diff --git a/util/meson.build b/util/meson.build | ||
224 | index XXXXXXX..XXXXXXX 100644 | ||
225 | --- a/util/meson.build | ||
226 | +++ b/util/meson.build | ||
227 | @@ -XXX,XX +XXX,XX @@ util_ss.add(files('qsp.c')) | ||
228 | util_ss.add(files('range.c')) | ||
229 | util_ss.add(files('stats64.c')) | ||
230 | util_ss.add(files('systemd.c')) | ||
231 | +util_ss.add(files('transactions.c')) | ||
232 | util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c')) | ||
233 | util_ss.add(files('guest-random.c')) | ||
234 | util_ss.add(files('yank.c')) | ||
399 | -- | 235 | -- |
400 | 2.13.5 | 236 | 2.30.2 |
401 | 237 | ||
402 | 238 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Add additional check that node parents do not interfere with each | ||
4 | other. This should not hurt existing callers and allows in further | ||
5 | patch use bdrv_refresh_perms() to update a subtree of changed | ||
6 | BdrvChild (check that change is correct). | ||
7 | |||
8 | New check will substitute bdrv_check_update_perm() in following | ||
9 | permissions refactoring, so keep error messages the same to avoid | ||
10 | unit test result changes. | ||
11 | |||
12 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
13 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
14 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | Message-Id: <20210428151804.439460-10-vsementsov@virtuozzo.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
17 | --- | ||
18 | block.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++--------- | ||
19 | 1 file changed, 54 insertions(+), 9 deletions(-) | ||
20 | |||
21 | diff --git a/block.c b/block.c | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/block.c | ||
24 | +++ b/block.c | ||
25 | @@ -XXX,XX +XXX,XX @@ bool bdrv_is_writable(BlockDriverState *bs) | ||
26 | return bdrv_is_writable_after_reopen(bs, NULL); | ||
27 | } | ||
28 | |||
29 | +static char *bdrv_child_user_desc(BdrvChild *c) | ||
30 | +{ | ||
31 | + if (c->klass->get_parent_desc) { | ||
32 | + return c->klass->get_parent_desc(c); | ||
33 | + } | ||
34 | + | ||
35 | + return g_strdup("another user"); | ||
36 | +} | ||
37 | + | ||
38 | +static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) | ||
39 | +{ | ||
40 | + g_autofree char *user = NULL; | ||
41 | + g_autofree char *perm_names = NULL; | ||
42 | + | ||
43 | + if ((b->perm & a->shared_perm) == b->perm) { | ||
44 | + return true; | ||
45 | + } | ||
46 | + | ||
47 | + perm_names = bdrv_perm_names(b->perm & ~a->shared_perm); | ||
48 | + user = bdrv_child_user_desc(a); | ||
49 | + error_setg(errp, "Conflicts with use by %s as '%s', which does not " | ||
50 | + "allow '%s' on %s", | ||
51 | + user, a->name, perm_names, bdrv_get_node_name(b->bs)); | ||
52 | + | ||
53 | + return false; | ||
54 | +} | ||
55 | + | ||
56 | +static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | ||
57 | +{ | ||
58 | + BdrvChild *a, *b; | ||
59 | + | ||
60 | + /* | ||
61 | + * During the loop we'll look at each pair twice. That's correct because | ||
62 | + * bdrv_a_allow_b() is asymmetric and we should check each pair in both | ||
63 | + * directions. | ||
64 | + */ | ||
65 | + QLIST_FOREACH(a, &bs->parents, next_parent) { | ||
66 | + QLIST_FOREACH(b, &bs->parents, next_parent) { | ||
67 | + if (a == b) { | ||
68 | + continue; | ||
69 | + } | ||
70 | + | ||
71 | + if (!bdrv_a_allow_b(a, b, errp)) { | ||
72 | + return true; | ||
73 | + } | ||
74 | + } | ||
75 | + } | ||
76 | + | ||
77 | + return false; | ||
78 | +} | ||
79 | + | ||
80 | static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, | ||
81 | BdrvChild *c, BdrvChildRole role, | ||
82 | BlockReopenQueue *reopen_queue, | ||
83 | @@ -XXX,XX +XXX,XX @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
84 | *shared_perm = cumulative_shared_perms; | ||
85 | } | ||
86 | |||
87 | -static char *bdrv_child_user_desc(BdrvChild *c) | ||
88 | -{ | ||
89 | - if (c->klass->get_parent_desc) { | ||
90 | - return c->klass->get_parent_desc(c); | ||
91 | - } | ||
92 | - | ||
93 | - return g_strdup("another user"); | ||
94 | -} | ||
95 | - | ||
96 | char *bdrv_perm_names(uint64_t perm) | ||
97 | { | ||
98 | struct perm_name { | ||
99 | @@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) | ||
100 | int ret; | ||
101 | uint64_t perm, shared_perm; | ||
102 | |||
103 | + if (bdrv_parent_perms_conflict(bs, errp)) { | ||
104 | + return -EPERM; | ||
105 | + } | ||
106 | bdrv_get_cumulative_perm(bs, &perm, &shared_perm); | ||
107 | ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp); | ||
108 | if (ret < 0) { | ||
109 | -- | ||
110 | 2.30.2 | ||
111 | |||
112 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 3 | Split out non-recursive parts, and refactor as block graph transaction |
4 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 4 | action. |
5 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 5 | |
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20210428151804.439460-11-vsementsov@virtuozzo.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | --- | 10 | --- |
8 | tests/qemu-iotests/184 | 205 ++++++++++++++++++++++++++++++ | 11 | block.c | 79 ++++++++++++++++++++++++++++++++++++++++++--------------- |
9 | tests/qemu-iotests/184.out | 302 +++++++++++++++++++++++++++++++++++++++++++++ | 12 | 1 file changed, 59 insertions(+), 20 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 | 13 | ||
15 | diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184 | 14 | diff --git a/block.c b/block.c |
16 | new file mode 100755 | 15 | index XXXXXXX..XXXXXXX 100644 |
17 | index XXXXXXX..XXXXXXX | 16 | --- a/block.c |
18 | --- /dev/null | 17 | +++ b/block.c |
19 | +++ b/tests/qemu-iotests/184 | ||
20 | @@ -XXX,XX +XXX,XX @@ | 18 | @@ -XXX,XX +XXX,XX @@ |
21 | +#!/bin/bash | 19 | #include "qemu/timer.h" |
22 | +# | 20 | #include "qemu/cutils.h" |
23 | +# Test I/O throttle block filter driver interface | 21 | #include "qemu/id.h" |
24 | +# | 22 | +#include "qemu/transactions.h" |
25 | +# Copyright (C) 2017 Manos Pitsidianakis | 23 | #include "block/coroutines.h" |
26 | +# | 24 | |
27 | +# This program is free software; you can redistribute it and/or modify | 25 | #ifdef CONFIG_BSD |
28 | +# it under the terms of the GNU General Public License as published by | 26 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, |
29 | +# the Free Software Foundation; either version 2 of the License, or | 27 | } |
30 | +# (at your option) any later version. | 28 | } |
31 | +# | 29 | |
32 | +# This program is distributed in the hope that it will be useful, | 30 | +static void bdrv_child_set_perm_commit(void *opaque) |
33 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | 31 | +{ |
34 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 32 | + BdrvChild *c = opaque; |
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 | + | 33 | + |
41 | +# creator | 34 | + c->has_backup_perm = false; |
42 | +owner="Manos Pitsidianakis" | ||
43 | + | ||
44 | +seq=`basename $0` | ||
45 | +echo "QA output created by $seq" | ||
46 | + | ||
47 | +here=`pwd` | ||
48 | +status=1 # failure is the default! | ||
49 | + | ||
50 | +_cleanup() | ||
51 | +{ | ||
52 | + _cleanup_test_img | ||
53 | +} | ||
54 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
55 | + | ||
56 | +# get standard environment, filters and checks | ||
57 | +. ./common.rc | ||
58 | +. ./common.filter | ||
59 | + | ||
60 | +_supported_fmt qcow2 | ||
61 | +_supported_proto file | ||
62 | +_supported_os Linux | ||
63 | + | ||
64 | +function do_run_qemu() | ||
65 | +{ | ||
66 | + echo Testing: "$@" | _filter_imgfmt | ||
67 | + $QEMU -nographic -qmp-pretty stdio -serial none "$@" | ||
68 | + echo | ||
69 | +} | 35 | +} |
70 | + | 36 | + |
71 | +function run_qemu() | 37 | +static void bdrv_child_set_perm_abort(void *opaque) |
72 | +{ | 38 | +{ |
73 | + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ | 39 | + BdrvChild *c = opaque; |
74 | + | _filter_qemu_io | _filter_generated_node_ids | 40 | + /* |
75 | +} | 41 | + * We may have child->has_backup_perm unset at this point, as in case of |
76 | + | 42 | + * _check_ stage of permission update failure we may _check_ not the whole |
77 | +_make_test_img 64M | 43 | + * subtree. Still, _abort_ is called on the whole subtree anyway. |
78 | +test_throttle=$($QEMU_IMG --help|grep throttle) | 44 | + */ |
79 | +[ "$test_throttle" = "" ] && _supported_fmt throttle | 45 | + if (c->has_backup_perm) { |
80 | + | 46 | + c->perm = c->backup_perm; |
81 | +echo | 47 | + c->shared_perm = c->backup_shared_perm; |
82 | +echo "== checking interface ==" | 48 | + c->has_backup_perm = false; |
83 | + | ||
84 | +run_qemu <<EOF | ||
85 | +{ "execute": "qmp_capabilities" } | ||
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 | + } | 49 | + } |
375 | +} | 50 | +} |
376 | + | 51 | + |
52 | +static TransactionActionDrv bdrv_child_set_pem_drv = { | ||
53 | + .abort = bdrv_child_set_perm_abort, | ||
54 | + .commit = bdrv_child_set_perm_commit, | ||
55 | +}; | ||
377 | + | 56 | + |
378 | +== property changes in ThrottleGroup == | 57 | +/* |
379 | +Testing: | 58 | + * With tran=NULL needs to be followed by direct call to either |
59 | + * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort(). | ||
60 | + * | ||
61 | + * With non-NULL tran needs to be followed by tran_abort() or tran_commit() | ||
62 | + * instead. | ||
63 | + */ | ||
64 | +static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm, | ||
65 | + uint64_t shared, Transaction *tran) | ||
380 | +{ | 66 | +{ |
381 | + QMP_VERSION | 67 | + if (!c->has_backup_perm) { |
382 | +} | 68 | + c->has_backup_perm = true; |
383 | +{ | 69 | + c->backup_perm = c->perm; |
384 | + "return": { | 70 | + c->backup_shared_perm = c->shared_perm; |
385 | + } | 71 | + } |
386 | +} | 72 | + /* |
387 | +{ | 73 | + * Note: it's OK if c->has_backup_perm was already set, as we can find the |
388 | + "return": { | 74 | + * same c twice during check_perm procedure |
389 | + } | 75 | + */ |
390 | +} | 76 | + |
391 | +{ | 77 | + c->perm = perm; |
392 | + "return": { | 78 | + c->shared_perm = shared; |
393 | + "bps-read-max-length": 1, | 79 | + |
394 | + "iops-read-max-length": 1, | 80 | + if (tran) { |
395 | + "bps-read-max": 0, | 81 | + tran_add(tran, &bdrv_child_set_pem_drv, c); |
396 | + "bps-total": 0, | ||
397 | + "iops-total-max-length": 1, | ||
398 | + "iops-total": 1000, | ||
399 | + "iops-write-max": 0, | ||
400 | + "bps-write": 0, | ||
401 | + "bps-total-max": 0, | ||
402 | + "bps-write-max": 0, | ||
403 | + "iops-size": 0, | ||
404 | + "iops-read": 0, | ||
405 | + "iops-write-max-length": 1, | ||
406 | + "iops-write": 0, | ||
407 | + "bps-total-max-length": 1, | ||
408 | + "iops-read-max": 0, | ||
409 | + "bps-read": 0, | ||
410 | + "bps-write-max-length": 1, | ||
411 | + "iops-total-max": 0 | ||
412 | + } | ||
413 | +} | ||
414 | +{ | ||
415 | + "return": { | ||
416 | + } | ||
417 | +} | ||
418 | +{ | ||
419 | + "return": { | ||
420 | + "bps-read-max-length": 1, | ||
421 | + "iops-read-max-length": 1, | ||
422 | + "bps-read-max": 0, | ||
423 | + "bps-total": 0, | ||
424 | + "iops-total-max-length": 1, | ||
425 | + "iops-total": 0, | ||
426 | + "iops-write-max": 0, | ||
427 | + "bps-write": 0, | ||
428 | + "bps-total-max": 0, | ||
429 | + "bps-write-max": 0, | ||
430 | + "iops-size": 0, | ||
431 | + "iops-read": 0, | ||
432 | + "iops-write-max-length": 1, | ||
433 | + "iops-write": 0, | ||
434 | + "bps-total-max-length": 1, | ||
435 | + "iops-read-max": 0, | ||
436 | + "bps-read": 0, | ||
437 | + "bps-write-max-length": 1, | ||
438 | + "iops-total-max": 0 | ||
439 | + } | ||
440 | +} | ||
441 | +{ | ||
442 | + "return": { | ||
443 | + } | ||
444 | +} | ||
445 | +{ | ||
446 | + "timestamp": { | ||
447 | + "seconds": TIMESTAMP, | ||
448 | + "microseconds": TIMESTAMP | ||
449 | + }, | ||
450 | + "event": "SHUTDOWN", | ||
451 | + "data": { | ||
452 | + "guest": false | ||
453 | + } | 82 | + } |
454 | +} | 83 | +} |
455 | + | 84 | + |
456 | + | 85 | /* |
457 | +== object creation/set errors == | 86 | * Check whether permissions on this node can be changed in a way that |
458 | +Testing: | 87 | * @cumulative_perms and @cumulative_shared_perms are the new cumulative |
459 | +{ | 88 | @@ -XXX,XX +XXX,XX @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, |
460 | + QMP_VERSION | 89 | return ret; |
461 | +} | 90 | } |
462 | +{ | 91 | |
463 | + "return": { | 92 | - if (!c->has_backup_perm) { |
464 | + } | 93 | - c->has_backup_perm = true; |
465 | +} | 94 | - c->backup_perm = c->perm; |
466 | +{ | 95 | - c->backup_shared_perm = c->shared_perm; |
467 | + "return": { | 96 | - } |
468 | + } | 97 | - /* |
469 | +} | 98 | - * Note: it's OK if c->has_backup_perm was already set, as we can find the |
470 | +{ | 99 | - * same child twice during check_perm procedure |
471 | + "error": { | 100 | - */ |
472 | + "class": "GenericError", | 101 | - |
473 | + "desc": "Property cannot be set after initialization" | 102 | - c->perm = perm; |
474 | + } | 103 | - c->shared_perm = shared; |
475 | +} | 104 | + bdrv_child_set_perm_safe(c, perm, shared, NULL); |
476 | +{ | 105 | |
477 | + "error": { | 106 | return 0; |
478 | + "class": "GenericError", | 107 | } |
479 | + "desc": "bps/iops/max total values and read/write values cannot be used at the same time" | 108 | |
480 | + } | 109 | static void bdrv_child_set_perm(BdrvChild *c) |
481 | +} | 110 | { |
482 | +{ | 111 | - c->has_backup_perm = false; |
483 | + "return": { | 112 | - |
484 | + } | 113 | + bdrv_child_set_perm_commit(c); |
485 | +} | 114 | bdrv_set_perm(c->bs); |
486 | +{ | 115 | } |
487 | + "timestamp": { | 116 | |
488 | + "seconds": TIMESTAMP, | 117 | static void bdrv_child_abort_perm_update(BdrvChild *c) |
489 | + "microseconds": TIMESTAMP | 118 | { |
490 | + }, | 119 | - if (c->has_backup_perm) { |
491 | + "event": "SHUTDOWN", | 120 | - c->perm = c->backup_perm; |
492 | + "data": { | 121 | - c->shared_perm = c->backup_shared_perm; |
493 | + "guest": false | 122 | - c->has_backup_perm = false; |
494 | + } | 123 | - } |
495 | +} | 124 | - |
496 | + | 125 | + bdrv_child_set_perm_abort(c); |
497 | + | 126 | bdrv_abort_perm_update(c->bs); |
498 | +== don't specify group == | 127 | } |
499 | +Testing: | 128 | |
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 | -- | 129 | -- |
547 | 2.13.5 | 130 | 2.30.2 |
548 | 131 | ||
549 | 132 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | We are going to drop recursive bdrv_child_* functions, so stop use them | ||
4 | in bdrv_child_try_set_perm() as a first step. | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20210428151804.439460-12-vsementsov@virtuozzo.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | block.c | 14 ++++++++------ | ||
12 | 1 file changed, 8 insertions(+), 6 deletions(-) | ||
13 | |||
14 | diff --git a/block.c b/block.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block.c | ||
17 | +++ b/block.c | ||
18 | @@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||
19 | Error **errp) | ||
20 | { | ||
21 | Error *local_err = NULL; | ||
22 | + Transaction *tran = tran_new(); | ||
23 | int ret; | ||
24 | |||
25 | - ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, &local_err); | ||
26 | + bdrv_child_set_perm_safe(c, perm, shared, tran); | ||
27 | + | ||
28 | + ret = bdrv_refresh_perms(c->bs, &local_err); | ||
29 | + | ||
30 | + tran_finalize(tran, ret); | ||
31 | + | ||
32 | if (ret < 0) { | ||
33 | - bdrv_child_abort_perm_update(c); | ||
34 | if ((perm & ~c->perm) || (c->shared_perm & ~shared)) { | ||
35 | /* tighten permissions */ | ||
36 | error_propagate(errp, local_err); | ||
37 | @@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||
38 | error_free(local_err); | ||
39 | ret = 0; | ||
40 | } | ||
41 | - return ret; | ||
42 | } | ||
43 | |||
44 | - bdrv_child_set_perm(c); | ||
45 | - | ||
46 | - return 0; | ||
47 | + return ret; | ||
48 | } | ||
49 | |||
50 | int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp) | ||
51 | -- | ||
52 | 2.30.2 | ||
53 | |||
54 | 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 | Each of them has only one caller. Open-coding simplifies further |
4 | throttle group state. Users will be able to create multiple throttle | 4 | pemission-update system changes. |
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 | ||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
11 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 7 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
12 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
13 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 9 | Message-Id: <20210428151804.439460-13-vsementsov@virtuozzo.com> |
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | --- | 11 | --- |
16 | include/block/throttle-groups.h | 39 +++++- | 12 | block.c | 59 +++++++++++++++++---------------------------------------- |
17 | include/sysemu/block-backend.h | 20 +-- | 13 | 1 file changed, 17 insertions(+), 42 deletions(-) |
18 | block/block-backend.c | 66 +++++---- | ||
19 | block/qapi.c | 8 +- | ||
20 | block/throttle-groups.c | 288 ++++++++++++++++++++-------------------- | ||
21 | blockdev.c | 4 +- | ||
22 | tests/test-throttle.c | 53 ++++---- | ||
23 | 7 files changed, 252 insertions(+), 226 deletions(-) | ||
24 | 14 | ||
25 | diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h | 15 | diff --git a/block.c b/block.c |
26 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/include/block/throttle-groups.h | 17 | --- a/block.c |
28 | +++ b/include/block/throttle-groups.h | 18 | +++ b/block.c |
29 | @@ -XXX,XX +XXX,XX @@ | 19 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, |
30 | #include "qemu/throttle.h" | 20 | return 0; |
31 | #include "block/block_int.h" | 21 | } |
32 | 22 | ||
33 | -const char *throttle_group_get_name(BlockBackend *blk); | 23 | -static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, |
34 | +/* The ThrottleGroupMember structure indicates membership in a ThrottleGroup | 24 | - uint64_t perm, uint64_t shared, |
35 | + * and holds related data. | 25 | - GSList *ignore_children, Error **errp); |
36 | + */ | 26 | -static void bdrv_child_abort_perm_update(BdrvChild *c); |
27 | -static void bdrv_child_set_perm(BdrvChild *c); | ||
28 | +static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
29 | + uint64_t new_used_perm, | ||
30 | + uint64_t new_shared_perm, | ||
31 | + GSList *ignore_children, | ||
32 | + Error **errp); | ||
33 | |||
34 | typedef struct BlockReopenQueueEntry { | ||
35 | bool prepared; | ||
36 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
37 | /* Check all children */ | ||
38 | QLIST_FOREACH(c, &bs->children, next) { | ||
39 | uint64_t cur_perm, cur_shared; | ||
40 | + GSList *cur_ignore_children; | ||
41 | |||
42 | bdrv_child_perm(bs, c->bs, c, c->role, q, | ||
43 | cumulative_perms, cumulative_shared_perms, | ||
44 | &cur_perm, &cur_shared); | ||
45 | - ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children, | ||
46 | - errp); | ||
37 | + | 47 | + |
38 | +typedef struct ThrottleGroupMember { | 48 | + cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c); |
39 | + /* throttled_reqs_lock protects the CoQueues for throttled requests. */ | 49 | + ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared, |
40 | + CoMutex throttled_reqs_lock; | 50 | + cur_ignore_children, errp); |
41 | + CoQueue throttled_reqs[2]; | 51 | + g_slist_free(cur_ignore_children); |
52 | if (ret < 0) { | ||
53 | return ret; | ||
54 | } | ||
42 | + | 55 | + |
43 | + /* Nonzero if the I/O limits are currently being ignored; generally | 56 | + bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL); |
44 | + * it is zero. Accessed with atomic operations. | ||
45 | + */ | ||
46 | + unsigned int io_limits_disabled; | ||
47 | + | ||
48 | + /* The following fields are protected by the ThrottleGroup lock. | ||
49 | + * See the ThrottleGroup documentation for details. | ||
50 | + * throttle_state tells us if I/O limits are configured. */ | ||
51 | + ThrottleState *throttle_state; | ||
52 | + ThrottleTimers throttle_timers; | ||
53 | + unsigned pending_reqs[2]; | ||
54 | + QLIST_ENTRY(ThrottleGroupMember) round_robin; | ||
55 | + | ||
56 | +} ThrottleGroupMember; | ||
57 | + | ||
58 | +const char *throttle_group_get_name(ThrottleGroupMember *tgm); | ||
59 | |||
60 | ThrottleState *throttle_group_incref(const char *name); | ||
61 | void throttle_group_unref(ThrottleState *ts); | ||
62 | |||
63 | -void throttle_group_config(BlockBackend *blk, ThrottleConfig *cfg); | ||
64 | -void throttle_group_get_config(BlockBackend *blk, ThrottleConfig *cfg); | ||
65 | +void throttle_group_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg); | ||
66 | +void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg); | ||
67 | |||
68 | -void throttle_group_register_blk(BlockBackend *blk, const char *groupname); | ||
69 | -void throttle_group_unregister_blk(BlockBackend *blk); | ||
70 | -void throttle_group_restart_blk(BlockBackend *blk); | ||
71 | +void throttle_group_register_tgm(ThrottleGroupMember *tgm, | ||
72 | + const char *groupname); | ||
73 | +void throttle_group_unregister_tgm(ThrottleGroupMember *tgm); | ||
74 | +void throttle_group_restart_tgm(ThrottleGroupMember *tgm); | ||
75 | |||
76 | -void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk, | ||
77 | +void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, | ||
78 | unsigned int bytes, | ||
79 | bool is_write); | ||
80 | |||
81 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | ||
82 | index XXXXXXX..XXXXXXX 100644 | ||
83 | --- a/include/sysemu/block-backend.h | ||
84 | +++ b/include/sysemu/block-backend.h | ||
85 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockDevOps { | ||
86 | |||
87 | /* This struct is embedded in (the private) BlockBackend struct and contains | ||
88 | * fields that must be public. This is in particular for QLIST_ENTRY() and | ||
89 | - * friends so that BlockBackends can be kept in lists outside block-backend.c */ | ||
90 | + * friends so that BlockBackends can be kept in lists outside block-backend.c | ||
91 | + * */ | ||
92 | typedef struct BlockBackendPublic { | ||
93 | - /* throttled_reqs_lock protects the CoQueues for throttled requests. */ | ||
94 | - CoMutex throttled_reqs_lock; | ||
95 | - CoQueue throttled_reqs[2]; | ||
96 | - | ||
97 | - /* Nonzero if the I/O limits are currently being ignored; generally | ||
98 | - * it is zero. Accessed with atomic operations. | ||
99 | - */ | ||
100 | - unsigned int io_limits_disabled; | ||
101 | - | ||
102 | - /* The following fields are protected by the ThrottleGroup lock. | ||
103 | - * See the ThrottleGroup documentation for details. | ||
104 | - * throttle_state tells us if I/O limits are configured. */ | ||
105 | - ThrottleState *throttle_state; | ||
106 | - ThrottleTimers throttle_timers; | ||
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 | ||
114 | index XXXXXXX..XXXXXXX 100644 | ||
115 | --- a/block/block-backend.c | ||
116 | +++ b/block/block-backend.c | ||
117 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | ||
118 | blk->shared_perm = shared_perm; | ||
119 | blk_set_enable_write_cache(blk, true); | ||
120 | |||
121 | - qemu_co_mutex_init(&blk->public.throttled_reqs_lock); | ||
122 | - qemu_co_queue_init(&blk->public.throttled_reqs[0]); | ||
123 | - qemu_co_queue_init(&blk->public.throttled_reqs[1]); | ||
124 | + qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock); | ||
125 | + qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]); | ||
126 | + qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]); | ||
127 | block_acct_init(&blk->stats); | ||
128 | |||
129 | notifier_list_init(&blk->remove_bs_notifiers); | ||
130 | @@ -XXX,XX +XXX,XX @@ static void blk_delete(BlockBackend *blk) | ||
131 | assert(!blk->refcnt); | ||
132 | assert(!blk->name); | ||
133 | assert(!blk->dev); | ||
134 | - if (blk->public.throttle_state) { | ||
135 | + if (blk->public.throttle_group_member.throttle_state) { | ||
136 | blk_io_limits_disable(blk); | ||
137 | } | 57 | } |
138 | if (blk->root) { | 58 | |
139 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public) | 59 | return 0; |
140 | */ | 60 | @@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs) |
141 | void blk_remove_bs(BlockBackend *blk) | ||
142 | { | ||
143 | + ThrottleTimers *tt; | ||
144 | + | ||
145 | notifier_list_notify(&blk->remove_bs_notifiers, blk); | ||
146 | - if (blk->public.throttle_state) { | ||
147 | - throttle_timers_detach_aio_context(&blk->public.throttle_timers); | ||
148 | + if (blk->public.throttle_group_member.throttle_state) { | ||
149 | + tt = &blk->public.throttle_group_member.throttle_timers; | ||
150 | + throttle_timers_detach_aio_context(tt); | ||
151 | } | 61 | } |
152 | 62 | ||
153 | blk_update_root_state(blk); | 63 | QLIST_FOREACH(c, &bs->children, next) { |
154 | @@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) | 64 | - bdrv_child_abort_perm_update(c); |
155 | bdrv_ref(bs); | 65 | + bdrv_child_set_perm_abort(c); |
156 | 66 | + bdrv_abort_perm_update(c->bs); | |
157 | notifier_list_notify(&blk->insert_bs_notifiers, blk); | ||
158 | - if (blk->public.throttle_state) { | ||
159 | + if (blk->public.throttle_group_member.throttle_state) { | ||
160 | throttle_timers_attach_aio_context( | ||
161 | - &blk->public.throttle_timers, bdrv_get_aio_context(bs)); | ||
162 | + &blk->public.throttle_group_member.throttle_timers, | ||
163 | + bdrv_get_aio_context(bs)); | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, | ||
168 | bdrv_inc_in_flight(bs); | ||
169 | |||
170 | /* throttling disk I/O */ | ||
171 | - if (blk->public.throttle_state) { | ||
172 | - throttle_group_co_io_limits_intercept(blk, bytes, false); | ||
173 | + if (blk->public.throttle_group_member.throttle_state) { | ||
174 | + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, | ||
175 | + bytes, false); | ||
176 | } | ||
177 | |||
178 | ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); | ||
179 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, | ||
180 | } | ||
181 | |||
182 | bdrv_inc_in_flight(bs); | ||
183 | - | ||
184 | /* throttling disk I/O */ | ||
185 | - if (blk->public.throttle_state) { | ||
186 | - throttle_group_co_io_limits_intercept(blk, bytes, true); | ||
187 | + if (blk->public.throttle_group_member.throttle_state) { | ||
188 | + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, | ||
189 | + bytes, true); | ||
190 | } | ||
191 | |||
192 | if (!blk->enable_write_cache) { | ||
193 | @@ -XXX,XX +XXX,XX @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||
194 | void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) | ||
195 | { | ||
196 | BlockDriverState *bs = blk_bs(blk); | ||
197 | + ThrottleTimers *tt; | ||
198 | |||
199 | if (bs) { | ||
200 | - if (blk->public.throttle_state) { | ||
201 | - throttle_timers_detach_aio_context(&blk->public.throttle_timers); | ||
202 | + if (blk->public.throttle_group_member.throttle_state) { | ||
203 | + tt = &blk->public.throttle_group_member.throttle_timers; | ||
204 | + throttle_timers_detach_aio_context(tt); | ||
205 | } | ||
206 | bdrv_set_aio_context(bs, new_context); | ||
207 | - if (blk->public.throttle_state) { | ||
208 | - throttle_timers_attach_aio_context(&blk->public.throttle_timers, | ||
209 | - new_context); | ||
210 | + if (blk->public.throttle_group_member.throttle_state) { | ||
211 | + tt = &blk->public.throttle_group_member.throttle_timers; | ||
212 | + throttle_timers_attach_aio_context(tt, new_context); | ||
213 | } | ||
214 | } | 67 | } |
215 | } | 68 | } |
216 | @@ -XXX,XX +XXX,XX @@ int blk_commit_all(void) | 69 | |
217 | /* throttling disk I/O limits */ | 70 | @@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs) |
218 | void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) | 71 | |
219 | { | 72 | /* Update all children */ |
220 | - throttle_group_config(blk, cfg); | 73 | QLIST_FOREACH(c, &bs->children, next) { |
221 | + throttle_group_config(&blk->public.throttle_group_member, cfg); | 74 | - bdrv_child_set_perm(c); |
222 | } | 75 | + bdrv_child_set_perm_commit(c); |
223 | 76 | + bdrv_set_perm(c->bs); | |
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 | } | 77 | } |
267 | } | 78 | } |
268 | 79 | ||
269 | @@ -XXX,XX +XXX,XX @@ static void blk_root_drained_end(BdrvChild *child) | 80 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, |
270 | BlockBackend *blk = child->opaque; | 81 | ignore_children, errp); |
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 | } | 82 | } |
357 | 83 | ||
358 | -/* Get the name from a BlockBackend's ThrottleGroup. The name (and the pointer) | 84 | -/* Needs to be followed by a call to either bdrv_child_set_perm() or |
359 | +/* Get the name from a ThrottleGroupMember's group. The name (and the pointer) | 85 | - * bdrv_child_abort_perm_update(). */ |
360 | * is guaranteed to remain constant during the lifetime of the group. | 86 | -static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, |
361 | * | 87 | - uint64_t perm, uint64_t shared, |
362 | - * @blk: a BlockBackend that is member of a throttling group | 88 | - GSList *ignore_children, Error **errp) |
363 | + * @tgm: a ThrottleGroupMember | 89 | -{ |
364 | * @ret: the name of the group. | 90 | - int ret; |
365 | */ | 91 | - |
366 | -const char *throttle_group_get_name(BlockBackend *blk) | 92 | - ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c); |
367 | +const char *throttle_group_get_name(ThrottleGroupMember *tgm) | 93 | - ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp); |
94 | - g_slist_free(ignore_children); | ||
95 | - | ||
96 | - if (ret < 0) { | ||
97 | - return ret; | ||
98 | - } | ||
99 | - | ||
100 | - bdrv_child_set_perm_safe(c, perm, shared, NULL); | ||
101 | - | ||
102 | - return 0; | ||
103 | -} | ||
104 | - | ||
105 | -static void bdrv_child_set_perm(BdrvChild *c) | ||
106 | -{ | ||
107 | - bdrv_child_set_perm_commit(c); | ||
108 | - bdrv_set_perm(c->bs); | ||
109 | -} | ||
110 | - | ||
111 | -static void bdrv_child_abort_perm_update(BdrvChild *c) | ||
112 | -{ | ||
113 | - bdrv_child_set_perm_abort(c); | ||
114 | - bdrv_abort_perm_update(c->bs); | ||
115 | -} | ||
116 | - | ||
117 | static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) | ||
368 | { | 118 | { |
369 | - BlockBackendPublic *blkp = blk_get_public(blk); | 119 | int ret; |
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 | -- | 120 | -- |
1004 | 2.13.5 | 121 | 2.30.2 |
1005 | 122 | ||
1006 | 123 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | |
2 | |||
3 | Rewrite bdrv_check_perm(), bdrv_abort_perm_update() and bdrv_set_perm() | ||
4 | to update nodes in topological sort order instead of simple DFS. With | ||
5 | topologically sorted nodes, we update a node only when all its parents | ||
6 | already updated. With DFS it's not so. | ||
7 | |||
8 | Consider the following example: | ||
9 | |||
10 | A -+ | ||
11 | | | | ||
12 | | v | ||
13 | | B | ||
14 | | | | ||
15 | v | | ||
16 | C<-+ | ||
17 | |||
18 | A is parent for B and C, B is parent for C. | ||
19 | |||
20 | Obviously, to update permissions, we should go in order A B C, so, when | ||
21 | we update C, all parent permissions already updated. But with current | ||
22 | approach (simple recursion) we can update in sequence A C B C (C is | ||
23 | updated twice). On first update of C, we consider old B permissions, so | ||
24 | doing wrong thing. If it succeed, all is OK, on second C update we will | ||
25 | finish with correct graph. But if the wrong thing failed, we break the | ||
26 | whole process for no reason (it's possible that updated B permission | ||
27 | will be less strict, but we will never check it). | ||
28 | |||
29 | Also new approach gives a way to simultaneously and correctly update | ||
30 | several nodes, we just need to run bdrv_topological_dfs() several times | ||
31 | to add all nodes and their subtrees into one topologically sorted list | ||
32 | (next patch will update bdrv_replace_node() in this manner). | ||
33 | |||
34 | Test test_parallel_perm_update() is now passing, so move it out of | ||
35 | debugging "if". | ||
36 | |||
37 | We also need to support ignore_children in | ||
38 | bdrv_parent_perms_conflict() | ||
39 | |||
40 | For test 283 order of conflicting parents check is changed. | ||
41 | |||
42 | Note also that in bdrv_check_perm() we don't check for parents conflict | ||
43 | at root bs, as we may be in the middle of permission update in | ||
44 | bdrv_reopen_multiple(). bdrv_reopen_multiple() will be updated soon. | ||
45 | |||
46 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
47 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
48 | Message-Id: <20210428151804.439460-14-vsementsov@virtuozzo.com> | ||
49 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
50 | --- | ||
51 | block.c | 116 +++++++++++++++++++++++++------ | ||
52 | tests/unit/test-bdrv-graph-mod.c | 4 +- | ||
53 | tests/qemu-iotests/283.out | 2 +- | ||
54 | 3 files changed, 99 insertions(+), 23 deletions(-) | ||
55 | |||
56 | diff --git a/block.c b/block.c | ||
57 | index XXXXXXX..XXXXXXX 100644 | ||
58 | --- a/block.c | ||
59 | +++ b/block.c | ||
60 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | -static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | ||
65 | +static bool bdrv_parent_perms_conflict(BlockDriverState *bs, | ||
66 | + GSList *ignore_children, | ||
67 | + Error **errp) | ||
68 | { | ||
69 | BdrvChild *a, *b; | ||
70 | |||
71 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | ||
72 | * directions. | ||
73 | */ | ||
74 | QLIST_FOREACH(a, &bs->parents, next_parent) { | ||
75 | + if (g_slist_find(ignore_children, a)) { | ||
76 | + continue; | ||
77 | + } | ||
78 | + | ||
79 | QLIST_FOREACH(b, &bs->parents, next_parent) { | ||
80 | - if (a == b) { | ||
81 | + if (a == b || g_slist_find(ignore_children, b)) { | ||
82 | continue; | ||
83 | } | ||
84 | |||
85 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, | ||
86 | } | ||
87 | } | ||
88 | |||
89 | +/* | ||
90 | + * Adds the whole subtree of @bs (including @bs itself) to the @list (except for | ||
91 | + * nodes that are already in the @list, of course) so that final list is | ||
92 | + * topologically sorted. Return the result (GSList @list object is updated, so | ||
93 | + * don't use old reference after function call). | ||
94 | + * | ||
95 | + * On function start @list must be already topologically sorted and for any node | ||
96 | + * in the @list the whole subtree of the node must be in the @list as well. The | ||
97 | + * simplest way to satisfy this criteria: use only result of | ||
98 | + * bdrv_topological_dfs() or NULL as @list parameter. | ||
99 | + */ | ||
100 | +static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found, | ||
101 | + BlockDriverState *bs) | ||
102 | +{ | ||
103 | + BdrvChild *child; | ||
104 | + g_autoptr(GHashTable) local_found = NULL; | ||
105 | + | ||
106 | + if (!found) { | ||
107 | + assert(!list); | ||
108 | + found = local_found = g_hash_table_new(NULL, NULL); | ||
109 | + } | ||
110 | + | ||
111 | + if (g_hash_table_contains(found, bs)) { | ||
112 | + return list; | ||
113 | + } | ||
114 | + g_hash_table_add(found, bs); | ||
115 | + | ||
116 | + QLIST_FOREACH(child, &bs->children, next) { | ||
117 | + list = bdrv_topological_dfs(list, found, child->bs); | ||
118 | + } | ||
119 | + | ||
120 | + return g_slist_prepend(list, bs); | ||
121 | +} | ||
122 | + | ||
123 | static void bdrv_child_set_perm_commit(void *opaque) | ||
124 | { | ||
125 | BdrvChild *c = opaque; | ||
126 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm, | ||
127 | * A call to this function must always be followed by a call to bdrv_set_perm() | ||
128 | * or bdrv_abort_perm_update(). | ||
129 | */ | ||
130 | -static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
131 | - uint64_t cumulative_perms, | ||
132 | - uint64_t cumulative_shared_perms, | ||
133 | - GSList *ignore_children, Error **errp) | ||
134 | +static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
135 | + uint64_t cumulative_perms, | ||
136 | + uint64_t cumulative_shared_perms, | ||
137 | + GSList *ignore_children, Error **errp) | ||
138 | { | ||
139 | BlockDriver *drv = bs->drv; | ||
140 | BdrvChild *c; | ||
141 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
142 | /* Check all children */ | ||
143 | QLIST_FOREACH(c, &bs->children, next) { | ||
144 | uint64_t cur_perm, cur_shared; | ||
145 | - GSList *cur_ignore_children; | ||
146 | |||
147 | bdrv_child_perm(bs, c->bs, c, c->role, q, | ||
148 | cumulative_perms, cumulative_shared_perms, | ||
149 | &cur_perm, &cur_shared); | ||
150 | + bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL); | ||
151 | + } | ||
152 | + | ||
153 | + return 0; | ||
154 | +} | ||
155 | + | ||
156 | +static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
157 | + uint64_t cumulative_perms, | ||
158 | + uint64_t cumulative_shared_perms, | ||
159 | + GSList *ignore_children, Error **errp) | ||
160 | +{ | ||
161 | + int ret; | ||
162 | + BlockDriverState *root = bs; | ||
163 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root); | ||
164 | |||
165 | - cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c); | ||
166 | - ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared, | ||
167 | - cur_ignore_children, errp); | ||
168 | - g_slist_free(cur_ignore_children); | ||
169 | + for ( ; list; list = list->next) { | ||
170 | + bs = list->data; | ||
171 | + | ||
172 | + if (bs != root) { | ||
173 | + if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) { | ||
174 | + return -EINVAL; | ||
175 | + } | ||
176 | + | ||
177 | + bdrv_get_cumulative_perm(bs, &cumulative_perms, | ||
178 | + &cumulative_shared_perms); | ||
179 | + } | ||
180 | + | ||
181 | + ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
182 | + cumulative_shared_perms, | ||
183 | + ignore_children, errp); | ||
184 | if (ret < 0) { | ||
185 | return ret; | ||
186 | } | ||
187 | - | ||
188 | - bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL); | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
193 | * Notifies drivers that after a previous bdrv_check_perm() call, the | ||
194 | * permission update is not performed and any preparations made for it (e.g. | ||
195 | * taken file locks) need to be undone. | ||
196 | - * | ||
197 | - * This function recursively notifies all child nodes. | ||
198 | */ | ||
199 | -static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
200 | +static void bdrv_node_abort_perm_update(BlockDriverState *bs) | ||
201 | { | ||
202 | BlockDriver *drv = bs->drv; | ||
203 | BdrvChild *c; | ||
204 | @@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
205 | |||
206 | QLIST_FOREACH(c, &bs->children, next) { | ||
207 | bdrv_child_set_perm_abort(c); | ||
208 | - bdrv_abort_perm_update(c->bs); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | -static void bdrv_set_perm(BlockDriverState *bs) | ||
213 | +static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
214 | +{ | ||
215 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
216 | + | ||
217 | + for ( ; list; list = list->next) { | ||
218 | + bdrv_node_abort_perm_update((BlockDriverState *)list->data); | ||
219 | + } | ||
220 | +} | ||
221 | + | ||
222 | +static void bdrv_node_set_perm(BlockDriverState *bs) | ||
223 | { | ||
224 | uint64_t cumulative_perms, cumulative_shared_perms; | ||
225 | BlockDriver *drv = bs->drv; | ||
226 | @@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs) | ||
227 | /* Update all children */ | ||
228 | QLIST_FOREACH(c, &bs->children, next) { | ||
229 | bdrv_child_set_perm_commit(c); | ||
230 | - bdrv_set_perm(c->bs); | ||
231 | + } | ||
232 | +} | ||
233 | + | ||
234 | +static void bdrv_set_perm(BlockDriverState *bs) | ||
235 | +{ | ||
236 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
237 | + | ||
238 | + for ( ; list; list = list->next) { | ||
239 | + bdrv_node_set_perm((BlockDriverState *)list->data); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | @@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) | ||
244 | int ret; | ||
245 | uint64_t perm, shared_perm; | ||
246 | |||
247 | - if (bdrv_parent_perms_conflict(bs, errp)) { | ||
248 | + if (bdrv_parent_perms_conflict(bs, NULL, errp)) { | ||
249 | return -EPERM; | ||
250 | } | ||
251 | bdrv_get_cumulative_perm(bs, &perm, &shared_perm); | ||
252 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
253 | index XXXXXXX..XXXXXXX 100644 | ||
254 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
255 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
256 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) | ||
257 | g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree); | ||
258 | g_test_add_func("/bdrv-graph-mod/should-update-child", | ||
259 | test_should_update_child); | ||
260 | + g_test_add_func("/bdrv-graph-mod/parallel-perm-update", | ||
261 | + test_parallel_perm_update); | ||
262 | |||
263 | if (debug) { | ||
264 | g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write", | ||
265 | test_parallel_exclusive_write); | ||
266 | - g_test_add_func("/bdrv-graph-mod/parallel-perm-update", | ||
267 | - test_parallel_perm_update); | ||
268 | g_test_add_func("/bdrv-graph-mod/append-greedy-filter", | ||
269 | test_append_greedy_filter); | ||
270 | } | ||
271 | diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out | ||
272 | index XXXXXXX..XXXXXXX 100644 | ||
273 | --- a/tests/qemu-iotests/283.out | ||
274 | +++ b/tests/qemu-iotests/283.out | ||
275 | @@ -XXX,XX +XXX,XX @@ | ||
276 | {"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}} | ||
277 | {"return": {}} | ||
278 | {"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}} | ||
279 | -{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}} | ||
280 | +{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}} | ||
281 | |||
282 | === backup-top should be gone after job-finalize === | ||
283 | |||
284 | -- | ||
285 | 2.30.2 | ||
286 | |||
287 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | timer_cb() needs to know about the current Aio context of the throttle | 3 | Refactor calling driver callbacks to a separate transaction action to |
4 | request that is woken up. In order to make ThrottleGroupMember backend | 4 | be used later. |
5 | agnostic, this information is stored in an aio_context field instead of | ||
6 | accessing it from BlockBackend. | ||
7 | 5 | ||
8 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
10 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 8 | Message-Id: <20210428151804.439460-15-vsementsov@virtuozzo.com> |
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 10 | --- |
13 | include/block/throttle-groups.h | 7 ++++- | 11 | block.c | 70 ++++++++++++++++++++++++++++++++++++++++++++------------- |
14 | block/block-backend.c | 15 ++++------ | 12 | 1 file changed, 54 insertions(+), 16 deletions(-) |
15 | block/throttle-groups.c | 38 ++++++++++++++++--------- | ||
16 | tests/test-throttle.c | 63 +++++++++++++++++++++-------------------- | ||
17 | 4 files changed, 69 insertions(+), 54 deletions(-) | ||
18 | 13 | ||
19 | diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h | 14 | diff --git a/block.c b/block.c |
20 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/include/block/throttle-groups.h | 16 | --- a/block.c |
22 | +++ b/include/block/throttle-groups.h | 17 | +++ b/block.c |
23 | @@ -XXX,XX +XXX,XX @@ | 18 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm, |
24 | */ | ||
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 | } | 19 | } |
74 | } | 20 | } |
75 | 21 | ||
76 | @@ -XXX,XX +XXX,XX @@ void blk_io_limits_disable(BlockBackend *blk) | 22 | +static void bdrv_drv_set_perm_commit(void *opaque) |
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 | +{ | 23 | +{ |
169 | + ThrottleTimers *tt = &tgm->throttle_timers; | 24 | + BlockDriverState *bs = opaque; |
170 | + throttle_timers_attach_aio_context(tt, new_context); | 25 | + uint64_t cumulative_perms, cumulative_shared_perms; |
171 | + tgm->aio_context = new_context; | 26 | + |
27 | + if (bs->drv->bdrv_set_perm) { | ||
28 | + bdrv_get_cumulative_perm(bs, &cumulative_perms, | ||
29 | + &cumulative_shared_perms); | ||
30 | + bs->drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms); | ||
31 | + } | ||
172 | +} | 32 | +} |
173 | + | 33 | + |
174 | +void throttle_group_detach_aio_context(ThrottleGroupMember *tgm) | 34 | +static void bdrv_drv_set_perm_abort(void *opaque) |
175 | +{ | 35 | +{ |
176 | + ThrottleTimers *tt = &tgm->throttle_timers; | 36 | + BlockDriverState *bs = opaque; |
177 | + throttle_timers_detach_aio_context(tt); | 37 | + |
178 | + tgm->aio_context = NULL; | 38 | + if (bs->drv->bdrv_abort_perm_update) { |
39 | + bs->drv->bdrv_abort_perm_update(bs); | ||
40 | + } | ||
179 | +} | 41 | +} |
180 | + | 42 | + |
181 | static void throttle_groups_init(void) | 43 | +TransactionActionDrv bdrv_drv_set_perm_drv = { |
44 | + .abort = bdrv_drv_set_perm_abort, | ||
45 | + .commit = bdrv_drv_set_perm_commit, | ||
46 | +}; | ||
47 | + | ||
48 | +static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, | ||
49 | + uint64_t shared_perm, Transaction *tran, | ||
50 | + Error **errp) | ||
51 | +{ | ||
52 | + if (!bs->drv) { | ||
53 | + return 0; | ||
54 | + } | ||
55 | + | ||
56 | + if (bs->drv->bdrv_check_perm) { | ||
57 | + int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp); | ||
58 | + if (ret < 0) { | ||
59 | + return ret; | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + if (tran) { | ||
64 | + tran_add(tran, &bdrv_drv_set_perm_drv, bs); | ||
65 | + } | ||
66 | + | ||
67 | + return 0; | ||
68 | +} | ||
69 | + | ||
70 | /* | ||
71 | * Check whether permissions on this node can be changed in a way that | ||
72 | * @cumulative_perms and @cumulative_shared_perms are the new cumulative | ||
73 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | - if (drv->bdrv_check_perm) { | ||
78 | - ret = drv->bdrv_check_perm(bs, cumulative_perms, | ||
79 | - cumulative_shared_perms, errp); | ||
80 | - if (ret < 0) { | ||
81 | - return ret; | ||
82 | - } | ||
83 | + ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL, | ||
84 | + errp); | ||
85 | + if (ret < 0) { | ||
86 | + return ret; | ||
87 | } | ||
88 | |||
89 | /* Drivers that never have children can omit .bdrv_child_perm() */ | ||
90 | @@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs) | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | - if (drv->bdrv_abort_perm_update) { | ||
95 | - drv->bdrv_abort_perm_update(bs); | ||
96 | - } | ||
97 | + bdrv_drv_set_perm_abort(bs); | ||
98 | |||
99 | QLIST_FOREACH(c, &bs->children, next) { | ||
100 | bdrv_child_set_perm_abort(c); | ||
101 | @@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
102 | |||
103 | static void bdrv_node_set_perm(BlockDriverState *bs) | ||
182 | { | 104 | { |
183 | qemu_mutex_init(&throttle_groups_lock); | 105 | - uint64_t cumulative_perms, cumulative_shared_perms; |
184 | diff --git a/tests/test-throttle.c b/tests/test-throttle.c | 106 | BlockDriver *drv = bs->drv; |
185 | index XXXXXXX..XXXXXXX 100644 | 107 | BdrvChild *c; |
186 | --- a/tests/test-throttle.c | 108 | |
187 | +++ b/tests/test-throttle.c | 109 | @@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs) |
188 | @@ -XXX,XX +XXX,XX @@ | 110 | return; |
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 | } | 111 | } |
229 | 112 | ||
230 | - throttle_timers_destroy(&tt); | 113 | - bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms); |
231 | + throttle_timers_destroy(tt); | 114 | - |
232 | } | 115 | - /* Update this node */ |
233 | 116 | - if (drv->bdrv_set_perm) { | |
234 | static void test_destroy(void) | 117 | - drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms); |
235 | { | 118 | - } |
236 | int i; | 119 | + bdrv_drv_set_perm_commit(bs); |
237 | throttle_init(&ts); | 120 | |
238 | - throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, | 121 | /* Drivers that never have children can omit .bdrv_child_perm() */ |
239 | + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, | 122 | if (!drv->bdrv_child_perm) { |
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 | -- | 123 | -- |
358 | 2.13.5 | 124 | 2.30.2 |
359 | 125 | ||
360 | 126 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | |
2 | |||
3 | Add new interface, allowing use of existing node list. It will be used | ||
4 | to fix bdrv_replace_node() in the further commit. | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20210428151804.439460-16-vsementsov@virtuozzo.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | block.c | 106 +++++++++++++++++++++++++++++++++++++------------------- | ||
12 | 1 file changed, 71 insertions(+), 35 deletions(-) | ||
13 | |||
14 | diff --git a/block.c b/block.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block.c | ||
17 | +++ b/block.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, | ||
19 | static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
20 | uint64_t cumulative_perms, | ||
21 | uint64_t cumulative_shared_perms, | ||
22 | - GSList *ignore_children, Error **errp) | ||
23 | + GSList *ignore_children, | ||
24 | + Transaction *tran, Error **errp) | ||
25 | { | ||
26 | BlockDriver *drv = bs->drv; | ||
27 | BdrvChild *c; | ||
28 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
29 | return 0; | ||
30 | } | ||
31 | |||
32 | - ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL, | ||
33 | + ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, tran, | ||
34 | errp); | ||
35 | if (ret < 0) { | ||
36 | return ret; | ||
37 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
38 | bdrv_child_perm(bs, c->bs, c, c->role, q, | ||
39 | cumulative_perms, cumulative_shared_perms, | ||
40 | &cur_perm, &cur_shared); | ||
41 | - bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL); | ||
42 | + bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran); | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | -static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
49 | - uint64_t cumulative_perms, | ||
50 | - uint64_t cumulative_shared_perms, | ||
51 | - GSList *ignore_children, Error **errp) | ||
52 | +/* | ||
53 | + * If use_cumulative_perms is true, use cumulative_perms and | ||
54 | + * cumulative_shared_perms for first element of the list. Otherwise just refresh | ||
55 | + * all permissions. | ||
56 | + */ | ||
57 | +static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
58 | + bool use_cumulative_perms, | ||
59 | + uint64_t cumulative_perms, | ||
60 | + uint64_t cumulative_shared_perms, | ||
61 | + GSList *ignore_children, | ||
62 | + Transaction *tran, Error **errp) | ||
63 | { | ||
64 | int ret; | ||
65 | - BlockDriverState *root = bs; | ||
66 | - g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root); | ||
67 | + BlockDriverState *bs; | ||
68 | |||
69 | - for ( ; list; list = list->next) { | ||
70 | + if (use_cumulative_perms) { | ||
71 | bs = list->data; | ||
72 | |||
73 | - if (bs != root) { | ||
74 | - if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) { | ||
75 | - return -EINVAL; | ||
76 | - } | ||
77 | + ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
78 | + cumulative_shared_perms, | ||
79 | + ignore_children, tran, errp); | ||
80 | + if (ret < 0) { | ||
81 | + return ret; | ||
82 | + } | ||
83 | |||
84 | - bdrv_get_cumulative_perm(bs, &cumulative_perms, | ||
85 | - &cumulative_shared_perms); | ||
86 | + list = list->next; | ||
87 | + } | ||
88 | + | ||
89 | + for ( ; list; list = list->next) { | ||
90 | + bs = list->data; | ||
91 | + | ||
92 | + if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) { | ||
93 | + return -EINVAL; | ||
94 | } | ||
95 | |||
96 | + bdrv_get_cumulative_perm(bs, &cumulative_perms, | ||
97 | + &cumulative_shared_perms); | ||
98 | + | ||
99 | ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
100 | cumulative_shared_perms, | ||
101 | - ignore_children, errp); | ||
102 | + ignore_children, tran, errp); | ||
103 | if (ret < 0) { | ||
104 | return ret; | ||
105 | } | ||
106 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | +static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
111 | + uint64_t cumulative_perms, | ||
112 | + uint64_t cumulative_shared_perms, | ||
113 | + GSList *ignore_children, Error **errp) | ||
114 | +{ | ||
115 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
116 | + return bdrv_check_perm_common(list, q, true, cumulative_perms, | ||
117 | + cumulative_shared_perms, ignore_children, | ||
118 | + NULL, errp); | ||
119 | +} | ||
120 | + | ||
121 | +static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
122 | + Transaction *tran, Error **errp) | ||
123 | +{ | ||
124 | + return bdrv_check_perm_common(list, q, false, 0, 0, NULL, tran, errp); | ||
125 | +} | ||
126 | + | ||
127 | /* | ||
128 | * Notifies drivers that after a previous bdrv_check_perm() call, the | ||
129 | * permission update is not performed and any preparations made for it (e.g. | ||
130 | @@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs) | ||
131 | } | ||
132 | } | ||
133 | |||
134 | -static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
135 | +static void bdrv_list_abort_perm_update(GSList *list) | ||
136 | { | ||
137 | - g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
138 | - | ||
139 | for ( ; list; list = list->next) { | ||
140 | bdrv_node_abort_perm_update((BlockDriverState *)list->data); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | +static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
145 | +{ | ||
146 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
147 | + return bdrv_list_abort_perm_update(list); | ||
148 | +} | ||
149 | + | ||
150 | static void bdrv_node_set_perm(BlockDriverState *bs) | ||
151 | { | ||
152 | BlockDriver *drv = bs->drv; | ||
153 | @@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs) | ||
154 | } | ||
155 | } | ||
156 | |||
157 | -static void bdrv_set_perm(BlockDriverState *bs) | ||
158 | +static void bdrv_list_set_perm(GSList *list) | ||
159 | { | ||
160 | - g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
161 | - | ||
162 | for ( ; list; list = list->next) { | ||
163 | bdrv_node_set_perm((BlockDriverState *)list->data); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | +static void bdrv_set_perm(BlockDriverState *bs) | ||
168 | +{ | ||
169 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
170 | + return bdrv_list_set_perm(list); | ||
171 | +} | ||
172 | + | ||
173 | void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
174 | uint64_t *shared_perm) | ||
175 | { | ||
176 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
177 | static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) | ||
178 | { | ||
179 | int ret; | ||
180 | - uint64_t perm, shared_perm; | ||
181 | + Transaction *tran = tran_new(); | ||
182 | + g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
183 | |||
184 | - if (bdrv_parent_perms_conflict(bs, NULL, errp)) { | ||
185 | - return -EPERM; | ||
186 | - } | ||
187 | - bdrv_get_cumulative_perm(bs, &perm, &shared_perm); | ||
188 | - ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp); | ||
189 | - if (ret < 0) { | ||
190 | - bdrv_abort_perm_update(bs); | ||
191 | - return ret; | ||
192 | - } | ||
193 | - bdrv_set_perm(bs); | ||
194 | + ret = bdrv_list_refresh_perms(list, NULL, tran, errp); | ||
195 | + tran_finalize(tran, ret); | ||
196 | |||
197 | - return 0; | ||
198 | + return ret; | ||
199 | } | ||
200 | |||
201 | int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||
202 | -- | ||
203 | 2.30.2 | ||
204 | |||
205 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | To be used in the following commit. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Message-Id: <20210428151804.439460-17-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | block.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
11 | 1 file changed, 54 insertions(+) | ||
12 | |||
13 | diff --git a/block.c b/block.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||
18 | BdrvChildRole child_role, | ||
19 | Error **errp); | ||
20 | |||
21 | +static void bdrv_replace_child_noperm(BdrvChild *child, | ||
22 | + BlockDriverState *new_bs); | ||
23 | + | ||
24 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue | ||
25 | *queue, Error **errp); | ||
26 | static void bdrv_reopen_commit(BDRVReopenState *reopen_state); | ||
27 | @@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, | ||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | +typedef struct BdrvReplaceChildState { | ||
32 | + BdrvChild *child; | ||
33 | + BlockDriverState *old_bs; | ||
34 | +} BdrvReplaceChildState; | ||
35 | + | ||
36 | +static void bdrv_replace_child_commit(void *opaque) | ||
37 | +{ | ||
38 | + BdrvReplaceChildState *s = opaque; | ||
39 | + | ||
40 | + bdrv_unref(s->old_bs); | ||
41 | +} | ||
42 | + | ||
43 | +static void bdrv_replace_child_abort(void *opaque) | ||
44 | +{ | ||
45 | + BdrvReplaceChildState *s = opaque; | ||
46 | + BlockDriverState *new_bs = s->child->bs; | ||
47 | + | ||
48 | + /* old_bs reference is transparently moved from @s to @s->child */ | ||
49 | + bdrv_replace_child_noperm(s->child, s->old_bs); | ||
50 | + bdrv_unref(new_bs); | ||
51 | +} | ||
52 | + | ||
53 | +static TransactionActionDrv bdrv_replace_child_drv = { | ||
54 | + .commit = bdrv_replace_child_commit, | ||
55 | + .abort = bdrv_replace_child_abort, | ||
56 | + .clean = g_free, | ||
57 | +}; | ||
58 | + | ||
59 | +/* | ||
60 | + * bdrv_replace_child_safe | ||
61 | + * | ||
62 | + * Note: real unref of old_bs is done only on commit. | ||
63 | + */ | ||
64 | +__attribute__((unused)) | ||
65 | +static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs, | ||
66 | + Transaction *tran) | ||
67 | +{ | ||
68 | + BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); | ||
69 | + *s = (BdrvReplaceChildState) { | ||
70 | + .child = child, | ||
71 | + .old_bs = child->bs, | ||
72 | + }; | ||
73 | + tran_add(tran, &bdrv_replace_child_drv, s); | ||
74 | + | ||
75 | + if (new_bs) { | ||
76 | + bdrv_ref(new_bs); | ||
77 | + } | ||
78 | + bdrv_replace_child_noperm(child, new_bs); | ||
79 | + /* old_bs reference is transparently moved from @child to @s */ | ||
80 | +} | ||
81 | + | ||
82 | /* | ||
83 | * Check whether permissions on this node can be changed in a way that | ||
84 | * @cumulative_perms and @cumulative_shared_perms are the new cumulative | ||
85 | -- | ||
86 | 2.30.2 | ||
87 | |||
88 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | inore_children thing doesn't help to track all propagated permissions | ||
4 | of children we want to ignore. The simplest way to correctly update | ||
5 | permissions is update graph first and then do permission update. In | ||
6 | this case we just referesh permissions for the whole subgraph (in | ||
7 | topological-sort defined order) and everything is correctly calculated | ||
8 | automatically without any ignore_children. | ||
9 | |||
10 | So, refactor bdrv_replace_node_common to first do graph update and then | ||
11 | refresh the permissions. | ||
12 | |||
13 | Test test_parallel_exclusive_write() now pass, so move it out of | ||
14 | debugging "if". | ||
15 | |||
16 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
17 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
18 | Message-Id: <20210428151804.439460-18-vsementsov@virtuozzo.com> | ||
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
20 | --- | ||
21 | block.c | 43 +++++++++++++------------------- | ||
22 | tests/unit/test-bdrv-graph-mod.c | 4 +-- | ||
23 | 2 files changed, 20 insertions(+), 27 deletions(-) | ||
24 | |||
25 | diff --git a/block.c b/block.c | ||
26 | index XXXXXXX..XXXXXXX 100644 | ||
27 | --- a/block.c | ||
28 | +++ b/block.c | ||
29 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = { | ||
30 | * | ||
31 | * Note: real unref of old_bs is done only on commit. | ||
32 | */ | ||
33 | -__attribute__((unused)) | ||
34 | static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs, | ||
35 | Transaction *tran) | ||
36 | { | ||
37 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
38 | bool auto_skip, Error **errp) | ||
39 | { | ||
40 | BdrvChild *c, *next; | ||
41 | - GSList *list = NULL, *p; | ||
42 | - uint64_t perm = 0, shared = BLK_PERM_ALL; | ||
43 | + Transaction *tran = tran_new(); | ||
44 | + g_autoptr(GHashTable) found = NULL; | ||
45 | + g_autoptr(GSList) refresh_list = NULL; | ||
46 | int ret; | ||
47 | |||
48 | /* Make sure that @from doesn't go away until we have successfully attached | ||
49 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
50 | assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); | ||
51 | bdrv_drained_begin(from); | ||
52 | |||
53 | - /* Put all parents into @list and calculate their cumulative permissions */ | ||
54 | + /* | ||
55 | + * Do the replacement without permission update. | ||
56 | + * Replacement may influence the permissions, we should calculate new | ||
57 | + * permissions based on new graph. If we fail, we'll roll-back the | ||
58 | + * replacement. | ||
59 | + */ | ||
60 | QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||
61 | assert(c->bs == from); | ||
62 | if (!should_update_child(c, to)) { | ||
63 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
64 | c->name, from->node_name); | ||
65 | goto out; | ||
66 | } | ||
67 | - list = g_slist_prepend(list, c); | ||
68 | - perm |= c->perm; | ||
69 | - shared &= c->shared_perm; | ||
70 | + bdrv_replace_child_safe(c, to, tran); | ||
71 | } | ||
72 | |||
73 | - /* Check whether the required permissions can be granted on @to, ignoring | ||
74 | - * all BdrvChild in @list so that they can't block themselves. */ | ||
75 | - ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp); | ||
76 | - if (ret < 0) { | ||
77 | - bdrv_abort_perm_update(to); | ||
78 | - goto out; | ||
79 | - } | ||
80 | + found = g_hash_table_new(NULL, NULL); | ||
81 | |||
82 | - /* Now actually perform the change. We performed the permission check for | ||
83 | - * all elements of @list at once, so set the permissions all at once at the | ||
84 | - * very end. */ | ||
85 | - for (p = list; p != NULL; p = p->next) { | ||
86 | - c = p->data; | ||
87 | + refresh_list = bdrv_topological_dfs(refresh_list, found, to); | ||
88 | + refresh_list = bdrv_topological_dfs(refresh_list, found, from); | ||
89 | |||
90 | - bdrv_ref(to); | ||
91 | - bdrv_replace_child_noperm(c, to); | ||
92 | - bdrv_unref(from); | ||
93 | + ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); | ||
94 | + if (ret < 0) { | ||
95 | + goto out; | ||
96 | } | ||
97 | |||
98 | - bdrv_set_perm(to); | ||
99 | - | ||
100 | ret = 0; | ||
101 | |||
102 | out: | ||
103 | - g_slist_free(list); | ||
104 | + tran_finalize(tran, ret); | ||
105 | + | ||
106 | bdrv_drained_end(from); | ||
107 | bdrv_unref(from); | ||
108 | |||
109 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
110 | index XXXXXXX..XXXXXXX 100644 | ||
111 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
112 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
113 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) | ||
114 | test_should_update_child); | ||
115 | g_test_add_func("/bdrv-graph-mod/parallel-perm-update", | ||
116 | test_parallel_perm_update); | ||
117 | + g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write", | ||
118 | + test_parallel_exclusive_write); | ||
119 | |||
120 | if (debug) { | ||
121 | - g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write", | ||
122 | - test_parallel_exclusive_write); | ||
123 | g_test_add_func("/bdrv-graph-mod/append-greedy-filter", | ||
124 | test_append_greedy_filter); | ||
125 | } | ||
126 | -- | ||
127 | 2.30.2 | ||
128 | |||
129 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | |
2 | |||
3 | Split out no-perm part of bdrv_root_attach_child() into separate | ||
4 | transaction action. bdrv_root_attach_child() now moves to new | ||
5 | permission update paradigm: first update graph relations then update | ||
6 | permissions. | ||
7 | |||
8 | qsd-jobs test output updated. Seems now permission update goes in | ||
9 | another order. Still, the test comment say that we only want to check | ||
10 | that command doesn't crash, and it's still so. | ||
11 | |||
12 | Error message is a bit misleading as it looks like job was added first. | ||
13 | But actually in new paradigm of graph update we can't distinguish such | ||
14 | things. We should update the error message, but let's not do it now. | ||
15 | |||
16 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
17 | Message-Id: <20210428151804.439460-19-vsementsov@virtuozzo.com> | ||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | --- | ||
20 | block.c | 190 ++++++++++++++++++-------- | ||
21 | tests/qemu-iotests/tests/qsd-jobs.out | 2 +- | ||
22 | 2 files changed, 137 insertions(+), 55 deletions(-) | ||
23 | |||
24 | diff --git a/block.c b/block.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/block.c | ||
27 | +++ b/block.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | -/* | ||
33 | - * This function steals the reference to child_bs from the caller. | ||
34 | - * That reference is later dropped by bdrv_root_unref_child(). | ||
35 | - * | ||
36 | - * On failure NULL is returned, errp is set and the reference to | ||
37 | - * child_bs is also dropped. | ||
38 | - * | ||
39 | - * The caller must hold the AioContext lock @child_bs, but not that of @ctx | ||
40 | - * (unless @child_bs is already in @ctx). | ||
41 | - */ | ||
42 | -BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
43 | - const char *child_name, | ||
44 | - const BdrvChildClass *child_class, | ||
45 | - BdrvChildRole child_role, | ||
46 | - uint64_t perm, uint64_t shared_perm, | ||
47 | - void *opaque, Error **errp) | ||
48 | +static void bdrv_remove_empty_child(BdrvChild *child) | ||
49 | { | ||
50 | - BdrvChild *child; | ||
51 | - Error *local_err = NULL; | ||
52 | - int ret; | ||
53 | - AioContext *ctx; | ||
54 | + assert(!child->bs); | ||
55 | + QLIST_SAFE_REMOVE(child, next); | ||
56 | + g_free(child->name); | ||
57 | + g_free(child); | ||
58 | +} | ||
59 | |||
60 | - ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp); | ||
61 | - if (ret < 0) { | ||
62 | - bdrv_abort_perm_update(child_bs); | ||
63 | - bdrv_unref(child_bs); | ||
64 | - return NULL; | ||
65 | +typedef struct BdrvAttachChildCommonState { | ||
66 | + BdrvChild **child; | ||
67 | + AioContext *old_parent_ctx; | ||
68 | + AioContext *old_child_ctx; | ||
69 | +} BdrvAttachChildCommonState; | ||
70 | + | ||
71 | +static void bdrv_attach_child_common_abort(void *opaque) | ||
72 | +{ | ||
73 | + BdrvAttachChildCommonState *s = opaque; | ||
74 | + BdrvChild *child = *s->child; | ||
75 | + BlockDriverState *bs = child->bs; | ||
76 | + | ||
77 | + bdrv_replace_child_noperm(child, NULL); | ||
78 | + | ||
79 | + if (bdrv_get_aio_context(bs) != s->old_child_ctx) { | ||
80 | + bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort); | ||
81 | } | ||
82 | |||
83 | - child = g_new(BdrvChild, 1); | ||
84 | - *child = (BdrvChild) { | ||
85 | + if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) { | ||
86 | + GSList *ignore = g_slist_prepend(NULL, child); | ||
87 | + | ||
88 | + child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore, | ||
89 | + &error_abort); | ||
90 | + g_slist_free(ignore); | ||
91 | + ignore = g_slist_prepend(NULL, child); | ||
92 | + child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore); | ||
93 | + | ||
94 | + g_slist_free(ignore); | ||
95 | + } | ||
96 | + | ||
97 | + bdrv_unref(bs); | ||
98 | + bdrv_remove_empty_child(child); | ||
99 | + *s->child = NULL; | ||
100 | +} | ||
101 | + | ||
102 | +static TransactionActionDrv bdrv_attach_child_common_drv = { | ||
103 | + .abort = bdrv_attach_child_common_abort, | ||
104 | + .clean = g_free, | ||
105 | +}; | ||
106 | + | ||
107 | +/* | ||
108 | + * Common part of attaching bdrv child to bs or to blk or to job | ||
109 | + */ | ||
110 | +static int bdrv_attach_child_common(BlockDriverState *child_bs, | ||
111 | + const char *child_name, | ||
112 | + const BdrvChildClass *child_class, | ||
113 | + BdrvChildRole child_role, | ||
114 | + uint64_t perm, uint64_t shared_perm, | ||
115 | + void *opaque, BdrvChild **child, | ||
116 | + Transaction *tran, Error **errp) | ||
117 | +{ | ||
118 | + BdrvChild *new_child; | ||
119 | + AioContext *parent_ctx; | ||
120 | + AioContext *child_ctx = bdrv_get_aio_context(child_bs); | ||
121 | + | ||
122 | + assert(child); | ||
123 | + assert(*child == NULL); | ||
124 | + | ||
125 | + new_child = g_new(BdrvChild, 1); | ||
126 | + *new_child = (BdrvChild) { | ||
127 | .bs = NULL, | ||
128 | .name = g_strdup(child_name), | ||
129 | .klass = child_class, | ||
130 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
131 | .opaque = opaque, | ||
132 | }; | ||
133 | |||
134 | - ctx = bdrv_child_get_parent_aio_context(child); | ||
135 | - | ||
136 | - /* If the AioContexts don't match, first try to move the subtree of | ||
137 | + /* | ||
138 | + * If the AioContexts don't match, first try to move the subtree of | ||
139 | * child_bs into the AioContext of the new parent. If this doesn't work, | ||
140 | - * try moving the parent into the AioContext of child_bs instead. */ | ||
141 | - if (bdrv_get_aio_context(child_bs) != ctx) { | ||
142 | - ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err); | ||
143 | + * try moving the parent into the AioContext of child_bs instead. | ||
144 | + */ | ||
145 | + parent_ctx = bdrv_child_get_parent_aio_context(new_child); | ||
146 | + if (child_ctx != parent_ctx) { | ||
147 | + Error *local_err = NULL; | ||
148 | + int ret = bdrv_try_set_aio_context(child_bs, parent_ctx, &local_err); | ||
149 | + | ||
150 | if (ret < 0 && child_class->can_set_aio_ctx) { | ||
151 | - GSList *ignore = g_slist_prepend(NULL, child); | ||
152 | - ctx = bdrv_get_aio_context(child_bs); | ||
153 | - if (child_class->can_set_aio_ctx(child, ctx, &ignore, NULL)) { | ||
154 | + GSList *ignore = g_slist_prepend(NULL, new_child); | ||
155 | + if (child_class->can_set_aio_ctx(new_child, child_ctx, &ignore, | ||
156 | + NULL)) | ||
157 | + { | ||
158 | error_free(local_err); | ||
159 | ret = 0; | ||
160 | g_slist_free(ignore); | ||
161 | - ignore = g_slist_prepend(NULL, child); | ||
162 | - child_class->set_aio_ctx(child, ctx, &ignore); | ||
163 | + ignore = g_slist_prepend(NULL, new_child); | ||
164 | + child_class->set_aio_ctx(new_child, child_ctx, &ignore); | ||
165 | } | ||
166 | g_slist_free(ignore); | ||
167 | } | ||
168 | + | ||
169 | if (ret < 0) { | ||
170 | error_propagate(errp, local_err); | ||
171 | - g_free(child); | ||
172 | - bdrv_abort_perm_update(child_bs); | ||
173 | - bdrv_unref(child_bs); | ||
174 | - return NULL; | ||
175 | + bdrv_remove_empty_child(new_child); | ||
176 | + return ret; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | - /* This performs the matching bdrv_set_perm() for the above check. */ | ||
181 | - bdrv_replace_child(child, child_bs); | ||
182 | + bdrv_ref(child_bs); | ||
183 | + bdrv_replace_child_noperm(new_child, child_bs); | ||
184 | |||
185 | + *child = new_child; | ||
186 | + | ||
187 | + BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1); | ||
188 | + *s = (BdrvAttachChildCommonState) { | ||
189 | + .child = child, | ||
190 | + .old_parent_ctx = parent_ctx, | ||
191 | + .old_child_ctx = child_ctx, | ||
192 | + }; | ||
193 | + tran_add(tran, &bdrv_attach_child_common_drv, s); | ||
194 | + | ||
195 | + return 0; | ||
196 | +} | ||
197 | + | ||
198 | +static void bdrv_detach_child(BdrvChild *child) | ||
199 | +{ | ||
200 | + bdrv_replace_child(child, NULL); | ||
201 | + bdrv_remove_empty_child(child); | ||
202 | +} | ||
203 | + | ||
204 | +/* | ||
205 | + * This function steals the reference to child_bs from the caller. | ||
206 | + * That reference is later dropped by bdrv_root_unref_child(). | ||
207 | + * | ||
208 | + * On failure NULL is returned, errp is set and the reference to | ||
209 | + * child_bs is also dropped. | ||
210 | + * | ||
211 | + * The caller must hold the AioContext lock @child_bs, but not that of @ctx | ||
212 | + * (unless @child_bs is already in @ctx). | ||
213 | + */ | ||
214 | +BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
215 | + const char *child_name, | ||
216 | + const BdrvChildClass *child_class, | ||
217 | + BdrvChildRole child_role, | ||
218 | + uint64_t perm, uint64_t shared_perm, | ||
219 | + void *opaque, Error **errp) | ||
220 | +{ | ||
221 | + int ret; | ||
222 | + BdrvChild *child = NULL; | ||
223 | + Transaction *tran = tran_new(); | ||
224 | + | ||
225 | + ret = bdrv_attach_child_common(child_bs, child_name, child_class, | ||
226 | + child_role, perm, shared_perm, opaque, | ||
227 | + &child, tran, errp); | ||
228 | + if (ret < 0) { | ||
229 | + bdrv_unref(child_bs); | ||
230 | + return NULL; | ||
231 | + } | ||
232 | + | ||
233 | + ret = bdrv_refresh_perms(child_bs, errp); | ||
234 | + tran_finalize(tran, ret); | ||
235 | + | ||
236 | + bdrv_unref(child_bs); | ||
237 | return child; | ||
238 | } | ||
239 | |||
240 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
241 | return child; | ||
242 | } | ||
243 | |||
244 | -static void bdrv_detach_child(BdrvChild *child) | ||
245 | -{ | ||
246 | - QLIST_SAFE_REMOVE(child, next); | ||
247 | - | ||
248 | - bdrv_replace_child(child, NULL); | ||
249 | - | ||
250 | - g_free(child->name); | ||
251 | - g_free(child); | ||
252 | -} | ||
253 | - | ||
254 | /* Callers must ensure that child->frozen is false. */ | ||
255 | void bdrv_root_unref_child(BdrvChild *child) | ||
256 | { | ||
257 | diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out | ||
258 | index XXXXXXX..XXXXXXX 100644 | ||
259 | --- a/tests/qemu-iotests/tests/qsd-jobs.out | ||
260 | +++ b/tests/qemu-iotests/tests/qsd-jobs.out | ||
261 | @@ -XXX,XX +XXX,XX @@ QMP_VERSION | ||
262 | {"return": {}} | ||
263 | {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} | ||
264 | {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} | ||
265 | -{"error": {"class": "GenericError", "desc": "Conflicts with use by a block device as 'root', which uses 'write' on fmt_base"}} | ||
266 | +{"error": {"class": "GenericError", "desc": "Conflicts with use by stream job 'job0' as 'intermediate node', which does not allow 'write' on fmt_base"}} | ||
267 | {"return": {}} | ||
268 | {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}} | ||
269 | *** done | ||
270 | -- | ||
271 | 2.30.2 | ||
272 | |||
273 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | The old signature has an ambiguous meaning for a return of 0: | 3 | Split no-perm part of bdrv_attach_child as separate transaction action. |
4 | either no allocation was requested or necessary, or an error | 4 | It will be used in later commits. |
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 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
9 | signature to have a separate return value that cleanly | 7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
10 | distinguishes between failure and success, along with a | 8 | Message-Id: <20210428151804.439460-20-vsementsov@virtuozzo.com> |
11 | parameter that cleanly holds a 64-bit value. Then update all | ||
12 | callers. | ||
13 | |||
14 | While auditing that all return paths return a negative errno | ||
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 | |||
18 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
21 | --- | 10 | --- |
22 | block/qcow.c | 123 +++++++++++++++++++++++++++++++++++------------------------ | 11 | block.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++----------- |
23 | 1 file changed, 73 insertions(+), 50 deletions(-) | 12 | 1 file changed, 58 insertions(+), 13 deletions(-) |
24 | 13 | ||
25 | diff --git a/block/qcow.c b/block/qcow.c | 14 | diff --git a/block.c b/block.c |
26 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/block/qcow.c | 16 | --- a/block.c |
28 | +++ b/block/qcow.c | 17 | +++ b/block.c |
29 | @@ -XXX,XX +XXX,XX @@ static int qcow_reopen_prepare(BDRVReopenState *state, | 18 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, |
30 | * 'compressed_size'. 'compressed_size' must be > 0 and < | 19 | |
31 | * cluster_size | 20 | static void bdrv_replace_child_noperm(BdrvChild *child, |
32 | * | 21 | BlockDriverState *new_bs); |
33 | - * return 0 if not allocated. | 22 | +static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, |
34 | + * return 0 if not allocated, 1 if *result is assigned, and negative | 23 | + BlockDriverState *child_bs, |
35 | + * errno on failure. | 24 | + const char *child_name, |
36 | */ | 25 | + const BdrvChildClass *child_class, |
37 | -static uint64_t get_cluster_offset(BlockDriverState *bs, | 26 | + BdrvChildRole child_role, |
38 | - uint64_t offset, int allocate, | 27 | + BdrvChild **child, |
39 | - int compressed_size, | 28 | + Transaction *tran, |
40 | - int n_start, int n_end) | 29 | + Error **errp); |
41 | +static int get_cluster_offset(BlockDriverState *bs, | 30 | |
42 | + uint64_t offset, int allocate, | 31 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue |
43 | + int compressed_size, | 32 | *queue, Error **errp); |
44 | + int n_start, int n_end, uint64_t *result) | 33 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, |
45 | { | 34 | return 0; |
46 | BDRVQcowState *s = bs->opaque; | ||
47 | - int min_index, i, j, l1_index, l2_index; | ||
48 | + int min_index, i, j, l1_index, l2_index, ret; | ||
49 | uint64_t l2_offset, *l2_table, cluster_offset, tmp; | ||
50 | uint32_t min_count; | ||
51 | int new_l2_table; | ||
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 | } | ||
73 | for(i = 0; i < L2_CACHE_SIZE; i++) { | ||
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 | } | 35 | } |
162 | 36 | ||
163 | static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | 37 | +static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, |
164 | int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | 38 | + BlockDriverState *child_bs, |
165 | { | 39 | + const char *child_name, |
166 | BDRVQcowState *s = bs->opaque; | 40 | + const BdrvChildClass *child_class, |
167 | - int index_in_cluster, n; | 41 | + BdrvChildRole child_role, |
168 | + int index_in_cluster, n, ret; | 42 | + BdrvChild **child, |
169 | uint64_t cluster_offset; | 43 | + Transaction *tran, |
170 | 44 | + Error **errp) | |
171 | qemu_co_mutex_lock(&s->lock); | 45 | +{ |
172 | - cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); | 46 | + int ret; |
173 | + ret = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0, &cluster_offset); | 47 | + uint64_t perm, shared_perm; |
174 | qemu_co_mutex_unlock(&s->lock); | 48 | + |
49 | + assert(parent_bs->drv); | ||
50 | + | ||
51 | + bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); | ||
52 | + bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, | ||
53 | + perm, shared_perm, &perm, &shared_perm); | ||
54 | + | ||
55 | + ret = bdrv_attach_child_common(child_bs, child_name, child_class, | ||
56 | + child_role, perm, shared_perm, parent_bs, | ||
57 | + child, tran, errp); | ||
175 | + if (ret < 0) { | 58 | + if (ret < 0) { |
176 | + return ret; | 59 | + return ret; |
177 | + } | 60 | + } |
178 | index_in_cluster = sector_num & (s->cluster_sectors - 1); | 61 | + |
179 | n = s->cluster_sectors - index_in_cluster; | 62 | + QLIST_INSERT_HEAD(&parent_bs->children, *child, next); |
180 | if (n > nb_sectors) | 63 | + /* |
181 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | 64 | + * child is removed in bdrv_attach_child_common_abort(), so don't care to |
182 | 65 | + * abort this change separately. | |
183 | while (nb_sectors != 0) { | 66 | + */ |
184 | /* prepare next request */ | 67 | + |
185 | - cluster_offset = get_cluster_offset(bs, sector_num << 9, | 68 | + return 0; |
186 | - 0, 0, 0, 0); | 69 | +} |
187 | + ret = get_cluster_offset(bs, sector_num << 9, | 70 | + |
188 | + 0, 0, 0, 0, &cluster_offset); | 71 | static void bdrv_detach_child(BdrvChild *child) |
189 | + if (ret < 0) { | 72 | { |
190 | + break; | 73 | bdrv_replace_child(child, NULL); |
191 | + } | 74 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, |
192 | index_in_cluster = sector_num & (s->cluster_sectors - 1); | 75 | BdrvChildRole child_role, |
193 | n = s->cluster_sectors - index_in_cluster; | 76 | Error **errp) |
194 | if (n > nb_sectors) { | 77 | { |
195 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | 78 | - BdrvChild *child; |
196 | ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); | 79 | - uint64_t perm, shared_perm; |
197 | qemu_co_mutex_lock(&s->lock); | 80 | - |
198 | if (ret < 0) { | 81 | - bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); |
199 | - goto fail; | 82 | + int ret; |
200 | + break; | 83 | + BdrvChild *child = NULL; |
201 | } | 84 | + Transaction *tran = tran_new(); |
202 | } else { | 85 | |
203 | /* Note: in this case, no need to wait */ | 86 | - assert(parent_bs->drv); |
204 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | 87 | - bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, |
205 | } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | 88 | - perm, shared_perm, &perm, &shared_perm); |
206 | /* add AIO support for compressed blocks ? */ | 89 | + ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class, |
207 | if (decompress_cluster(bs, cluster_offset) < 0) { | 90 | + child_role, &child, tran, errp); |
208 | - goto fail; | 91 | + if (ret < 0) { |
209 | + ret = -EIO; | 92 | + goto out; |
210 | + break; | 93 | + } |
211 | } | 94 | |
212 | memcpy(buf, | 95 | - child = bdrv_root_attach_child(child_bs, child_name, child_class, |
213 | s->cluster_cache + index_in_cluster * 512, 512 * n); | 96 | - child_role, perm, shared_perm, parent_bs, |
214 | } else { | 97 | - errp); |
215 | if ((cluster_offset & 511) != 0) { | 98 | - if (child == NULL) { |
216 | - goto fail; | 99 | - return NULL; |
217 | + ret = -EIO; | 100 | + ret = bdrv_refresh_perms(parent_bs, errp); |
218 | + break; | 101 | + if (ret < 0) { |
219 | } | 102 | + goto out; |
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 | } | 103 | } |
235 | 104 | ||
236 | -done: | 105 | - QLIST_INSERT_HEAD(&parent_bs->children, child, next); |
237 | qemu_co_mutex_unlock(&s->lock); | 106 | +out: |
238 | 107 | + tran_finalize(tran, ret); | |
239 | if (qiov->niov > 1) { | 108 | + |
240 | @@ -XXX,XX +XXX,XX @@ done: | 109 | + bdrv_unref(child_bs); |
241 | } | 110 | + |
242 | 111 | return child; | |
243 | return ret; | ||
244 | - | ||
245 | -fail: | ||
246 | - ret = -EIO; | ||
247 | - goto done; | ||
248 | } | 112 | } |
249 | 113 | ||
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 | -- | 114 | -- |
281 | 2.13.5 | 115 | 2.30.2 |
282 | 116 | ||
283 | 117 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Split part of bdrv_replace_node_common() to be used separately. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Message-Id: <20210428151804.439460-21-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | block.c | 50 +++++++++++++++++++++++++++++++------------------- | ||
11 | 1 file changed, 31 insertions(+), 19 deletions(-) | ||
12 | |||
13 | diff --git a/block.c b/block.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) | ||
18 | return ret; | ||
19 | } | ||
20 | |||
21 | +static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
22 | + BlockDriverState *to, | ||
23 | + bool auto_skip, Transaction *tran, | ||
24 | + Error **errp) | ||
25 | +{ | ||
26 | + BdrvChild *c, *next; | ||
27 | + | ||
28 | + QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||
29 | + assert(c->bs == from); | ||
30 | + if (!should_update_child(c, to)) { | ||
31 | + if (auto_skip) { | ||
32 | + continue; | ||
33 | + } | ||
34 | + error_setg(errp, "Should not change '%s' link to '%s'", | ||
35 | + c->name, from->node_name); | ||
36 | + return -EINVAL; | ||
37 | + } | ||
38 | + if (c->frozen) { | ||
39 | + error_setg(errp, "Cannot change '%s' link to '%s'", | ||
40 | + c->name, from->node_name); | ||
41 | + return -EPERM; | ||
42 | + } | ||
43 | + bdrv_replace_child_safe(c, to, tran); | ||
44 | + } | ||
45 | + | ||
46 | + return 0; | ||
47 | +} | ||
48 | + | ||
49 | /* | ||
50 | * With auto_skip=true bdrv_replace_node_common skips updating from parents | ||
51 | * if it creates a parent-child relation loop or if parent is block-job. | ||
52 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
53 | BlockDriverState *to, | ||
54 | bool auto_skip, Error **errp) | ||
55 | { | ||
56 | - BdrvChild *c, *next; | ||
57 | Transaction *tran = tran_new(); | ||
58 | g_autoptr(GHashTable) found = NULL; | ||
59 | g_autoptr(GSList) refresh_list = NULL; | ||
60 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
61 | * permissions based on new graph. If we fail, we'll roll-back the | ||
62 | * replacement. | ||
63 | */ | ||
64 | - QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||
65 | - assert(c->bs == from); | ||
66 | - if (!should_update_child(c, to)) { | ||
67 | - if (auto_skip) { | ||
68 | - continue; | ||
69 | - } | ||
70 | - ret = -EINVAL; | ||
71 | - error_setg(errp, "Should not change '%s' link to '%s'", | ||
72 | - c->name, from->node_name); | ||
73 | - goto out; | ||
74 | - } | ||
75 | - if (c->frozen) { | ||
76 | - ret = -EPERM; | ||
77 | - error_setg(errp, "Cannot change '%s' link to '%s'", | ||
78 | - c->name, from->node_name); | ||
79 | - goto out; | ||
80 | - } | ||
81 | - bdrv_replace_child_safe(c, to, tran); | ||
82 | + ret = bdrv_replace_node_noperm(from, to, auto_skip, tran, errp); | ||
83 | + if (ret < 0) { | ||
84 | + goto out; | ||
85 | } | ||
86 | |||
87 | found = g_hash_table_new(NULL, NULL); | ||
88 | -- | ||
89 | 2.30.2 | ||
90 | |||
91 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | bdrv_append is not very good for inserting filters: it does extra | ||
4 | permission update as part of bdrv_set_backing_hd(). During this update | ||
5 | filter may conflict with other parents of top_bs. | ||
6 | |||
7 | Instead, let's first do all graph modifications and after it update | ||
8 | permissions. | ||
9 | |||
10 | append-greedy-filter test-case in test-bdrv-graph-mod is now works, so | ||
11 | move it out of debug option. | ||
12 | |||
13 | Note: bdrv_append() is still only works for backing-child based | ||
14 | filters. It's something to improve later. | ||
15 | |||
16 | Note2: we use the fact that bdrv_append() is used to append new nodes, | ||
17 | without backing child, so we don't need frozen check and inherits_from | ||
18 | logic from bdrv_set_backing_hd(). | ||
19 | |||
20 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
21 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
22 | Message-Id: <20210428151804.439460-22-vsementsov@virtuozzo.com> | ||
23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
24 | --- | ||
25 | block.c | 27 ++++++++++++++++++++------- | ||
26 | tests/unit/test-bdrv-graph-mod.c | 17 ++--------------- | ||
27 | 2 files changed, 22 insertions(+), 22 deletions(-) | ||
28 | |||
29 | diff --git a/block.c b/block.c | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/block.c | ||
32 | +++ b/block.c | ||
33 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||
34 | * This will modify the BlockDriverState fields, and swap contents | ||
35 | * between bs_new and bs_top. Both bs_new and bs_top are modified. | ||
36 | * | ||
37 | - * bs_new must not be attached to a BlockBackend. | ||
38 | + * bs_new must not be attached to a BlockBackend and must not have backing | ||
39 | + * child. | ||
40 | * | ||
41 | * This function does not create any image files. | ||
42 | */ | ||
43 | int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
44 | Error **errp) | ||
45 | { | ||
46 | - int ret = bdrv_set_backing_hd(bs_new, bs_top, errp); | ||
47 | + int ret; | ||
48 | + Transaction *tran = tran_new(); | ||
49 | + | ||
50 | + assert(!bs_new->backing); | ||
51 | + | ||
52 | + ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing", | ||
53 | + &child_of_bds, bdrv_backing_role(bs_new), | ||
54 | + &bs_new->backing, tran, errp); | ||
55 | if (ret < 0) { | ||
56 | - return ret; | ||
57 | + goto out; | ||
58 | } | ||
59 | |||
60 | - ret = bdrv_replace_node(bs_top, bs_new, errp); | ||
61 | + ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); | ||
62 | if (ret < 0) { | ||
63 | - bdrv_set_backing_hd(bs_new, NULL, &error_abort); | ||
64 | - return ret; | ||
65 | + goto out; | ||
66 | } | ||
67 | |||
68 | - return 0; | ||
69 | + ret = bdrv_refresh_perms(bs_new, errp); | ||
70 | +out: | ||
71 | + tran_finalize(tran, ret); | ||
72 | + | ||
73 | + bdrv_refresh_limits(bs_top, NULL); | ||
74 | + | ||
75 | + return ret; | ||
76 | } | ||
77 | |||
78 | static void bdrv_delete(BlockDriverState *bs) | ||
79 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
80 | index XXXXXXX..XXXXXXX 100644 | ||
81 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
82 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
83 | @@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void) | ||
84 | |||
85 | int main(int argc, char *argv[]) | ||
86 | { | ||
87 | - int i; | ||
88 | - bool debug = false; | ||
89 | - | ||
90 | - for (i = 1; i < argc; i++) { | ||
91 | - if (!strcmp(argv[i], "-d")) { | ||
92 | - debug = true; | ||
93 | - break; | ||
94 | - } | ||
95 | - } | ||
96 | - | ||
97 | bdrv_init(); | ||
98 | qemu_init_main_loop(&error_abort); | ||
99 | |||
100 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) | ||
101 | test_parallel_perm_update); | ||
102 | g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write", | ||
103 | test_parallel_exclusive_write); | ||
104 | - | ||
105 | - if (debug) { | ||
106 | - g_test_add_func("/bdrv-graph-mod/append-greedy-filter", | ||
107 | - test_append_greedy_filter); | ||
108 | - } | ||
109 | + g_test_add_func("/bdrv-graph-mod/append-greedy-filter", | ||
110 | + test_append_greedy_filter); | ||
111 | |||
112 | return g_test_run(); | ||
113 | } | ||
114 | -- | ||
115 | 2.30.2 | ||
116 | |||
117 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | ThrottleGroup is converted to an object. This will allow the future | 3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
4 | throttle block filter drive easy creation and configuration of throttle | 4 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
5 | groups in QMP and cli. | 5 | Message-Id: <20210428151804.439460-23-vsementsov@virtuozzo.com> |
6 | |||
7 | A new QAPI struct, ThrottleLimits, is introduced to provide a shared | ||
8 | struct for all throttle configuration needs in QMP. | ||
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> | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
51 | --- | 7 | --- |
52 | qapi/block-core.json | 48 +++++ | 8 | block.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
53 | include/block/throttle-groups.h | 3 + | 9 | 1 file changed, 82 insertions(+), 2 deletions(-) |
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 | 10 | ||
61 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 11 | diff --git a/block.c b/block.c |
62 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
63 | --- a/qapi/block-core.json | 13 | --- a/block.c |
64 | +++ b/qapi/block-core.json | 14 | +++ b/block.c |
65 | @@ -XXX,XX +XXX,XX @@ | 15 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) |
66 | '*iops_size': 'int', '*group': 'str' } } | 16 | } |
67 | 17 | } | |
68 | ## | 18 | |
69 | +# @ThrottleLimits: | 19 | +static void bdrv_child_free(void *opaque) |
70 | +# | 20 | +{ |
71 | +# Limit parameters for throttling. | 21 | + BdrvChild *c = opaque; |
72 | +# Since some limit combinations are illegal, limits should always be set in one | ||
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 | + | 22 | + |
116 | +## | 23 | + g_free(c->name); |
117 | # @block-stream: | 24 | + g_free(c); |
118 | # | 25 | +} |
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 | + | 26 | + |
131 | const char *throttle_group_get_name(ThrottleGroupMember *tgm); | 27 | static void bdrv_remove_empty_child(BdrvChild *child) |
132 | 28 | { | |
133 | ThrottleState *throttle_group_incref(const char *name); | 29 | assert(!child->bs); |
134 | diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h | 30 | QLIST_SAFE_REMOVE(child, next); |
135 | index XXXXXXX..XXXXXXX 100644 | 31 | - g_free(child->name); |
136 | --- a/include/qemu/throttle-options.h | 32 | - g_free(child); |
137 | +++ b/include/qemu/throttle-options.h | 33 | + bdrv_child_free(child); |
138 | @@ -XXX,XX +XXX,XX @@ | 34 | } |
139 | #ifndef THROTTLE_OPTIONS_H | 35 | |
140 | #define THROTTLE_OPTIONS_H | 36 | typedef struct BdrvAttachChildCommonState { |
141 | 37 | @@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) | |
142 | +#define QEMU_OPT_IOPS_TOTAL "iops-total" | 38 | return ret; |
143 | +#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max" | 39 | } |
144 | +#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length" | 40 | |
145 | +#define QEMU_OPT_IOPS_READ "iops-read" | 41 | +typedef struct BdrvRemoveFilterOrCowChild { |
146 | +#define QEMU_OPT_IOPS_READ_MAX "iops-read-max" | 42 | + BdrvChild *child; |
147 | +#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length" | 43 | + bool is_backing; |
148 | +#define QEMU_OPT_IOPS_WRITE "iops-write" | 44 | +} BdrvRemoveFilterOrCowChild; |
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 | + | 45 | + |
162 | +#define THROTTLE_OPT_PREFIX "throttling." | 46 | +static void bdrv_remove_filter_or_cow_child_abort(void *opaque) |
163 | #define THROTTLE_OPTS \ | 47 | +{ |
164 | { \ | 48 | + BdrvRemoveFilterOrCowChild *s = opaque; |
165 | - .name = "throttling.iops-total",\ | 49 | + BlockDriverState *parent_bs = s->child->opaque; |
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 | + | 50 | + |
290 | +static void throttle_group_obj_init(Object *obj); | 51 | + QLIST_INSERT_HEAD(&parent_bs->children, s->child, next); |
291 | +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp); | 52 | + if (s->is_backing) { |
292 | 53 | + parent_bs->backing = s->child; | |
293 | /* The ThrottleGroup structure (with its ThrottleState) is shared | 54 | + } else { |
294 | * among different ThrottleGroupMembers and it's independent from | 55 | + parent_bs->file = s->child; |
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 | + } | 56 | + } |
335 | + | 57 | + |
336 | + return NULL; | 58 | + /* |
59 | + * We don't have to restore child->bs here to undo bdrv_replace_child() | ||
60 | + * because that function is transactionable and it registered own completion | ||
61 | + * entries in @tran, so .abort() for bdrv_replace_child_safe() will be | ||
62 | + * called automatically. | ||
63 | + */ | ||
337 | +} | 64 | +} |
338 | + | 65 | + |
339 | /* Increments the reference count of a ThrottleGroup given its name. | 66 | +static void bdrv_remove_filter_or_cow_child_commit(void *opaque) |
340 | * | 67 | +{ |
341 | * If no ThrottleGroup is found with the given name a new one is | 68 | + BdrvRemoveFilterOrCowChild *s = opaque; |
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 | + | 69 | + |
370 | + if (tg) { | 70 | + bdrv_child_free(s->child); |
371 | + object_ref(OBJECT(tg)); | 71 | +} |
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 | + | 72 | + |
440 | +/* Helper struct and array for QOM property setter/getter */ | 73 | +static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = { |
441 | +typedef struct { | 74 | + .abort = bdrv_remove_filter_or_cow_child_abort, |
442 | + const char *name; | 75 | + .commit = bdrv_remove_filter_or_cow_child_commit, |
443 | + BucketType type; | 76 | + .clean = g_free, |
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 | +}; | 77 | +}; |
530 | + | 78 | + |
531 | +/* This function edits throttle_groups and must be called under the global | 79 | +/* |
532 | + * mutex */ | 80 | + * A function to remove backing-chain child of @bs if exists: cow child for |
533 | +static void throttle_group_obj_init(Object *obj) | 81 | + * format nodes (always .backing) and filter child for filters (may be .file or |
82 | + * .backing) | ||
83 | + */ | ||
84 | +__attribute__((unused)) | ||
85 | +static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, | ||
86 | + Transaction *tran) | ||
534 | +{ | 87 | +{ |
535 | + ThrottleGroup *tg = THROTTLE_GROUP(obj); | 88 | + BdrvRemoveFilterOrCowChild *s; |
89 | + BdrvChild *child = bdrv_filter_or_cow_child(bs); | ||
536 | + | 90 | + |
537 | + tg->clock_type = QEMU_CLOCK_REALTIME; | 91 | + if (!child) { |
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; | 92 | + return; |
566 | + } | 93 | + } |
567 | + | 94 | + |
568 | + /* check validity */ | 95 | + if (child->bs) { |
569 | + throttle_get_config(&tg->ts, &cfg); | 96 | + bdrv_replace_child_safe(child, NULL, tran); |
570 | + if (!throttle_is_valid(&cfg, errp)) { | ||
571 | + return; | ||
572 | + } | 97 | + } |
573 | + throttle_config(&tg->ts, tg->clock_type, &cfg); | 98 | + |
574 | + QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); | 99 | + s = g_new(BdrvRemoveFilterOrCowChild, 1); |
575 | + tg->is_initialized = true; | 100 | + *s = (BdrvRemoveFilterOrCowChild) { |
101 | + .child = child, | ||
102 | + .is_backing = (child == bs->backing), | ||
103 | + }; | ||
104 | + tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); | ||
105 | + | ||
106 | + QLIST_SAFE_REMOVE(child, next); | ||
107 | + if (s->is_backing) { | ||
108 | + bs->backing = NULL; | ||
109 | + } else { | ||
110 | + bs->file = NULL; | ||
111 | + } | ||
576 | +} | 112 | +} |
577 | + | 113 | + |
578 | +/* This function edits throttle_groups and must be called under the global | 114 | static int bdrv_replace_node_noperm(BlockDriverState *from, |
579 | + * mutex */ | 115 | BlockDriverState *to, |
580 | +static void throttle_group_obj_finalize(Object *obj) | 116 | bool auto_skip, Transaction *tran, |
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 | -- | 117 | -- |
946 | 2.13.5 | 118 | 2.30.2 |
947 | 119 | ||
948 | 120 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Using bdrv_replace_node() for removing filter is not good enough: it | ||
4 | keeps child reference of the filter, which may conflict with original | ||
5 | top node during permission update. | ||
6 | |||
7 | Instead let's create new interface, which will do all graph | ||
8 | modifications first and then update permissions. | ||
9 | |||
10 | Let's modify bdrv_replace_node_common(), allowing it additionally drop | ||
11 | backing chain child link pointing to new node. This is quite | ||
12 | appropriate for bdrv_drop_intermediate() and makes possible to add | ||
13 | new bdrv_drop_filter() as a simple wrapper. | ||
14 | |||
15 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
16 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
17 | Message-Id: <20210428151804.439460-24-vsementsov@virtuozzo.com> | ||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | --- | ||
20 | include/block/block.h | 1 + | ||
21 | block.c | 43 +++++++++++++++++++++++++++++++++++++++---- | ||
22 | 2 files changed, 40 insertions(+), 4 deletions(-) | ||
23 | |||
24 | diff --git a/include/block/block.h b/include/block/block.h | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/include/block/block.h | ||
27 | +++ b/include/block/block.h | ||
28 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||
29 | Error **errp); | ||
30 | BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, | ||
31 | int flags, Error **errp); | ||
32 | +int bdrv_drop_filter(BlockDriverState *bs, Error **errp); | ||
33 | |||
34 | int bdrv_parse_aio(const char *mode, int *flags); | ||
35 | int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); | ||
36 | diff --git a/block.c b/block.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | ||
38 | --- a/block.c | ||
39 | +++ b/block.c | ||
40 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = { | ||
41 | * format nodes (always .backing) and filter child for filters (may be .file or | ||
42 | * .backing) | ||
43 | */ | ||
44 | -__attribute__((unused)) | ||
45 | static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, | ||
46 | Transaction *tran) | ||
47 | { | ||
48 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
49 | * | ||
50 | * With auto_skip=false the error is returned if from has a parent which should | ||
51 | * not be updated. | ||
52 | + * | ||
53 | + * With @detach_subchain=true @to must be in a backing chain of @from. In this | ||
54 | + * case backing link of the cow-parent of @to is removed. | ||
55 | */ | ||
56 | static int bdrv_replace_node_common(BlockDriverState *from, | ||
57 | BlockDriverState *to, | ||
58 | - bool auto_skip, Error **errp) | ||
59 | + bool auto_skip, bool detach_subchain, | ||
60 | + Error **errp) | ||
61 | { | ||
62 | Transaction *tran = tran_new(); | ||
63 | g_autoptr(GHashTable) found = NULL; | ||
64 | g_autoptr(GSList) refresh_list = NULL; | ||
65 | + BlockDriverState *to_cow_parent; | ||
66 | int ret; | ||
67 | |||
68 | + if (detach_subchain) { | ||
69 | + assert(bdrv_chain_contains(from, to)); | ||
70 | + assert(from != to); | ||
71 | + for (to_cow_parent = from; | ||
72 | + bdrv_filter_or_cow_bs(to_cow_parent) != to; | ||
73 | + to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) | ||
74 | + { | ||
75 | + ; | ||
76 | + } | ||
77 | + } | ||
78 | + | ||
79 | /* Make sure that @from doesn't go away until we have successfully attached | ||
80 | * all of its parents to @to. */ | ||
81 | bdrv_ref(from); | ||
82 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
83 | goto out; | ||
84 | } | ||
85 | |||
86 | + if (detach_subchain) { | ||
87 | + bdrv_remove_filter_or_cow_child(to_cow_parent, tran); | ||
88 | + } | ||
89 | + | ||
90 | found = g_hash_table_new(NULL, NULL); | ||
91 | |||
92 | refresh_list = bdrv_topological_dfs(refresh_list, found, to); | ||
93 | @@ -XXX,XX +XXX,XX @@ out: | ||
94 | int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||
95 | Error **errp) | ||
96 | { | ||
97 | - return bdrv_replace_node_common(from, to, true, errp); | ||
98 | + return bdrv_replace_node_common(from, to, true, false, errp); | ||
99 | +} | ||
100 | + | ||
101 | +int bdrv_drop_filter(BlockDriverState *bs, Error **errp) | ||
102 | +{ | ||
103 | + return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true, | ||
104 | + errp); | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, | ||
109 | updated_children = g_slist_prepend(updated_children, c); | ||
110 | } | ||
111 | |||
112 | - bdrv_replace_node_common(top, base, false, &local_err); | ||
113 | + /* | ||
114 | + * It seems correct to pass detach_subchain=true here, but it triggers | ||
115 | + * one more yet not fixed bug, when due to nested aio_poll loop we switch to | ||
116 | + * another drained section, which modify the graph (for example, removing | ||
117 | + * the child, which we keep in updated_children list). So, it's a TODO. | ||
118 | + * | ||
119 | + * Note, bug triggered if pass detach_subchain=true here and run | ||
120 | + * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash. | ||
121 | + * That's a FIXME. | ||
122 | + */ | ||
123 | + bdrv_replace_node_common(top, base, false, false, &local_err); | ||
124 | if (local_err) { | ||
125 | error_report_err(local_err); | ||
126 | goto exit; | ||
127 | -- | ||
128 | 2.30.2 | ||
129 | |||
130 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | We don't need this workaround anymore: bdrv_append is already smart | ||
4 | enough and we can use new bdrv_drop_filter(). | ||
5 | |||
6 | This commit efficiently reverts also recent 705dde27c6c53b73, which | ||
7 | checked .active on io path. Still it said that the problem should be | ||
8 | theoretical. And the logic of filter removement is changed anyway. | ||
9 | |||
10 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Message-Id: <20210428151804.439460-25-vsementsov@virtuozzo.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | ||
15 | block/backup-top.c | 47 +------------------------------------- | ||
16 | tests/qemu-iotests/283.out | 2 +- | ||
17 | 2 files changed, 2 insertions(+), 47 deletions(-) | ||
18 | |||
19 | diff --git a/block/backup-top.c b/block/backup-top.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/backup-top.c | ||
22 | +++ b/block/backup-top.c | ||
23 | @@ -XXX,XX +XXX,XX @@ | ||
24 | typedef struct BDRVBackupTopState { | ||
25 | BlockCopyState *bcs; | ||
26 | BdrvChild *target; | ||
27 | - bool active; | ||
28 | int64_t cluster_size; | ||
29 | } BDRVBackupTopState; | ||
30 | |||
31 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_co_preadv( | ||
32 | BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||
33 | QEMUIOVector *qiov, int flags) | ||
34 | { | ||
35 | - BDRVBackupTopState *s = bs->opaque; | ||
36 | - | ||
37 | - if (!s->active) { | ||
38 | - return -EIO; | ||
39 | - } | ||
40 | - | ||
41 | return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); | ||
42 | } | ||
43 | |||
44 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, | ||
45 | BDRVBackupTopState *s = bs->opaque; | ||
46 | uint64_t off, end; | ||
47 | |||
48 | - if (!s->active) { | ||
49 | - return -EIO; | ||
50 | - } | ||
51 | - | ||
52 | if (flags & BDRV_REQ_WRITE_UNCHANGED) { | ||
53 | return 0; | ||
54 | } | ||
55 | @@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
56 | uint64_t perm, uint64_t shared, | ||
57 | uint64_t *nperm, uint64_t *nshared) | ||
58 | { | ||
59 | - BDRVBackupTopState *s = bs->opaque; | ||
60 | - | ||
61 | - if (!s->active) { | ||
62 | - /* | ||
63 | - * The filter node may be in process of bdrv_append(), which firstly do | ||
64 | - * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that | ||
65 | - * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So, | ||
66 | - * let's require nothing during bdrv_append() and refresh permissions | ||
67 | - * after it (see bdrv_backup_top_append()). | ||
68 | - */ | ||
69 | - *nperm = 0; | ||
70 | - *nshared = BLK_PERM_ALL; | ||
71 | - return; | ||
72 | - } | ||
73 | - | ||
74 | if (!(role & BDRV_CHILD_FILTERED)) { | ||
75 | /* | ||
76 | * Target child | ||
77 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||
78 | } | ||
79 | appended = true; | ||
80 | |||
81 | - /* | ||
82 | - * bdrv_append() finished successfully, now we can require permissions | ||
83 | - * we want. | ||
84 | - */ | ||
85 | - state->active = true; | ||
86 | - ret = bdrv_child_refresh_perms(top, top->backing, errp); | ||
87 | - if (ret < 0) { | ||
88 | - error_prepend(errp, "Cannot set permissions for backup-top filter: "); | ||
89 | - goto fail; | ||
90 | - } | ||
91 | - | ||
92 | state->cluster_size = cluster_size; | ||
93 | state->bcs = block_copy_state_new(top->backing, state->target, | ||
94 | cluster_size, perf->use_copy_range, | ||
95 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||
96 | |||
97 | fail: | ||
98 | if (appended) { | ||
99 | - state->active = false; | ||
100 | bdrv_backup_top_drop(top); | ||
101 | } else { | ||
102 | bdrv_unref(top); | ||
103 | @@ -XXX,XX +XXX,XX @@ void bdrv_backup_top_drop(BlockDriverState *bs) | ||
104 | { | ||
105 | BDRVBackupTopState *s = bs->opaque; | ||
106 | |||
107 | - bdrv_drained_begin(bs); | ||
108 | + bdrv_drop_filter(bs, &error_abort); | ||
109 | |||
110 | block_copy_state_free(s->bcs); | ||
111 | |||
112 | - s->active = false; | ||
113 | - bdrv_child_refresh_perms(bs, bs->backing, &error_abort); | ||
114 | - bdrv_replace_node(bs, bs->backing->bs, &error_abort); | ||
115 | - bdrv_set_backing_hd(bs, NULL, &error_abort); | ||
116 | - | ||
117 | - bdrv_drained_end(bs); | ||
118 | - | ||
119 | bdrv_unref(bs); | ||
120 | } | ||
121 | diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out | ||
122 | index XXXXXXX..XXXXXXX 100644 | ||
123 | --- a/tests/qemu-iotests/283.out | ||
124 | +++ b/tests/qemu-iotests/283.out | ||
125 | @@ -XXX,XX +XXX,XX @@ | ||
126 | {"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}} | ||
127 | {"return": {}} | ||
128 | {"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}} | ||
129 | -{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}} | ||
130 | +{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}} | ||
131 | |||
132 | === backup-top should be gone after job-finalize === | ||
133 | |||
134 | -- | ||
135 | 2.30.2 | ||
136 | |||
137 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | This argument is always NULL. Drop it. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Message-Id: <20210428151804.439460-26-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | block.c | 38 +++++++++++--------------------------- | ||
11 | 1 file changed, 11 insertions(+), 27 deletions(-) | ||
12 | |||
13 | diff --git a/block.c b/block.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, | ||
18 | static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
19 | uint64_t new_used_perm, | ||
20 | uint64_t new_shared_perm, | ||
21 | - GSList *ignore_children, | ||
22 | Error **errp); | ||
23 | |||
24 | typedef struct BlockReopenQueueEntry { | ||
25 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) | ||
26 | return false; | ||
27 | } | ||
28 | |||
29 | -static bool bdrv_parent_perms_conflict(BlockDriverState *bs, | ||
30 | - GSList *ignore_children, | ||
31 | - Error **errp) | ||
32 | +static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | ||
33 | { | ||
34 | BdrvChild *a, *b; | ||
35 | |||
36 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs, | ||
37 | * directions. | ||
38 | */ | ||
39 | QLIST_FOREACH(a, &bs->parents, next_parent) { | ||
40 | - if (g_slist_find(ignore_children, a)) { | ||
41 | - continue; | ||
42 | - } | ||
43 | - | ||
44 | QLIST_FOREACH(b, &bs->parents, next_parent) { | ||
45 | - if (a == b || g_slist_find(ignore_children, b)) { | ||
46 | + if (a == b) { | ||
47 | continue; | ||
48 | } | ||
49 | |||
50 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs, | ||
51 | static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
52 | uint64_t cumulative_perms, | ||
53 | uint64_t cumulative_shared_perms, | ||
54 | - GSList *ignore_children, | ||
55 | Transaction *tran, Error **errp) | ||
56 | { | ||
57 | BlockDriver *drv = bs->drv; | ||
58 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
59 | bool use_cumulative_perms, | ||
60 | uint64_t cumulative_perms, | ||
61 | uint64_t cumulative_shared_perms, | ||
62 | - GSList *ignore_children, | ||
63 | Transaction *tran, Error **errp) | ||
64 | { | ||
65 | int ret; | ||
66 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
67 | |||
68 | ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
69 | cumulative_shared_perms, | ||
70 | - ignore_children, tran, errp); | ||
71 | + tran, errp); | ||
72 | if (ret < 0) { | ||
73 | return ret; | ||
74 | } | ||
75 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
76 | for ( ; list; list = list->next) { | ||
77 | bs = list->data; | ||
78 | |||
79 | - if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) { | ||
80 | + if (bdrv_parent_perms_conflict(bs, errp)) { | ||
81 | return -EINVAL; | ||
82 | } | ||
83 | |||
84 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
85 | |||
86 | ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
87 | cumulative_shared_perms, | ||
88 | - ignore_children, tran, errp); | ||
89 | + tran, errp); | ||
90 | if (ret < 0) { | ||
91 | return ret; | ||
92 | } | ||
93 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
94 | |||
95 | static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
96 | uint64_t cumulative_perms, | ||
97 | - uint64_t cumulative_shared_perms, | ||
98 | - GSList *ignore_children, Error **errp) | ||
99 | + uint64_t cumulative_shared_perms, Error **errp) | ||
100 | { | ||
101 | g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
102 | return bdrv_check_perm_common(list, q, true, cumulative_perms, | ||
103 | - cumulative_shared_perms, ignore_children, | ||
104 | - NULL, errp); | ||
105 | + cumulative_shared_perms, NULL, errp); | ||
106 | } | ||
107 | |||
108 | static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
109 | Transaction *tran, Error **errp) | ||
110 | { | ||
111 | - return bdrv_check_perm_common(list, q, false, 0, 0, NULL, tran, errp); | ||
112 | + return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp); | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | @@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm) | ||
117 | static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
118 | uint64_t new_used_perm, | ||
119 | uint64_t new_shared_perm, | ||
120 | - GSList *ignore_children, | ||
121 | Error **errp) | ||
122 | { | ||
123 | BdrvChild *c; | ||
124 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
125 | assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED); | ||
126 | |||
127 | QLIST_FOREACH(c, &bs->parents, next_parent) { | ||
128 | - if (g_slist_find(ignore_children, c)) { | ||
129 | - continue; | ||
130 | - } | ||
131 | - | ||
132 | if ((new_used_perm & c->shared_perm) != new_used_perm) { | ||
133 | char *user = bdrv_child_user_desc(c); | ||
134 | char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm); | ||
135 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
136 | } | ||
137 | |||
138 | return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms, | ||
139 | - ignore_children, errp); | ||
140 | + errp); | ||
141 | } | ||
142 | |||
143 | static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) | ||
144 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
145 | QTAILQ_FOREACH(bs_entry, bs_queue, entry) { | ||
146 | BDRVReopenState *state = &bs_entry->state; | ||
147 | ret = bdrv_check_perm(state->bs, bs_queue, state->perm, | ||
148 | - state->shared_perm, NULL, errp); | ||
149 | + state->shared_perm, errp); | ||
150 | if (ret < 0) { | ||
151 | goto cleanup_perm; | ||
152 | } | ||
153 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
154 | bs_queue, state->perm, state->shared_perm, | ||
155 | &nperm, &nshared); | ||
156 | ret = bdrv_check_update_perm(state->new_backing_bs, NULL, | ||
157 | - nperm, nshared, NULL, errp); | ||
158 | + nperm, nshared, errp); | ||
159 | if (ret < 0) { | ||
160 | goto cleanup_perm; | ||
161 | } | ||
162 | -- | ||
163 | 2.30.2 | ||
164 | |||
165 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | To be used in the further commit. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Message-Id: <20210428151804.439460-27-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | block.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- | ||
11 | 1 file changed, 42 insertions(+), 4 deletions(-) | ||
12 | |||
13 | diff --git a/block.c b/block.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) | ||
18 | bdrv_unref(child_bs); | ||
19 | } | ||
20 | |||
21 | +typedef struct BdrvSetInheritsFrom { | ||
22 | + BlockDriverState *bs; | ||
23 | + BlockDriverState *old_inherits_from; | ||
24 | +} BdrvSetInheritsFrom; | ||
25 | + | ||
26 | +static void bdrv_set_inherits_from_abort(void *opaque) | ||
27 | +{ | ||
28 | + BdrvSetInheritsFrom *s = opaque; | ||
29 | + | ||
30 | + s->bs->inherits_from = s->old_inherits_from; | ||
31 | +} | ||
32 | + | ||
33 | +static TransactionActionDrv bdrv_set_inherits_from_drv = { | ||
34 | + .abort = bdrv_set_inherits_from_abort, | ||
35 | + .clean = g_free, | ||
36 | +}; | ||
37 | + | ||
38 | +/* @tran is allowed to be NULL. In this case no rollback is possible */ | ||
39 | +static void bdrv_set_inherits_from(BlockDriverState *bs, | ||
40 | + BlockDriverState *new_inherits_from, | ||
41 | + Transaction *tran) | ||
42 | +{ | ||
43 | + if (tran) { | ||
44 | + BdrvSetInheritsFrom *s = g_new(BdrvSetInheritsFrom, 1); | ||
45 | + | ||
46 | + *s = (BdrvSetInheritsFrom) { | ||
47 | + .bs = bs, | ||
48 | + .old_inherits_from = bs->inherits_from, | ||
49 | + }; | ||
50 | + | ||
51 | + tran_add(tran, &bdrv_set_inherits_from_drv, s); | ||
52 | + } | ||
53 | + | ||
54 | + bs->inherits_from = new_inherits_from; | ||
55 | +} | ||
56 | + | ||
57 | /** | ||
58 | * Clear all inherits_from pointers from children and grandchildren of | ||
59 | * @root that point to @root, where necessary. | ||
60 | + * @tran is allowed to be NULL. In this case no rollback is possible | ||
61 | */ | ||
62 | -static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child) | ||
63 | +static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, | ||
64 | + Transaction *tran) | ||
65 | { | ||
66 | BdrvChild *c; | ||
67 | |||
68 | @@ -XXX,XX +XXX,XX @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child) | ||
69 | } | ||
70 | } | ||
71 | if (c == NULL) { | ||
72 | - child->bs->inherits_from = NULL; | ||
73 | + bdrv_set_inherits_from(child->bs, NULL, tran); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | QLIST_FOREACH(c, &child->bs->children, next) { | ||
78 | - bdrv_unset_inherits_from(root, c); | ||
79 | + bdrv_unset_inherits_from(root, c, tran); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | @@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | - bdrv_unset_inherits_from(parent, child); | ||
88 | + bdrv_unset_inherits_from(parent, child, NULL); | ||
89 | bdrv_root_unref_child(child); | ||
90 | } | ||
91 | |||
92 | -- | ||
93 | 2.30.2 | ||
94 | |||
95 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | To be used in further commit. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Message-Id: <20210428151804.439460-28-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | include/block/block.h | 3 ++- | ||
11 | block.c | 9 ++++----- | ||
12 | block/io.c | 31 +++++++++++++++++++++++++++++-- | ||
13 | 3 files changed, 35 insertions(+), 8 deletions(-) | ||
14 | |||
15 | diff --git a/include/block/block.h b/include/block/block.h | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/include/block/block.h | ||
18 | +++ b/include/block/block.h | ||
19 | @@ -XXX,XX +XXX,XX @@ | ||
20 | #include "block/dirty-bitmap.h" | ||
21 | #include "block/blockjob.h" | ||
22 | #include "qemu/hbitmap.h" | ||
23 | +#include "qemu/transactions.h" | ||
24 | |||
25 | /* | ||
26 | * generated_co_wrapper | ||
27 | @@ -XXX,XX +XXX,XX @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); | ||
28 | BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, | ||
29 | BlockDriverState *in_bs, Error **errp); | ||
30 | void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); | ||
31 | -void bdrv_refresh_limits(BlockDriverState *bs, Error **errp); | ||
32 | +void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); | ||
33 | int bdrv_commit(BlockDriverState *bs); | ||
34 | int bdrv_make_empty(BdrvChild *c, Error **errp); | ||
35 | int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, | ||
36 | diff --git a/block.c b/block.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | ||
38 | --- a/block.c | ||
39 | +++ b/block.c | ||
40 | @@ -XXX,XX +XXX,XX @@ | ||
41 | #include "qemu/timer.h" | ||
42 | #include "qemu/cutils.h" | ||
43 | #include "qemu/id.h" | ||
44 | -#include "qemu/transactions.h" | ||
45 | #include "block/coroutines.h" | ||
46 | |||
47 | #ifdef CONFIG_BSD | ||
48 | @@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, | ||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | - bdrv_refresh_limits(bs, &local_err); | ||
53 | + bdrv_refresh_limits(bs, NULL, &local_err); | ||
54 | if (local_err) { | ||
55 | error_propagate(errp, local_err); | ||
56 | return -EINVAL; | ||
57 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
58 | } | ||
59 | |||
60 | out: | ||
61 | - bdrv_refresh_limits(bs, NULL); | ||
62 | + bdrv_refresh_limits(bs, NULL, NULL); | ||
63 | |||
64 | return ret; | ||
65 | } | ||
66 | @@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||
67 | bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort); | ||
68 | } | ||
69 | |||
70 | - bdrv_refresh_limits(bs, NULL); | ||
71 | + bdrv_refresh_limits(bs, NULL, NULL); | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
76 | out: | ||
77 | tran_finalize(tran, ret); | ||
78 | |||
79 | - bdrv_refresh_limits(bs_top, NULL); | ||
80 | + bdrv_refresh_limits(bs_top, NULL, NULL); | ||
81 | |||
82 | return ret; | ||
83 | } | ||
84 | diff --git a/block/io.c b/block/io.c | ||
85 | index XXXXXXX..XXXXXXX 100644 | ||
86 | --- a/block/io.c | ||
87 | +++ b/block/io.c | ||
88 | @@ -XXX,XX +XXX,XX @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) | ||
89 | dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov); | ||
90 | } | ||
91 | |||
92 | -void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) | ||
93 | +typedef struct BdrvRefreshLimitsState { | ||
94 | + BlockDriverState *bs; | ||
95 | + BlockLimits old_bl; | ||
96 | +} BdrvRefreshLimitsState; | ||
97 | + | ||
98 | +static void bdrv_refresh_limits_abort(void *opaque) | ||
99 | +{ | ||
100 | + BdrvRefreshLimitsState *s = opaque; | ||
101 | + | ||
102 | + s->bs->bl = s->old_bl; | ||
103 | +} | ||
104 | + | ||
105 | +static TransactionActionDrv bdrv_refresh_limits_drv = { | ||
106 | + .abort = bdrv_refresh_limits_abort, | ||
107 | + .clean = g_free, | ||
108 | +}; | ||
109 | + | ||
110 | +/* @tran is allowed to be NULL, in this case no rollback is possible. */ | ||
111 | +void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) | ||
112 | { | ||
113 | ERRP_GUARD(); | ||
114 | BlockDriver *drv = bs->drv; | ||
115 | BdrvChild *c; | ||
116 | bool have_limits; | ||
117 | |||
118 | + if (tran) { | ||
119 | + BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1); | ||
120 | + *s = (BdrvRefreshLimitsState) { | ||
121 | + .bs = bs, | ||
122 | + .old_bl = bs->bl, | ||
123 | + }; | ||
124 | + tran_add(tran, &bdrv_refresh_limits_drv, s); | ||
125 | + } | ||
126 | + | ||
127 | memset(&bs->bl, 0, sizeof(bs->bl)); | ||
128 | |||
129 | if (!drv) { | ||
130 | @@ -XXX,XX +XXX,XX @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) | ||
131 | QLIST_FOREACH(c, &bs->children, next) { | ||
132 | if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW)) | ||
133 | { | ||
134 | - bdrv_refresh_limits(c->bs, errp); | ||
135 | + bdrv_refresh_limits(c->bs, tran, errp); | ||
136 | if (*errp) { | ||
137 | return; | ||
138 | } | ||
139 | -- | ||
140 | 2.30.2 | ||
141 | |||
142 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Split out no-perm part of bdrv_set_backing_hd() as a separate | ||
4 | transaction action. Note the in case of existing BdrvChild we reuse it, | ||
5 | not recreate, just to do less actions. | ||
6 | |||
7 | We don't need to create extra reference to backing_hd as we don't lose | ||
8 | it in bdrv_attach_child(). | ||
9 | |||
10 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Message-Id: <20210428151804.439460-29-vsementsov@virtuozzo.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | ||
15 | block.c | 54 +++++++++++++++++++++++++++++++++++++----------------- | ||
16 | 1 file changed, 37 insertions(+), 17 deletions(-) | ||
17 | |||
18 | diff --git a/block.c b/block.c | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/block.c | ||
21 | +++ b/block.c | ||
22 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
23 | BdrvChild **child, | ||
24 | Transaction *tran, | ||
25 | Error **errp); | ||
26 | +static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, | ||
27 | + Transaction *tran); | ||
28 | |||
29 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue | ||
30 | *queue, Error **errp); | ||
31 | @@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) | ||
32 | * Sets the bs->backing link of a BDS. A new reference is created; callers | ||
33 | * which don't need their own reference any more must call bdrv_unref(). | ||
34 | */ | ||
35 | -int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
36 | - Error **errp) | ||
37 | +static int bdrv_set_backing_noperm(BlockDriverState *bs, | ||
38 | + BlockDriverState *backing_hd, | ||
39 | + Transaction *tran, Error **errp) | ||
40 | { | ||
41 | int ret = 0; | ||
42 | bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) && | ||
43 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
44 | return -EPERM; | ||
45 | } | ||
46 | |||
47 | - if (backing_hd) { | ||
48 | - bdrv_ref(backing_hd); | ||
49 | - } | ||
50 | - | ||
51 | if (bs->backing) { | ||
52 | /* Cannot be frozen, we checked that above */ | ||
53 | - bdrv_unref_child(bs, bs->backing); | ||
54 | - bs->backing = NULL; | ||
55 | + bdrv_unset_inherits_from(bs, bs->backing, tran); | ||
56 | + bdrv_remove_filter_or_cow_child(bs, tran); | ||
57 | } | ||
58 | |||
59 | if (!backing_hd) { | ||
60 | goto out; | ||
61 | } | ||
62 | |||
63 | - bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds, | ||
64 | - bdrv_backing_role(bs), errp); | ||
65 | - if (!bs->backing) { | ||
66 | - ret = -EPERM; | ||
67 | - goto out; | ||
68 | + ret = bdrv_attach_child_noperm(bs, backing_hd, "backing", | ||
69 | + &child_of_bds, bdrv_backing_role(bs), | ||
70 | + &bs->backing, tran, errp); | ||
71 | + if (ret < 0) { | ||
72 | + return ret; | ||
73 | } | ||
74 | |||
75 | - /* If backing_hd was already part of bs's backing chain, and | ||
76 | + | ||
77 | + /* | ||
78 | + * If backing_hd was already part of bs's backing chain, and | ||
79 | * inherits_from pointed recursively to bs then let's update it to | ||
80 | - * point directly to bs (else it will become NULL). */ | ||
81 | + * point directly to bs (else it will become NULL). | ||
82 | + */ | ||
83 | if (update_inherits_from) { | ||
84 | - backing_hd->inherits_from = bs; | ||
85 | + bdrv_set_inherits_from(backing_hd, bs, tran); | ||
86 | } | ||
87 | |||
88 | out: | ||
89 | - bdrv_refresh_limits(bs, NULL, NULL); | ||
90 | + bdrv_refresh_limits(bs, tran, NULL); | ||
91 | + | ||
92 | + return 0; | ||
93 | +} | ||
94 | + | ||
95 | +int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
96 | + Error **errp) | ||
97 | +{ | ||
98 | + int ret; | ||
99 | + Transaction *tran = tran_new(); | ||
100 | + | ||
101 | + ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); | ||
102 | + if (ret < 0) { | ||
103 | + goto out; | ||
104 | + } | ||
105 | + | ||
106 | + ret = bdrv_refresh_perms(bs, errp); | ||
107 | +out: | ||
108 | + tran_finalize(tran, ret); | ||
109 | |||
110 | return ret; | ||
111 | } | ||
112 | -- | ||
113 | 2.30.2 | ||
114 | |||
115 | diff view generated by jsdifflib |
1 | From: Pavel Butsykin <pbutsykin@virtuozzo.com> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | After calling qcow2_inactivate(), all qcow2 caches must be flushed, but this | 3 | During reopen we may add backing bs from other aio context, which may |
4 | may not happen, because the last call qcow2_store_persistent_dirty_bitmaps() | 4 | lead to changing original context of top bs. |
5 | can lead to marking l2/refcont cache as dirty. | ||
6 | 5 | ||
7 | Let's move qcow2_store_persistent_dirty_bitmaps() before the caсhe flushing | 6 | We are going to move graph modification to prepare stage. So, it will |
8 | to fix it. | 7 | be possible that bdrv_flush() in bdrv_reopen_prepare called on bs in |
8 | non-original aio context, which we didn't aquire which leads to crash. | ||
9 | 9 | ||
10 | Cc: qemu-stable@nongnu.org | 10 | To avoid this problem move bdrv_flush() to be a separate reopen stage |
11 | Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com> | 11 | before bdrv_reopen_prepare(). |
12 | |||
13 | This doesn't seem correct to acquire only one aio context and not all | ||
14 | contexts participating in reopen. But it's not obvious how to do it | ||
15 | correctly, keeping in mind: | ||
16 | |||
17 | 1. rules of bdrv_set_aio_context_ignore() that requires new_context | ||
18 | lock not being held | ||
19 | |||
20 | 2. possible deadlocks because of holding all (or several?) AioContext | ||
21 | locks | ||
22 | |||
23 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
24 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
25 | Message-Id: <20210428151804.439460-30-vsementsov@virtuozzo.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 27 | --- |
14 | block/qcow2.c | 16 ++++++++-------- | 28 | block.c | 14 ++++++++------ |
15 | 1 file changed, 8 insertions(+), 8 deletions(-) | 29 | 1 file changed, 8 insertions(+), 6 deletions(-) |
16 | 30 | ||
17 | diff --git a/block/qcow2.c b/block/qcow2.c | 31 | diff --git a/block.c b/block.c |
18 | index XXXXXXX..XXXXXXX 100644 | 32 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block/qcow2.c | 33 | --- a/block.c |
20 | +++ b/block/qcow2.c | 34 | +++ b/block.c |
21 | @@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs) | 35 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) |
22 | int ret, result = 0; | 36 | |
23 | Error *local_err = NULL; | 37 | assert(bs_queue != NULL); |
24 | 38 | ||
25 | + qcow2_store_persistent_dirty_bitmaps(bs, &local_err); | 39 | + QTAILQ_FOREACH(bs_entry, bs_queue, entry) { |
26 | + if (local_err != NULL) { | 40 | + ret = bdrv_flush(bs_entry->state.bs); |
27 | + result = -EINVAL; | 41 | + if (ret < 0) { |
28 | + error_report_err(local_err); | 42 | + error_setg_errno(errp, -ret, "Error flushing drive"); |
29 | + error_report("Persistent bitmaps are lost for node '%s'", | 43 | + goto cleanup; |
30 | + bdrv_get_device_or_node_name(bs)); | 44 | + } |
31 | + } | 45 | + } |
32 | + | 46 | + |
33 | ret = qcow2_cache_flush(bs, s->l2_table_cache); | 47 | QTAILQ_FOREACH(bs_entry, bs_queue, entry) { |
34 | if (ret) { | 48 | assert(bs_entry->state.bs->quiesce_counter > 0); |
35 | result = ret; | 49 | if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) { |
36 | @@ -XXX,XX +XXX,XX @@ static int qcow2_inactivate(BlockDriverState *bs) | 50 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, |
37 | strerror(-ret)); | 51 | bdrv_reopen_perm(queue, reopen_state->bs, |
38 | } | 52 | &reopen_state->perm, &reopen_state->shared_perm); |
39 | 53 | ||
40 | - qcow2_store_persistent_dirty_bitmaps(bs, &local_err); | 54 | - ret = bdrv_flush(reopen_state->bs); |
41 | - if (local_err != NULL) { | 55 | - if (ret) { |
42 | - result = -EINVAL; | 56 | - error_setg_errno(errp, -ret, "Error flushing drive"); |
43 | - error_report_err(local_err); | 57 | - goto error; |
44 | - error_report("Persistent bitmaps are lost for node '%s'", | ||
45 | - bdrv_get_device_or_node_name(bs)); | ||
46 | - } | 58 | - } |
47 | - | 59 | - |
48 | if (result == 0) { | 60 | if (drv->bdrv_reopen_prepare) { |
49 | qcow2_mark_clean(bs); | 61 | /* |
50 | } | 62 | * If a driver-specific option is missing, it means that we |
51 | -- | 63 | -- |
52 | 2.13.5 | 64 | 2.30.2 |
53 | 65 | ||
54 | 66 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Move bdrv_reopen_multiple to new paradigm of permission update: | ||
4 | first update graph relations, then do refresh the permissions. | ||
5 | |||
6 | We have to modify reopen process in file-posix driver: with new scheme | ||
7 | we don't have prepared permissions in raw_reopen_prepare(), so we | ||
8 | should reconfigure fd in raw_check_perm(). Still this seems more native | ||
9 | and simple anyway. | ||
10 | |||
11 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
12 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | Message-Id: <20210428151804.439460-31-vsementsov@virtuozzo.com> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | --- | ||
16 | include/block/block.h | 3 +- | ||
17 | block.c | 187 ++++++++++++------------------------------ | ||
18 | block/file-posix.c | 91 +++++++------------- | ||
19 | 3 files changed, 84 insertions(+), 197 deletions(-) | ||
20 | |||
21 | diff --git a/include/block/block.h b/include/block/block.h | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/include/block/block.h | ||
24 | +++ b/include/block/block.h | ||
25 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState { | ||
26 | BlockdevDetectZeroesOptions detect_zeroes; | ||
27 | bool backing_missing; | ||
28 | bool replace_backing_bs; /* new_backing_bs is ignored if this is false */ | ||
29 | - BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */ | ||
30 | - uint64_t perm, shared_perm; | ||
31 | + BlockDriverState *old_backing_bs; /* keep pointer for permissions update */ | ||
32 | QDict *options; | ||
33 | QDict *explicit_options; | ||
34 | void *opaque; | ||
35 | diff --git a/block.c b/block.c | ||
36 | index XXXXXXX..XXXXXXX 100644 | ||
37 | --- a/block.c | ||
38 | +++ b/block.c | ||
39 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
40 | static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, | ||
41 | Transaction *tran); | ||
42 | |||
43 | -static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue | ||
44 | - *queue, Error **errp); | ||
45 | +static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
46 | + BlockReopenQueue *queue, | ||
47 | + Transaction *set_backings_tran, Error **errp); | ||
48 | static void bdrv_reopen_commit(BDRVReopenState *reopen_state); | ||
49 | static void bdrv_reopen_abort(BDRVReopenState *reopen_state); | ||
50 | |||
51 | @@ -XXX,XX +XXX,XX @@ static void bdrv_list_abort_perm_update(GSList *list) | ||
52 | } | ||
53 | } | ||
54 | |||
55 | +__attribute__((unused)) | ||
56 | static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
57 | { | ||
58 | g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
59 | @@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm) | ||
60 | * | ||
61 | * Needs to be followed by a call to either bdrv_set_perm() or | ||
62 | * bdrv_abort_perm_update(). */ | ||
63 | +__attribute__((unused)) | ||
64 | static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
65 | uint64_t new_used_perm, | ||
66 | uint64_t new_shared_perm, | ||
67 | @@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, | ||
68 | bs_entry->state.explicit_options = explicit_options; | ||
69 | bs_entry->state.flags = flags; | ||
70 | |||
71 | - /* This needs to be overwritten in bdrv_reopen_prepare() */ | ||
72 | - bs_entry->state.perm = UINT64_MAX; | ||
73 | - bs_entry->state.shared_perm = 0; | ||
74 | - | ||
75 | /* | ||
76 | * If keep_old_opts is false then it means that unspecified | ||
77 | * options must be reset to their original value. We don't allow | ||
78 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
79 | { | ||
80 | int ret = -1; | ||
81 | BlockReopenQueueEntry *bs_entry, *next; | ||
82 | + Transaction *tran = tran_new(); | ||
83 | + g_autoptr(GHashTable) found = NULL; | ||
84 | + g_autoptr(GSList) refresh_list = NULL; | ||
85 | |||
86 | assert(bs_queue != NULL); | ||
87 | |||
88 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
89 | |||
90 | QTAILQ_FOREACH(bs_entry, bs_queue, entry) { | ||
91 | assert(bs_entry->state.bs->quiesce_counter > 0); | ||
92 | - if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) { | ||
93 | - goto cleanup; | ||
94 | + ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp); | ||
95 | + if (ret < 0) { | ||
96 | + goto abort; | ||
97 | } | ||
98 | bs_entry->prepared = true; | ||
99 | } | ||
100 | |||
101 | + found = g_hash_table_new(NULL, NULL); | ||
102 | QTAILQ_FOREACH(bs_entry, bs_queue, entry) { | ||
103 | BDRVReopenState *state = &bs_entry->state; | ||
104 | - ret = bdrv_check_perm(state->bs, bs_queue, state->perm, | ||
105 | - state->shared_perm, errp); | ||
106 | - if (ret < 0) { | ||
107 | - goto cleanup_perm; | ||
108 | - } | ||
109 | - /* Check if new_backing_bs would accept the new permissions */ | ||
110 | - if (state->replace_backing_bs && state->new_backing_bs) { | ||
111 | - uint64_t nperm, nshared; | ||
112 | - bdrv_child_perm(state->bs, state->new_backing_bs, | ||
113 | - NULL, bdrv_backing_role(state->bs), | ||
114 | - bs_queue, state->perm, state->shared_perm, | ||
115 | - &nperm, &nshared); | ||
116 | - ret = bdrv_check_update_perm(state->new_backing_bs, NULL, | ||
117 | - nperm, nshared, errp); | ||
118 | - if (ret < 0) { | ||
119 | - goto cleanup_perm; | ||
120 | - } | ||
121 | + | ||
122 | + refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs); | ||
123 | + if (state->old_backing_bs) { | ||
124 | + refresh_list = bdrv_topological_dfs(refresh_list, found, | ||
125 | + state->old_backing_bs); | ||
126 | } | ||
127 | - bs_entry->perms_checked = true; | ||
128 | + } | ||
129 | + | ||
130 | + /* | ||
131 | + * Note that file-posix driver rely on permission update done during reopen | ||
132 | + * (even if no permission changed), because it wants "new" permissions for | ||
133 | + * reconfiguring the fd and that's why it does it in raw_check_perm(), not | ||
134 | + * in raw_reopen_prepare() which is called with "old" permissions. | ||
135 | + */ | ||
136 | + ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp); | ||
137 | + if (ret < 0) { | ||
138 | + goto abort; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
143 | bdrv_reopen_commit(&bs_entry->state); | ||
144 | } | ||
145 | |||
146 | - ret = 0; | ||
147 | -cleanup_perm: | ||
148 | - QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
149 | - BDRVReopenState *state = &bs_entry->state; | ||
150 | - | ||
151 | - if (!bs_entry->perms_checked) { | ||
152 | - continue; | ||
153 | - } | ||
154 | - | ||
155 | - if (ret == 0) { | ||
156 | - uint64_t perm, shared; | ||
157 | + tran_commit(tran); | ||
158 | |||
159 | - bdrv_get_cumulative_perm(state->bs, &perm, &shared); | ||
160 | - assert(perm == state->perm); | ||
161 | - assert(shared == state->shared_perm); | ||
162 | + QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { | ||
163 | + BlockDriverState *bs = bs_entry->state.bs; | ||
164 | |||
165 | - bdrv_set_perm(state->bs); | ||
166 | - } else { | ||
167 | - bdrv_abort_perm_update(state->bs); | ||
168 | - if (state->replace_backing_bs && state->new_backing_bs) { | ||
169 | - bdrv_abort_perm_update(state->new_backing_bs); | ||
170 | - } | ||
171 | + if (bs->drv->bdrv_reopen_commit_post) { | ||
172 | + bs->drv->bdrv_reopen_commit_post(&bs_entry->state); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | - if (ret == 0) { | ||
177 | - QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { | ||
178 | - BlockDriverState *bs = bs_entry->state.bs; | ||
179 | + ret = 0; | ||
180 | + goto cleanup; | ||
181 | |||
182 | - if (bs->drv->bdrv_reopen_commit_post) | ||
183 | - bs->drv->bdrv_reopen_commit_post(&bs_entry->state); | ||
184 | +abort: | ||
185 | + tran_abort(tran); | ||
186 | + QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
187 | + if (bs_entry->prepared) { | ||
188 | + bdrv_reopen_abort(&bs_entry->state); | ||
189 | } | ||
190 | + qobject_unref(bs_entry->state.explicit_options); | ||
191 | + qobject_unref(bs_entry->state.options); | ||
192 | } | ||
193 | + | ||
194 | cleanup: | ||
195 | QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
196 | - if (ret) { | ||
197 | - if (bs_entry->prepared) { | ||
198 | - bdrv_reopen_abort(&bs_entry->state); | ||
199 | - } | ||
200 | - qobject_unref(bs_entry->state.explicit_options); | ||
201 | - qobject_unref(bs_entry->state.options); | ||
202 | - } | ||
203 | - if (bs_entry->state.new_backing_bs) { | ||
204 | - bdrv_unref(bs_entry->state.new_backing_bs); | ||
205 | - } | ||
206 | g_free(bs_entry); | ||
207 | } | ||
208 | g_free(bs_queue); | ||
209 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | -static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q, | ||
214 | - BdrvChild *c) | ||
215 | -{ | ||
216 | - BlockReopenQueueEntry *entry; | ||
217 | - | ||
218 | - QTAILQ_FOREACH(entry, q, entry) { | ||
219 | - BlockDriverState *bs = entry->state.bs; | ||
220 | - BdrvChild *child; | ||
221 | - | ||
222 | - QLIST_FOREACH(child, &bs->children, next) { | ||
223 | - if (child == c) { | ||
224 | - return entry; | ||
225 | - } | ||
226 | - } | ||
227 | - } | ||
228 | - | ||
229 | - return NULL; | ||
230 | -} | ||
231 | - | ||
232 | -static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs, | ||
233 | - uint64_t *perm, uint64_t *shared) | ||
234 | -{ | ||
235 | - BdrvChild *c; | ||
236 | - BlockReopenQueueEntry *parent; | ||
237 | - uint64_t cumulative_perms = 0; | ||
238 | - uint64_t cumulative_shared_perms = BLK_PERM_ALL; | ||
239 | - | ||
240 | - QLIST_FOREACH(c, &bs->parents, next_parent) { | ||
241 | - parent = find_parent_in_reopen_queue(q, c); | ||
242 | - if (!parent) { | ||
243 | - cumulative_perms |= c->perm; | ||
244 | - cumulative_shared_perms &= c->shared_perm; | ||
245 | - } else { | ||
246 | - uint64_t nperm, nshared; | ||
247 | - | ||
248 | - bdrv_child_perm(parent->state.bs, bs, c, c->role, q, | ||
249 | - parent->state.perm, parent->state.shared_perm, | ||
250 | - &nperm, &nshared); | ||
251 | - | ||
252 | - cumulative_perms |= nperm; | ||
253 | - cumulative_shared_perms &= nshared; | ||
254 | - } | ||
255 | - } | ||
256 | - *perm = cumulative_perms; | ||
257 | - *shared = cumulative_shared_perms; | ||
258 | -} | ||
259 | - | ||
260 | static bool bdrv_reopen_can_attach(BlockDriverState *parent, | ||
261 | BdrvChild *child, | ||
262 | BlockDriverState *new_child, | ||
263 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_reopen_can_attach(BlockDriverState *parent, | ||
264 | * Return 0 on success, otherwise return < 0 and set @errp. | ||
265 | */ | ||
266 | static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, | ||
267 | + Transaction *set_backings_tran, | ||
268 | Error **errp) | ||
269 | { | ||
270 | BlockDriverState *bs = reopen_state->bs; | ||
271 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, | ||
272 | |||
273 | /* If we want to replace the backing file we need some extra checks */ | ||
274 | if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) { | ||
275 | + int ret; | ||
276 | + | ||
277 | /* Check for implicit nodes between bs and its backing file */ | ||
278 | if (bs != overlay_bs) { | ||
279 | error_setg(errp, "Cannot change backing link if '%s' has " | ||
280 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, | ||
281 | return -EPERM; | ||
282 | } | ||
283 | reopen_state->replace_backing_bs = true; | ||
284 | - if (new_backing_bs) { | ||
285 | - bdrv_ref(new_backing_bs); | ||
286 | - reopen_state->new_backing_bs = new_backing_bs; | ||
287 | + reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL; | ||
288 | + ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran, | ||
289 | + errp); | ||
290 | + if (ret < 0) { | ||
291 | + return ret; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, | ||
296 | * | ||
297 | */ | ||
298 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
299 | - BlockReopenQueue *queue, Error **errp) | ||
300 | + BlockReopenQueue *queue, | ||
301 | + Transaction *set_backings_tran, Error **errp) | ||
302 | { | ||
303 | int ret = -1; | ||
304 | int old_flags; | ||
305 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
306 | goto error; | ||
307 | } | ||
308 | |||
309 | - /* Calculate required permissions after reopening */ | ||
310 | - bdrv_reopen_perm(queue, reopen_state->bs, | ||
311 | - &reopen_state->perm, &reopen_state->shared_perm); | ||
312 | - | ||
313 | if (drv->bdrv_reopen_prepare) { | ||
314 | /* | ||
315 | * If a driver-specific option is missing, it means that we | ||
316 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
317 | * either a reference to an existing node (using its node name) | ||
318 | * or NULL to simply detach the current backing file. | ||
319 | */ | ||
320 | - ret = bdrv_reopen_parse_backing(reopen_state, errp); | ||
321 | + ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp); | ||
322 | if (ret < 0) { | ||
323 | goto error; | ||
324 | } | ||
325 | @@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||
326 | qdict_del(bs->explicit_options, child->name); | ||
327 | qdict_del(bs->options, child->name); | ||
328 | } | ||
329 | - | ||
330 | - /* | ||
331 | - * Change the backing file if a new one was specified. We do this | ||
332 | - * after updating bs->options, so bdrv_refresh_filename() (called | ||
333 | - * from bdrv_set_backing_hd()) has the new values. | ||
334 | - */ | ||
335 | - if (reopen_state->replace_backing_bs) { | ||
336 | - BlockDriverState *old_backing_bs = child_bs(bs->backing); | ||
337 | - assert(!old_backing_bs || !old_backing_bs->implicit); | ||
338 | - /* Abort the permission update on the backing bs we're detaching */ | ||
339 | - if (old_backing_bs) { | ||
340 | - bdrv_abort_perm_update(old_backing_bs); | ||
341 | - } | ||
342 | - bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort); | ||
343 | - } | ||
344 | - | ||
345 | bdrv_refresh_limits(bs, NULL, NULL); | ||
346 | } | ||
347 | |||
348 | diff --git a/block/file-posix.c b/block/file-posix.c | ||
349 | index XXXXXXX..XXXXXXX 100644 | ||
350 | --- a/block/file-posix.c | ||
351 | +++ b/block/file-posix.c | ||
352 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState { | ||
353 | } BDRVRawState; | ||
354 | |||
355 | typedef struct BDRVRawReopenState { | ||
356 | - int fd; | ||
357 | int open_flags; | ||
358 | bool drop_cache; | ||
359 | bool check_cache_dropped; | ||
360 | @@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state, | ||
361 | BDRVRawReopenState *rs; | ||
362 | QemuOpts *opts; | ||
363 | int ret; | ||
364 | - Error *local_err = NULL; | ||
365 | |||
366 | assert(state != NULL); | ||
367 | assert(state->bs != NULL); | ||
368 | @@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state, | ||
369 | * bdrv_reopen_prepare() will detect changes and complain. */ | ||
370 | qemu_opts_to_qdict(opts, state->options); | ||
371 | |||
372 | - rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags, | ||
373 | - state->perm, true, &local_err); | ||
374 | - if (local_err) { | ||
375 | - error_propagate(errp, local_err); | ||
376 | - ret = -1; | ||
377 | - goto out; | ||
378 | - } | ||
379 | - | ||
380 | - /* Fail already reopen_prepare() if we can't get a working O_DIRECT | ||
381 | - * alignment with the new fd. */ | ||
382 | - if (rs->fd != -1) { | ||
383 | - raw_probe_alignment(state->bs, rs->fd, &local_err); | ||
384 | - if (local_err) { | ||
385 | - error_propagate(errp, local_err); | ||
386 | - ret = -EINVAL; | ||
387 | - goto out_fd; | ||
388 | - } | ||
389 | - } | ||
390 | + /* | ||
391 | + * As part of reopen prepare we also want to create new fd by | ||
392 | + * raw_reconfigure_getfd(). But it wants updated "perm", when in | ||
393 | + * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to | ||
394 | + * permission update. Happily, permission update is always a part (a seprate | ||
395 | + * stage) of bdrv_reopen_multiple() so we can rely on this fact and | ||
396 | + * reconfigure fd in raw_check_perm(). | ||
397 | + */ | ||
398 | |||
399 | s->reopen_state = state; | ||
400 | ret = 0; | ||
401 | -out_fd: | ||
402 | - if (ret < 0) { | ||
403 | - qemu_close(rs->fd); | ||
404 | - rs->fd = -1; | ||
405 | - } | ||
406 | + | ||
407 | out: | ||
408 | qemu_opts_del(opts); | ||
409 | return ret; | ||
410 | @@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state) | ||
411 | s->drop_cache = rs->drop_cache; | ||
412 | s->check_cache_dropped = rs->check_cache_dropped; | ||
413 | s->open_flags = rs->open_flags; | ||
414 | - | ||
415 | - qemu_close(s->fd); | ||
416 | - s->fd = rs->fd; | ||
417 | - | ||
418 | g_free(state->opaque); | ||
419 | state->opaque = NULL; | ||
420 | |||
421 | @@ -XXX,XX +XXX,XX @@ static void raw_reopen_abort(BDRVReopenState *state) | ||
422 | return; | ||
423 | } | ||
424 | |||
425 | - if (rs->fd >= 0) { | ||
426 | - qemu_close(rs->fd); | ||
427 | - rs->fd = -1; | ||
428 | - } | ||
429 | g_free(state->opaque); | ||
430 | state->opaque = NULL; | ||
431 | |||
432 | @@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, | ||
433 | Error **errp) | ||
434 | { | ||
435 | BDRVRawState *s = bs->opaque; | ||
436 | - BDRVRawReopenState *rs = NULL; | ||
437 | + int input_flags = s->reopen_state ? s->reopen_state->flags : bs->open_flags; | ||
438 | int open_flags; | ||
439 | int ret; | ||
440 | |||
441 | - if (s->perm_change_fd) { | ||
442 | + /* We may need a new fd if auto-read-only switches the mode */ | ||
443 | + ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, | ||
444 | + false, errp); | ||
445 | + if (ret < 0) { | ||
446 | + return ret; | ||
447 | + } else if (ret != s->fd) { | ||
448 | + Error *local_err = NULL; | ||
449 | + | ||
450 | /* | ||
451 | - * In the context of reopen, this function may be called several times | ||
452 | - * (directly and recursively while change permissions of the parent). | ||
453 | - * This is even true for children that don't inherit from the original | ||
454 | - * reopen node, so s->reopen_state is not set. | ||
455 | - * | ||
456 | - * Ignore all but the first call. | ||
457 | + * Fail already check_perm() if we can't get a working O_DIRECT | ||
458 | + * alignment with the new fd. | ||
459 | */ | ||
460 | - return 0; | ||
461 | - } | ||
462 | - | ||
463 | - if (s->reopen_state) { | ||
464 | - /* We already have a new file descriptor to set permissions for */ | ||
465 | - assert(s->reopen_state->perm == perm); | ||
466 | - assert(s->reopen_state->shared_perm == shared); | ||
467 | - rs = s->reopen_state->opaque; | ||
468 | - s->perm_change_fd = rs->fd; | ||
469 | - s->perm_change_flags = rs->open_flags; | ||
470 | - } else { | ||
471 | - /* We may need a new fd if auto-read-only switches the mode */ | ||
472 | - ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm, | ||
473 | - false, errp); | ||
474 | - if (ret < 0) { | ||
475 | - return ret; | ||
476 | - } else if (ret != s->fd) { | ||
477 | - s->perm_change_fd = ret; | ||
478 | - s->perm_change_flags = open_flags; | ||
479 | + raw_probe_alignment(bs, ret, &local_err); | ||
480 | + if (local_err) { | ||
481 | + error_propagate(errp, local_err); | ||
482 | + return -EINVAL; | ||
483 | } | ||
484 | + | ||
485 | + s->perm_change_fd = ret; | ||
486 | + s->perm_change_flags = open_flags; | ||
487 | } | ||
488 | |||
489 | /* Prepare permissions on old fd to avoid conflicts between old and new, | ||
490 | @@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, | ||
491 | return 0; | ||
492 | |||
493 | fail: | ||
494 | - if (s->perm_change_fd && !s->reopen_state) { | ||
495 | + if (s->perm_change_fd) { | ||
496 | qemu_close(s->perm_change_fd); | ||
497 | } | ||
498 | s->perm_change_fd = 0; | ||
499 | @@ -XXX,XX +XXX,XX @@ static void raw_abort_perm_update(BlockDriverState *bs) | ||
500 | |||
501 | /* For reopen, .bdrv_reopen_abort is called afterwards and will close | ||
502 | * the file descriptor. */ | ||
503 | - if (s->perm_change_fd && !s->reopen_state) { | ||
504 | + if (s->perm_change_fd) { | ||
505 | qemu_close(s->perm_change_fd); | ||
506 | } | ||
507 | s->perm_change_fd = 0; | ||
508 | -- | ||
509 | 2.30.2 | ||
510 | |||
511 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
5 | Message-Id: <20210428151804.439460-32-vsementsov@virtuozzo.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | --- | ||
8 | block.c | 103 -------------------------------------------------------- | ||
9 | 1 file changed, 103 deletions(-) | ||
10 | |||
11 | diff --git a/block.c b/block.c | ||
12 | index XXXXXXX..XXXXXXX 100644 | ||
13 | --- a/block.c | ||
14 | +++ b/block.c | ||
15 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, | ||
16 | return 0; | ||
17 | } | ||
18 | |||
19 | -static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
20 | - uint64_t new_used_perm, | ||
21 | - uint64_t new_shared_perm, | ||
22 | - Error **errp); | ||
23 | - | ||
24 | typedef struct BlockReopenQueueEntry { | ||
25 | bool prepared; | ||
26 | bool perms_checked; | ||
27 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | -static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
32 | - uint64_t cumulative_perms, | ||
33 | - uint64_t cumulative_shared_perms, Error **errp) | ||
34 | -{ | ||
35 | - g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
36 | - return bdrv_check_perm_common(list, q, true, cumulative_perms, | ||
37 | - cumulative_shared_perms, NULL, errp); | ||
38 | -} | ||
39 | - | ||
40 | static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
41 | Transaction *tran, Error **errp) | ||
42 | { | ||
43 | return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp); | ||
44 | } | ||
45 | |||
46 | -/* | ||
47 | - * Notifies drivers that after a previous bdrv_check_perm() call, the | ||
48 | - * permission update is not performed and any preparations made for it (e.g. | ||
49 | - * taken file locks) need to be undone. | ||
50 | - */ | ||
51 | -static void bdrv_node_abort_perm_update(BlockDriverState *bs) | ||
52 | -{ | ||
53 | - BlockDriver *drv = bs->drv; | ||
54 | - BdrvChild *c; | ||
55 | - | ||
56 | - if (!drv) { | ||
57 | - return; | ||
58 | - } | ||
59 | - | ||
60 | - bdrv_drv_set_perm_abort(bs); | ||
61 | - | ||
62 | - QLIST_FOREACH(c, &bs->children, next) { | ||
63 | - bdrv_child_set_perm_abort(c); | ||
64 | - } | ||
65 | -} | ||
66 | - | ||
67 | -static void bdrv_list_abort_perm_update(GSList *list) | ||
68 | -{ | ||
69 | - for ( ; list; list = list->next) { | ||
70 | - bdrv_node_abort_perm_update((BlockDriverState *)list->data); | ||
71 | - } | ||
72 | -} | ||
73 | - | ||
74 | -__attribute__((unused)) | ||
75 | -static void bdrv_abort_perm_update(BlockDriverState *bs) | ||
76 | -{ | ||
77 | - g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
78 | - return bdrv_list_abort_perm_update(list); | ||
79 | -} | ||
80 | - | ||
81 | static void bdrv_node_set_perm(BlockDriverState *bs) | ||
82 | { | ||
83 | BlockDriver *drv = bs->drv; | ||
84 | @@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm) | ||
85 | return g_string_free(result, FALSE); | ||
86 | } | ||
87 | |||
88 | -/* | ||
89 | - * Checks whether a new reference to @bs can be added if the new user requires | ||
90 | - * @new_used_perm/@new_shared_perm as its permissions. If @ignore_children is | ||
91 | - * set, the BdrvChild objects in this list are ignored in the calculations; | ||
92 | - * this allows checking permission updates for an existing reference. | ||
93 | - * | ||
94 | - * Needs to be followed by a call to either bdrv_set_perm() or | ||
95 | - * bdrv_abort_perm_update(). */ | ||
96 | -__attribute__((unused)) | ||
97 | -static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
98 | - uint64_t new_used_perm, | ||
99 | - uint64_t new_shared_perm, | ||
100 | - Error **errp) | ||
101 | -{ | ||
102 | - BdrvChild *c; | ||
103 | - uint64_t cumulative_perms = new_used_perm; | ||
104 | - uint64_t cumulative_shared_perms = new_shared_perm; | ||
105 | - | ||
106 | - | ||
107 | - /* There is no reason why anyone couldn't tolerate write_unchanged */ | ||
108 | - assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED); | ||
109 | - | ||
110 | - QLIST_FOREACH(c, &bs->parents, next_parent) { | ||
111 | - if ((new_used_perm & c->shared_perm) != new_used_perm) { | ||
112 | - char *user = bdrv_child_user_desc(c); | ||
113 | - char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm); | ||
114 | - | ||
115 | - error_setg(errp, "Conflicts with use by %s as '%s', which does not " | ||
116 | - "allow '%s' on %s", | ||
117 | - user, c->name, perm_names, bdrv_get_node_name(c->bs)); | ||
118 | - g_free(user); | ||
119 | - g_free(perm_names); | ||
120 | - return -EPERM; | ||
121 | - } | ||
122 | - | ||
123 | - if ((c->perm & new_shared_perm) != c->perm) { | ||
124 | - char *user = bdrv_child_user_desc(c); | ||
125 | - char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm); | ||
126 | - | ||
127 | - error_setg(errp, "Conflicts with use by %s as '%s', which uses " | ||
128 | - "'%s' on %s", | ||
129 | - user, c->name, perm_names, bdrv_get_node_name(c->bs)); | ||
130 | - g_free(user); | ||
131 | - g_free(perm_names); | ||
132 | - return -EPERM; | ||
133 | - } | ||
134 | - | ||
135 | - cumulative_perms |= c->perm; | ||
136 | - cumulative_shared_perms &= c->shared_perm; | ||
137 | - } | ||
138 | - | ||
139 | - return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms, | ||
140 | - errp); | ||
141 | -} | ||
142 | |||
143 | static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) | ||
144 | { | ||
145 | -- | ||
146 | 2.30.2 | ||
147 | |||
148 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | bdrv_check_perm_common() has only one caller, so no more sense in | ||
4 | "common". | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20210428151804.439460-33-vsementsov@virtuozzo.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | block.c | 32 +++----------------------------- | ||
12 | 1 file changed, 3 insertions(+), 29 deletions(-) | ||
13 | |||
14 | diff --git a/block.c b/block.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block.c | ||
17 | +++ b/block.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
19 | return 0; | ||
20 | } | ||
21 | |||
22 | -/* | ||
23 | - * If use_cumulative_perms is true, use cumulative_perms and | ||
24 | - * cumulative_shared_perms for first element of the list. Otherwise just refresh | ||
25 | - * all permissions. | ||
26 | - */ | ||
27 | -static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
28 | - bool use_cumulative_perms, | ||
29 | - uint64_t cumulative_perms, | ||
30 | - uint64_t cumulative_shared_perms, | ||
31 | - Transaction *tran, Error **errp) | ||
32 | +static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
33 | + Transaction *tran, Error **errp) | ||
34 | { | ||
35 | int ret; | ||
36 | + uint64_t cumulative_perms, cumulative_shared_perms; | ||
37 | BlockDriverState *bs; | ||
38 | |||
39 | - if (use_cumulative_perms) { | ||
40 | - bs = list->data; | ||
41 | - | ||
42 | - ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
43 | - cumulative_shared_perms, | ||
44 | - tran, errp); | ||
45 | - if (ret < 0) { | ||
46 | - return ret; | ||
47 | - } | ||
48 | - | ||
49 | - list = list->next; | ||
50 | - } | ||
51 | - | ||
52 | for ( ; list; list = list->next) { | ||
53 | bs = list->data; | ||
54 | |||
55 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q, | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | -static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
60 | - Transaction *tran, Error **errp) | ||
61 | -{ | ||
62 | - return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp); | ||
63 | -} | ||
64 | - | ||
65 | static void bdrv_node_set_perm(BlockDriverState *bs) | ||
66 | { | ||
67 | BlockDriver *drv = bs->drv; | ||
68 | -- | ||
69 | 2.30.2 | ||
70 | |||
71 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | Now that bdrv_truncate is passed to bs->file by default, remove the | 3 | bdrv_replace_child() has only one caller, the second argument is |
4 | callback from block/blkdebug.c and set is_filter to true. is_filter also gives | 4 | unused. Inline it now. This triggers deletion of some more unused |
5 | access to other callbacks that are forwarded automatically to bs->file for | 5 | interfaces. |
6 | filters. | ||
7 | 6 | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 7 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
10 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 9 | Message-Id: <20210428151804.439460-34-vsementsov@virtuozzo.com> |
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 11 | --- |
13 | block/blkdebug.c | 8 +------- | 12 | block.c | 101 ++++++++++---------------------------------------------- |
14 | 1 file changed, 1 insertion(+), 7 deletions(-) | 13 | 1 file changed, 18 insertions(+), 83 deletions(-) |
15 | 14 | ||
16 | diff --git a/block/blkdebug.c b/block/blkdebug.c | 15 | diff --git a/block.c b/block.c |
17 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/blkdebug.c | 17 | --- a/block.c |
19 | +++ b/block/blkdebug.c | 18 | +++ b/block.c |
20 | @@ -XXX,XX +XXX,XX @@ static int64_t blkdebug_getlength(BlockDriverState *bs) | 19 | @@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, |
21 | return bdrv_getlength(bs->file->bs); | 20 | return 0; |
22 | } | 21 | } |
23 | 22 | ||
24 | -static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, | 23 | -static void bdrv_node_set_perm(BlockDriverState *bs) |
25 | - PreallocMode prealloc, Error **errp) | ||
26 | -{ | 24 | -{ |
27 | - return bdrv_truncate(bs->file, offset, prealloc, errp); | 25 | - BlockDriver *drv = bs->drv; |
26 | - BdrvChild *c; | ||
27 | - | ||
28 | - if (!drv) { | ||
29 | - return; | ||
30 | - } | ||
31 | - | ||
32 | - bdrv_drv_set_perm_commit(bs); | ||
33 | - | ||
34 | - /* Drivers that never have children can omit .bdrv_child_perm() */ | ||
35 | - if (!drv->bdrv_child_perm) { | ||
36 | - assert(QLIST_EMPTY(&bs->children)); | ||
37 | - return; | ||
38 | - } | ||
39 | - | ||
40 | - /* Update all children */ | ||
41 | - QLIST_FOREACH(c, &bs->children, next) { | ||
42 | - bdrv_child_set_perm_commit(c); | ||
43 | - } | ||
28 | -} | 44 | -} |
29 | - | 45 | - |
30 | static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | 46 | -static void bdrv_list_set_perm(GSList *list) |
47 | -{ | ||
48 | - for ( ; list; list = list->next) { | ||
49 | - bdrv_node_set_perm((BlockDriverState *)list->data); | ||
50 | - } | ||
51 | -} | ||
52 | - | ||
53 | -static void bdrv_set_perm(BlockDriverState *bs) | ||
54 | -{ | ||
55 | - g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); | ||
56 | - return bdrv_list_set_perm(list); | ||
57 | -} | ||
58 | - | ||
59 | void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
60 | uint64_t *shared_perm) | ||
31 | { | 61 | { |
32 | BDRVBlkdebugState *s = bs->opaque; | 62 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, |
33 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | 63 | } |
34 | .format_name = "blkdebug", | 64 | } |
35 | .protocol_name = "blkdebug", | 65 | |
36 | .instance_size = sizeof(BDRVBlkdebugState), | 66 | -/* |
37 | + .is_filter = true, | 67 | - * Updates @child to change its reference to point to @new_bs, including |
38 | 68 | - * checking and applying the necessary permission updates both to the old node | |
39 | .bdrv_parse_filename = blkdebug_parse_filename, | 69 | - * and to @new_bs. |
40 | .bdrv_file_open = blkdebug_open, | 70 | - * |
41 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | 71 | - * NULL is passed as @new_bs for removing the reference before freeing @child. |
42 | .bdrv_child_perm = bdrv_filter_default_perms, | 72 | - * |
43 | 73 | - * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this | |
44 | .bdrv_getlength = blkdebug_getlength, | 74 | - * function uses bdrv_set_perm() to update the permissions according to the new |
45 | - .bdrv_truncate = blkdebug_truncate, | 75 | - * reference that @new_bs gets. |
46 | .bdrv_refresh_filename = blkdebug_refresh_filename, | 76 | - * |
47 | .bdrv_refresh_limits = blkdebug_refresh_limits, | 77 | - * Callers must ensure that child->frozen is false. |
48 | 78 | - */ | |
79 | -static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) | ||
80 | -{ | ||
81 | - BlockDriverState *old_bs = child->bs; | ||
82 | - | ||
83 | - /* Asserts that child->frozen == false */ | ||
84 | - bdrv_replace_child_noperm(child, new_bs); | ||
85 | - | ||
86 | - /* | ||
87 | - * Start with the new node's permissions. If @new_bs is a (direct | ||
88 | - * or indirect) child of @old_bs, we must complete the permission | ||
89 | - * update on @new_bs before we loosen the restrictions on @old_bs. | ||
90 | - * Otherwise, bdrv_check_perm() on @old_bs would re-initiate | ||
91 | - * updating the permissions of @new_bs, and thus not purely loosen | ||
92 | - * restrictions. | ||
93 | - */ | ||
94 | - if (new_bs) { | ||
95 | - bdrv_set_perm(new_bs); | ||
96 | - } | ||
97 | - | ||
98 | - if (old_bs) { | ||
99 | - /* | ||
100 | - * Update permissions for old node. We're just taking a parent away, so | ||
101 | - * we're loosening restrictions. Errors of permission update are not | ||
102 | - * fatal in this case, ignore them. | ||
103 | - */ | ||
104 | - bdrv_refresh_perms(old_bs, NULL); | ||
105 | - | ||
106 | - /* When the parent requiring a non-default AioContext is removed, the | ||
107 | - * node moves back to the main AioContext */ | ||
108 | - bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL); | ||
109 | - } | ||
110 | -} | ||
111 | - | ||
112 | static void bdrv_child_free(void *opaque) | ||
113 | { | ||
114 | BdrvChild *c = opaque; | ||
115 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
116 | |||
117 | static void bdrv_detach_child(BdrvChild *child) | ||
118 | { | ||
119 | - bdrv_replace_child(child, NULL); | ||
120 | + BlockDriverState *old_bs = child->bs; | ||
121 | + | ||
122 | + bdrv_replace_child_noperm(child, NULL); | ||
123 | bdrv_remove_empty_child(child); | ||
124 | + | ||
125 | + if (old_bs) { | ||
126 | + /* | ||
127 | + * Update permissions for old node. We're just taking a parent away, so | ||
128 | + * we're loosening restrictions. Errors of permission update are not | ||
129 | + * fatal in this case, ignore them. | ||
130 | + */ | ||
131 | + bdrv_refresh_perms(old_bs, NULL); | ||
132 | + | ||
133 | + /* | ||
134 | + * When the parent requiring a non-default AioContext is removed, the | ||
135 | + * node moves back to the main AioContext | ||
136 | + */ | ||
137 | + bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL); | ||
138 | + } | ||
139 | } | ||
140 | |||
141 | /* | ||
49 | -- | 142 | -- |
50 | 2.13.5 | 143 | 2.30.2 |
51 | 144 | ||
52 | 145 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | 2 | ||
3 | The following functions fail if bs->drv is a filter and does not | 3 | Old interfaces dropped, nobody directly calls |
4 | implement them: | 4 | bdrv_child_set_perm_abort() and bdrv_child_set_perm_commit(), so we can |
5 | use personal state structure for the action and stop exploiting | ||
6 | BdrvChild structure. Also, drop "_safe" suffix which is redundant now. | ||
5 | 7 | ||
6 | bdrv_probe_blocksizes | 8 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
7 | bdrv_probe_geometry | 9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
8 | bdrv_truncate | 10 | Message-Id: <20210428151804.439460-35-vsementsov@virtuozzo.com> |
9 | bdrv_has_zero_init | ||
10 | bdrv_get_info | ||
11 | |||
12 | Instead, the call should be passed to bs->file if it exists, to allow | ||
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> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
22 | --- | 12 | --- |
23 | include/block/block_int.h | 6 +++++- | 13 | include/block/block_int.h | 5 ---- |
24 | block.c | 21 +++++++++++++++++++-- | 14 | block.c | 63 ++++++++++++++------------------------- |
25 | 2 files changed, 24 insertions(+), 3 deletions(-) | 15 | 2 files changed, 22 insertions(+), 46 deletions(-) |
26 | 16 | ||
27 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 17 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
28 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/include/block/block_int.h | 19 | --- a/include/block/block_int.h |
30 | +++ b/include/block/block_int.h | 20 | +++ b/include/block/block_int.h |
31 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 21 | @@ -XXX,XX +XXX,XX @@ struct BdrvChild { |
32 | const char *format_name; | 22 | */ |
33 | int instance_size; | 23 | uint64_t shared_perm; |
34 | 24 | ||
35 | - /* set to true if the BlockDriver is a block filter */ | 25 | - /* backup of permissions during permission update procedure */ |
36 | + /* set to true if the BlockDriver is a block filter. Block filters pass | 26 | - bool has_backup_perm; |
37 | + * certain callbacks that refer to data (see block.c) to their bs->file if | 27 | - uint64_t backup_perm; |
38 | + * the driver doesn't implement them. Drivers that do not wish to forward | 28 | - uint64_t backup_shared_perm; |
39 | + * must implement them and return -ENOTSUP. | 29 | - |
40 | + */ | 30 | /* |
41 | bool is_filter; | 31 | * This link is frozen: the child can neither be replaced nor |
42 | /* for snapshots block filter like Quorum can implement the | 32 | * detached from the parent. |
43 | * following recursive callback. | ||
44 | diff --git a/block.c b/block.c | 33 | diff --git a/block.c b/block.c |
45 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
46 | --- a/block.c | 35 | --- a/block.c |
47 | +++ b/block.c | 36 | +++ b/block.c |
48 | @@ -XXX,XX +XXX,XX @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) | 37 | @@ -XXX,XX +XXX,XX @@ static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found, |
49 | 38 | return g_slist_prepend(list, bs); | |
50 | if (drv && drv->bdrv_probe_blocksizes) { | 39 | } |
51 | return drv->bdrv_probe_blocksizes(bs, bsz); | 40 | |
52 | + } else if (drv && drv->is_filter && bs->file) { | 41 | -static void bdrv_child_set_perm_commit(void *opaque) |
53 | + return bdrv_probe_blocksizes(bs->file->bs, bsz); | 42 | -{ |
43 | - BdrvChild *c = opaque; | ||
44 | - | ||
45 | - c->has_backup_perm = false; | ||
46 | -} | ||
47 | +typedef struct BdrvChildSetPermState { | ||
48 | + BdrvChild *child; | ||
49 | + uint64_t old_perm; | ||
50 | + uint64_t old_shared_perm; | ||
51 | +} BdrvChildSetPermState; | ||
52 | |||
53 | static void bdrv_child_set_perm_abort(void *opaque) | ||
54 | { | ||
55 | - BdrvChild *c = opaque; | ||
56 | - /* | ||
57 | - * We may have child->has_backup_perm unset at this point, as in case of | ||
58 | - * _check_ stage of permission update failure we may _check_ not the whole | ||
59 | - * subtree. Still, _abort_ is called on the whole subtree anyway. | ||
60 | - */ | ||
61 | - if (c->has_backup_perm) { | ||
62 | - c->perm = c->backup_perm; | ||
63 | - c->shared_perm = c->backup_shared_perm; | ||
64 | - c->has_backup_perm = false; | ||
65 | - } | ||
66 | + BdrvChildSetPermState *s = opaque; | ||
67 | + | ||
68 | + s->child->perm = s->old_perm; | ||
69 | + s->child->shared_perm = s->old_shared_perm; | ||
70 | } | ||
71 | |||
72 | static TransactionActionDrv bdrv_child_set_pem_drv = { | ||
73 | .abort = bdrv_child_set_perm_abort, | ||
74 | - .commit = bdrv_child_set_perm_commit, | ||
75 | + .clean = g_free, | ||
76 | }; | ||
77 | |||
78 | -/* | ||
79 | - * With tran=NULL needs to be followed by direct call to either | ||
80 | - * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort(). | ||
81 | - * | ||
82 | - * With non-NULL tran needs to be followed by tran_abort() or tran_commit() | ||
83 | - * instead. | ||
84 | - */ | ||
85 | -static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm, | ||
86 | - uint64_t shared, Transaction *tran) | ||
87 | +static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, | ||
88 | + uint64_t shared, Transaction *tran) | ||
89 | { | ||
90 | - if (!c->has_backup_perm) { | ||
91 | - c->has_backup_perm = true; | ||
92 | - c->backup_perm = c->perm; | ||
93 | - c->backup_shared_perm = c->shared_perm; | ||
94 | - } | ||
95 | - /* | ||
96 | - * Note: it's OK if c->has_backup_perm was already set, as we can find the | ||
97 | - * same c twice during check_perm procedure | ||
98 | - */ | ||
99 | + BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1); | ||
100 | + | ||
101 | + *s = (BdrvChildSetPermState) { | ||
102 | + .child = c, | ||
103 | + .old_perm = c->perm, | ||
104 | + .old_shared_perm = c->shared_perm, | ||
105 | + }; | ||
106 | |||
107 | c->perm = perm; | ||
108 | c->shared_perm = shared; | ||
109 | |||
110 | - if (tran) { | ||
111 | - tran_add(tran, &bdrv_child_set_pem_drv, c); | ||
112 | - } | ||
113 | + tran_add(tran, &bdrv_child_set_pem_drv, s); | ||
114 | } | ||
115 | |||
116 | static void bdrv_drv_set_perm_commit(void *opaque) | ||
117 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
118 | bdrv_child_perm(bs, c->bs, c, c->role, q, | ||
119 | cumulative_perms, cumulative_shared_perms, | ||
120 | &cur_perm, &cur_shared); | ||
121 | - bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran); | ||
122 | + bdrv_child_set_perm(c, cur_perm, cur_shared, tran); | ||
54 | } | 123 | } |
55 | 124 | ||
56 | return -ENOTSUP; | ||
57 | @@ -XXX,XX +XXX,XX @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | ||
58 | |||
59 | if (drv && drv->bdrv_probe_geometry) { | ||
60 | return drv->bdrv_probe_geometry(bs, geo); | ||
61 | + } else if (drv && drv->is_filter && bs->file) { | ||
62 | + return bdrv_probe_geometry(bs->file->bs, geo); | ||
63 | } | ||
64 | |||
65 | return -ENOTSUP; | ||
66 | @@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, | ||
67 | |||
68 | assert(child->perm & BLK_PERM_RESIZE); | ||
69 | |||
70 | + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ | ||
71 | if (!drv) { | ||
72 | error_setg(errp, "No medium inserted"); | ||
73 | return -ENOMEDIUM; | ||
74 | } | ||
75 | if (!drv->bdrv_truncate) { | ||
76 | + if (bs->file && drv->is_filter) { | ||
77 | + return bdrv_truncate(bs->file, offset, prealloc, errp); | ||
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 | + } | ||
89 | |||
90 | /* safe default */ | ||
91 | return 0; | 125 | return 0; |
92 | @@ -XXX,XX +XXX,XX @@ void bdrv_get_backing_filename(BlockDriverState *bs, | 126 | @@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, |
93 | int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | 127 | Transaction *tran = tran_new(); |
94 | { | 128 | int ret; |
95 | BlockDriver *drv = bs->drv; | 129 | |
96 | - if (!drv) | 130 | - bdrv_child_set_perm_safe(c, perm, shared, tran); |
97 | + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ | 131 | + bdrv_child_set_perm(c, perm, shared, tran); |
98 | + if (!drv) { | 132 | |
99 | return -ENOMEDIUM; | 133 | ret = bdrv_refresh_perms(c->bs, &local_err); |
100 | - if (!drv->bdrv_get_info) | 134 | |
101 | + } | ||
102 | + if (!drv->bdrv_get_info) { | ||
103 | + if (bs->file && drv->is_filter) { | ||
104 | + return bdrv_get_info(bs->file->bs, bdi); | ||
105 | + } | ||
106 | return -ENOTSUP; | ||
107 | + } | ||
108 | memset(bdi, 0, sizeof(*bdi)); | ||
109 | return drv->bdrv_get_info(bs, bdi); | ||
110 | } | ||
111 | -- | 135 | -- |
112 | 2.13.5 | 136 | 2.30.2 |
113 | 137 | ||
114 | 138 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | We don't have bdrv_replace_child(), so it's time for | ||
4 | bdrv_replace_child_safe() to take its place. | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20210428151804.439460-36-vsementsov@virtuozzo.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | block.c | 10 +++++----- | ||
12 | 1 file changed, 5 insertions(+), 5 deletions(-) | ||
13 | |||
14 | diff --git a/block.c b/block.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block.c | ||
17 | +++ b/block.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = { | ||
19 | }; | ||
20 | |||
21 | /* | ||
22 | - * bdrv_replace_child_safe | ||
23 | + * bdrv_replace_child | ||
24 | * | ||
25 | * Note: real unref of old_bs is done only on commit. | ||
26 | */ | ||
27 | -static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs, | ||
28 | - Transaction *tran) | ||
29 | +static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs, | ||
30 | + Transaction *tran) | ||
31 | { | ||
32 | BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); | ||
33 | *s = (BdrvReplaceChildState) { | ||
34 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, | ||
35 | } | ||
36 | |||
37 | if (child->bs) { | ||
38 | - bdrv_replace_child_safe(child, NULL, tran); | ||
39 | + bdrv_replace_child(child, NULL, tran); | ||
40 | } | ||
41 | |||
42 | s = g_new(BdrvRemoveFilterOrCowChild, 1); | ||
43 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
44 | c->name, from->node_name); | ||
45 | return -EPERM; | ||
46 | } | ||
47 | - bdrv_replace_child_safe(c, to, tran); | ||
48 | + bdrv_replace_child(c, to, tran); | ||
49 | } | ||
50 | |||
51 | return 0; | ||
52 | -- | ||
53 | 2.30.2 | ||
54 | |||
55 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Now, bdrv_node_check_perm() is called only with fresh cumulative | ||
4 | permissions, so its actually "refresh_perm". | ||
5 | |||
6 | Move permission calculation to the function. Also, drop unreachable | ||
7 | error message and rewrite the remaining one to be more generic (as now | ||
8 | we don't know which node is added and which was already here). | ||
9 | |||
10 | Add also Virtuozzo copyright, as big work is done at this point. | ||
11 | |||
12 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
13 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | Message-Id: <20210428151804.439460-37-vsementsov@virtuozzo.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | --- | ||
17 | block.c | 38 +++++++++++--------------------------- | ||
18 | tests/qemu-iotests/245 | 2 +- | ||
19 | 2 files changed, 12 insertions(+), 28 deletions(-) | ||
20 | |||
21 | diff --git a/block.c b/block.c | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/block.c | ||
24 | +++ b/block.c | ||
25 | @@ -XXX,XX +XXX,XX @@ | ||
26 | * QEMU System Emulator block driver | ||
27 | * | ||
28 | * Copyright (c) 2003 Fabrice Bellard | ||
29 | + * Copyright (c) 2020 Virtuozzo International GmbH. | ||
30 | * | ||
31 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
32 | * of this software and associated documentation files (the "Software"), to deal | ||
33 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs, | ||
34 | } | ||
35 | |||
36 | /* | ||
37 | - * Check whether permissions on this node can be changed in a way that | ||
38 | - * @cumulative_perms and @cumulative_shared_perms are the new cumulative | ||
39 | - * permissions of all its parents. This involves checking whether all necessary | ||
40 | - * permission changes to child nodes can be performed. | ||
41 | - * | ||
42 | - * A call to this function must always be followed by a call to bdrv_set_perm() | ||
43 | - * or bdrv_abort_perm_update(). | ||
44 | + * Refresh permissions in @bs subtree. The function is intended to be called | ||
45 | + * after some graph modification that was done without permission update. | ||
46 | */ | ||
47 | -static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
48 | - uint64_t cumulative_perms, | ||
49 | - uint64_t cumulative_shared_perms, | ||
50 | - Transaction *tran, Error **errp) | ||
51 | +static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
52 | + Transaction *tran, Error **errp) | ||
53 | { | ||
54 | BlockDriver *drv = bs->drv; | ||
55 | BdrvChild *c; | ||
56 | int ret; | ||
57 | + uint64_t cumulative_perms, cumulative_shared_perms; | ||
58 | + | ||
59 | + bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms); | ||
60 | |||
61 | /* Write permissions never work with read-only images */ | ||
62 | if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && | ||
63 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
64 | if (!bdrv_is_writable_after_reopen(bs, NULL)) { | ||
65 | error_setg(errp, "Block node is read-only"); | ||
66 | } else { | ||
67 | - uint64_t current_perms, current_shared; | ||
68 | - bdrv_get_cumulative_perm(bs, ¤t_perms, ¤t_shared); | ||
69 | - if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) { | ||
70 | - error_setg(errp, "Cannot make block node read-only, there is " | ||
71 | - "a writer on it"); | ||
72 | - } else { | ||
73 | - error_setg(errp, "Cannot make block node read-only and create " | ||
74 | - "a writer on it"); | ||
75 | - } | ||
76 | + error_setg(errp, "Read-only block node '%s' cannot support " | ||
77 | + "read-write users", bdrv_get_node_name(bs)); | ||
78 | } | ||
79 | |||
80 | return -EPERM; | ||
81 | @@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
82 | Transaction *tran, Error **errp) | ||
83 | { | ||
84 | int ret; | ||
85 | - uint64_t cumulative_perms, cumulative_shared_perms; | ||
86 | BlockDriverState *bs; | ||
87 | |||
88 | for ( ; list; list = list->next) { | ||
89 | @@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | - bdrv_get_cumulative_perm(bs, &cumulative_perms, | ||
94 | - &cumulative_shared_perms); | ||
95 | - | ||
96 | - ret = bdrv_node_check_perm(bs, q, cumulative_perms, | ||
97 | - cumulative_shared_perms, | ||
98 | - tran, errp); | ||
99 | + ret = bdrv_node_refresh_perm(bs, q, tran, errp); | ||
100 | if (ret < 0) { | ||
101 | return ret; | ||
102 | } | ||
103 | diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 | ||
104 | index XXXXXXX..XXXXXXX 100755 | ||
105 | --- a/tests/qemu-iotests/245 | ||
106 | +++ b/tests/qemu-iotests/245 | ||
107 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase): | ||
108 | # We can't reopen hd1 to read-only, as block-stream requires it to be | ||
109 | # read-write | ||
110 | self.reopen(opts['backing'], {'read-only': True}, | ||
111 | - "Cannot make block node read-only, there is a writer on it") | ||
112 | + "Read-only block node 'hd1' cannot support read-write users") | ||
113 | |||
114 | # We can't remove hd2 while the stream job is ongoing | ||
115 | opts['backing']['backing'] = None | ||
116 | -- | ||
117 | 2.30.2 | ||
118 | |||
119 | diff view generated by jsdifflib |
1 | From: Manos Pitsidianakis <el13635@mail.ntua.gr> | 1 | Normally, blk_new_open() just shares all permissions. This was fine |
---|---|---|---|
2 | originally when permissions only protected against uses in the same | ||
3 | process because no other part of the code would actually get to access | ||
4 | the block nodes opened with blk_new_open(). However, since we use it for | ||
5 | file locking now, unsharing permissions becomes desirable. | ||
2 | 6 | ||
3 | Move the CoMutex and CoQueue inits inside throttle_group_register_tgm() | 7 | Add a new BDRV_O_NO_SHARE flag that is used in blk_new_open() to unshare |
4 | which is called whenever a ThrottleGroupMember is initialized. There's | 8 | any permissions that can be unshared. |
5 | no need for them to be separate. | ||
6 | 9 | ||
7 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 11 | Message-Id: <20210422164344.283389-2-kwolf@redhat.com> |
9 | Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr> | 12 | Reviewed-by: Eric Blake <eblake@redhat.com> |
13 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 15 | --- |
12 | block/block-backend.c | 3 --- | 16 | include/block/block.h | 1 + |
13 | block/throttle-groups.c | 3 +++ | 17 | block/block-backend.c | 19 +++++++++++++------ |
14 | 2 files changed, 3 insertions(+), 3 deletions(-) | 18 | 2 files changed, 14 insertions(+), 6 deletions(-) |
15 | 19 | ||
20 | diff --git a/include/block/block.h b/include/block/block.h | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/include/block/block.h | ||
23 | +++ b/include/block/block.h | ||
24 | @@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry { | ||
25 | uint32_t cylinders; | ||
26 | } HDGeometry; | ||
27 | |||
28 | +#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */ | ||
29 | #define BDRV_O_RDWR 0x0002 | ||
30 | #define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */ | ||
31 | #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ | ||
16 | diff --git a/block/block-backend.c b/block/block-backend.c | 32 | diff --git a/block/block-backend.c b/block/block-backend.c |
17 | index XXXXXXX..XXXXXXX 100644 | 33 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/block-backend.c | 34 | --- a/block/block-backend.c |
19 | +++ b/block/block-backend.c | 35 | +++ b/block/block-backend.c |
20 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | 36 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, |
21 | blk->shared_perm = shared_perm; | 37 | BlockBackend *blk; |
22 | blk_set_enable_write_cache(blk, true); | 38 | BlockDriverState *bs; |
23 | 39 | uint64_t perm = 0; | |
24 | - qemu_co_mutex_init(&blk->public.throttle_group_member.throttled_reqs_lock); | 40 | + uint64_t shared = BLK_PERM_ALL; |
25 | - qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[0]); | 41 | |
26 | - qemu_co_queue_init(&blk->public.throttle_group_member.throttled_reqs[1]); | 42 | - /* blk_new_open() is mainly used in .bdrv_create implementations and the |
27 | block_acct_init(&blk->stats); | 43 | - * tools where sharing isn't a concern because the BDS stays private, so we |
28 | 44 | - * just request permission according to the flags. | |
29 | notifier_list_init(&blk->remove_bs_notifiers); | 45 | + /* |
30 | diff --git a/block/throttle-groups.c b/block/throttle-groups.c | 46 | + * blk_new_open() is mainly used in .bdrv_create implementations and the |
31 | index XXXXXXX..XXXXXXX 100644 | 47 | + * tools where sharing isn't a major concern because the BDS stays private |
32 | --- a/block/throttle-groups.c | 48 | + * and the file is generally not supposed to be used by a second process, |
33 | +++ b/block/throttle-groups.c | 49 | + * so we just request permission according to the flags. |
34 | @@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, | 50 | * |
35 | read_timer_cb, | 51 | * The exceptions are xen_disk and blockdev_init(); in these cases, the |
36 | write_timer_cb, | 52 | * caller of blk_new_open() doesn't make use of the permissions, but they |
37 | tgm); | 53 | * shouldn't hurt either. We can still share everything here because the |
38 | + qemu_co_mutex_init(&tgm->throttled_reqs_lock); | 54 | - * guest devices will add their own blockers if they can't share. */ |
39 | + qemu_co_queue_init(&tgm->throttled_reqs[0]); | 55 | + * guest devices will add their own blockers if they can't share. |
40 | + qemu_co_queue_init(&tgm->throttled_reqs[1]); | 56 | + */ |
41 | 57 | if ((flags & BDRV_O_NO_IO) == 0) { | |
42 | qemu_mutex_unlock(&tg->lock); | 58 | perm |= BLK_PERM_CONSISTENT_READ; |
43 | } | 59 | if (flags & BDRV_O_RDWR) { |
60 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, | ||
61 | if (flags & BDRV_O_RESIZE) { | ||
62 | perm |= BLK_PERM_RESIZE; | ||
63 | } | ||
64 | + if (flags & BDRV_O_NO_SHARE) { | ||
65 | + shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; | ||
66 | + } | ||
67 | |||
68 | - blk = blk_new(qemu_get_aio_context(), perm, BLK_PERM_ALL); | ||
69 | + blk = blk_new(qemu_get_aio_context(), perm, shared); | ||
70 | bs = bdrv_open(filename, reference, options, flags, errp); | ||
71 | if (!bs) { | ||
72 | blk_unref(blk); | ||
73 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, | ||
74 | |||
75 | blk->root = bdrv_root_attach_child(bs, "root", &child_root, | ||
76 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | ||
77 | - perm, BLK_PERM_ALL, blk, errp); | ||
78 | + perm, shared, blk, errp); | ||
79 | if (!blk->root) { | ||
80 | blk_unref(blk); | ||
81 | return NULL; | ||
44 | -- | 82 | -- |
45 | 2.13.5 | 83 | 2.30.2 |
46 | 84 | ||
47 | 85 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | For a successful conversion of an image, we must make sure that its | ||
2 | content doesn't change during the conversion. | ||
1 | 3 | ||
4 | A special case of this is using the same image file both as the source | ||
5 | and as the destination. If both input and output format are raw, the | ||
6 | operation would just be useless work, with other formats it is a sure | ||
7 | way to destroy the image. This will now fail because the image file | ||
8 | can't be opened a second time for the output when opening it for the | ||
9 | input has already acquired file locks to unshare BLK_PERM_WRITE. | ||
10 | |||
11 | Nevertheless, if there is some reason in a special case why it is | ||
12 | actually okay to allow writes to the image while it is being converted, | ||
13 | -U can still be used to force sharing all permissions. | ||
14 | |||
15 | Note that for most image formats, BLK_PERM_WRITE would already be | ||
16 | unshared by the format driver, so this only really makes a difference | ||
17 | for raw source images (but any output format). | ||
18 | |||
19 | Reported-by: Xueqiang Wei <xuwei@redhat.com> | ||
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
21 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
22 | Message-Id: <20210422164344.283389-3-kwolf@redhat.com> | ||
23 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
25 | --- | ||
26 | qemu-img.c | 2 +- | ||
27 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
28 | |||
29 | diff --git a/qemu-img.c b/qemu-img.c | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/qemu-img.c | ||
32 | +++ b/qemu-img.c | ||
33 | @@ -XXX,XX +XXX,XX @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) | ||
34 | |||
35 | static int img_convert(int argc, char **argv) | ||
36 | { | ||
37 | - int c, bs_i, flags, src_flags = 0; | ||
38 | + int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE; | ||
39 | const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe", | ||
40 | *src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL, | ||
41 | *out_filename, *out_baseimg_param, *snapshot_name = NULL; | ||
42 | -- | ||
43 | 2.30.2 | ||
44 | |||
45 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | virtio_add_queue() aborts when queue_size > VIRTQUEUE_MAX_SIZE, so |
---|---|---|---|
2 | vhost_user_blk_device_realize() should check this before calling it. | ||
2 | 3 | ||
3 | Omitting the check for whether bdrv_getlength() and bdrv_truncate() | 4 | Simple reproducer: |
4 | failed meant that it was theoretically possible to return an | ||
5 | incorrect offset to the caller. More likely, conditions for either | ||
6 | of these functions to fail would also cause one of our other calls | ||
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 | 5 | ||
11 | Use osdep.h macros instead of open-coded rounding while in the | 6 | qemu-system-x86_64 \ |
12 | area. | 7 | -chardev null,id=foo \ |
8 | -device vhost-user-blk-pci,queue-size=4096,chardev=foo | ||
13 | 9 | ||
14 | Reported-by: Markus Armbruster <armbru@redhat.com> | 10 | Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1935014 |
15 | Signed-off-by: Eric Blake <eblake@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | Message-Id: <20210413165654.50810-1-kwolf@redhat.com> | ||
13 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
14 | Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com> | ||
15 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
16 | Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 18 | --- |
18 | block/qcow.c | 30 ++++++++++++++++++++++-------- | 19 | hw/block/vhost-user-blk.c | 5 +++++ |
19 | 1 file changed, 22 insertions(+), 8 deletions(-) | 20 | 1 file changed, 5 insertions(+) |
20 | 21 | ||
21 | diff --git a/block/qcow.c b/block/qcow.c | 22 | diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c |
22 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/block/qcow.c | 24 | --- a/hw/block/vhost-user-blk.c |
24 | +++ b/block/qcow.c | 25 | +++ b/hw/block/vhost-user-blk.c |
25 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | 26 | @@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) |
26 | { | 27 | error_setg(errp, "vhost-user-blk: queue size must be non-zero"); |
27 | BDRVQcowState *s = bs->opaque; | 28 | return; |
28 | int min_index, i, j, l1_index, l2_index, ret; | 29 | } |
29 | - uint64_t l2_offset, *l2_table, cluster_offset, tmp; | 30 | + if (s->queue_size > VIRTQUEUE_MAX_SIZE) { |
30 | + int64_t l2_offset; | 31 | + error_setg(errp, "vhost-user-blk: queue size must not exceed %d", |
31 | + uint64_t *l2_table, cluster_offset, tmp; | 32 | + VIRTQUEUE_MAX_SIZE); |
32 | uint32_t min_count; | 33 | + return; |
33 | int new_l2_table; | 34 | + } |
34 | 35 | ||
35 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | 36 | if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) { |
36 | return 0; | 37 | return; |
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 | -- | 38 | -- |
87 | 2.13.5 | 39 | 2.30.2 |
88 | 40 | ||
89 | 41 | diff view generated by jsdifflib |