1da668aa1SThomas Huth /*
2da668aa1SThomas Huth * Block node graph modifications tests
3da668aa1SThomas Huth *
4d71cc67dSVladimir Sementsov-Ogievskiy * Copyright (c) 2019-2021 Virtuozzo International GmbH. All rights reserved.
5da668aa1SThomas Huth *
6da668aa1SThomas Huth * This program is free software; you can redistribute it and/or modify
7da668aa1SThomas Huth * it under the terms of the GNU General Public License as published by
8da668aa1SThomas Huth * the Free Software Foundation; either version 2 of the License, or
9da668aa1SThomas Huth * (at your option) any later version.
10da668aa1SThomas Huth *
11da668aa1SThomas Huth * This program is distributed in the hope that it will be useful,
12da668aa1SThomas Huth * but WITHOUT ANY WARRANTY; without even the implied warranty of
13da668aa1SThomas Huth * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14da668aa1SThomas Huth * GNU General Public License for more details.
15da668aa1SThomas Huth *
16da668aa1SThomas Huth * You should have received a copy of the GNU General Public License
17da668aa1SThomas Huth * along with this program. If not, see <http://www.gnu.org/licenses/>.
18da668aa1SThomas Huth *
19da668aa1SThomas Huth */
20da668aa1SThomas Huth
21da668aa1SThomas Huth #include "qemu/osdep.h"
22da668aa1SThomas Huth #include "qapi/error.h"
23da668aa1SThomas Huth #include "qemu/main-loop.h"
24da668aa1SThomas Huth #include "block/block_int.h"
25da668aa1SThomas Huth #include "sysemu/block-backend.h"
26da668aa1SThomas Huth
27da668aa1SThomas Huth static BlockDriver bdrv_pass_through = {
28da668aa1SThomas Huth .format_name = "pass-through",
291921b4f7SVladimir Sementsov-Ogievskiy .is_filter = true,
301921b4f7SVladimir Sementsov-Ogievskiy .filtered_child_is_backing = true,
31da668aa1SThomas Huth .bdrv_child_perm = bdrv_default_perms,
32da668aa1SThomas Huth };
33da668aa1SThomas Huth
no_perm_default_perms(BlockDriverState * bs,BdrvChild * c,BdrvChildRole role,BlockReopenQueue * reopen_queue,uint64_t perm,uint64_t shared,uint64_t * nperm,uint64_t * nshared)34da668aa1SThomas Huth static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c,
35da668aa1SThomas Huth BdrvChildRole role,
36da668aa1SThomas Huth BlockReopenQueue *reopen_queue,
37da668aa1SThomas Huth uint64_t perm, uint64_t shared,
38da668aa1SThomas Huth uint64_t *nperm, uint64_t *nshared)
39da668aa1SThomas Huth {
40da668aa1SThomas Huth *nperm = 0;
41da668aa1SThomas Huth *nshared = BLK_PERM_ALL;
42da668aa1SThomas Huth }
43da668aa1SThomas Huth
44da668aa1SThomas Huth static BlockDriver bdrv_no_perm = {
45da668aa1SThomas Huth .format_name = "no-perm",
4625f78d9eSVladimir Sementsov-Ogievskiy .supports_backing = true,
47da668aa1SThomas Huth .bdrv_child_perm = no_perm_default_perms,
48da668aa1SThomas Huth };
49da668aa1SThomas Huth
exclusive_write_perms(BlockDriverState * bs,BdrvChild * c,BdrvChildRole role,BlockReopenQueue * reopen_queue,uint64_t perm,uint64_t shared,uint64_t * nperm,uint64_t * nshared)50d71cc67dSVladimir Sementsov-Ogievskiy static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c,
51d71cc67dSVladimir Sementsov-Ogievskiy BdrvChildRole role,
52d71cc67dSVladimir Sementsov-Ogievskiy BlockReopenQueue *reopen_queue,
53d71cc67dSVladimir Sementsov-Ogievskiy uint64_t perm, uint64_t shared,
54d71cc67dSVladimir Sementsov-Ogievskiy uint64_t *nperm, uint64_t *nshared)
55d71cc67dSVladimir Sementsov-Ogievskiy {
56d71cc67dSVladimir Sementsov-Ogievskiy *nperm = BLK_PERM_WRITE;
57d71cc67dSVladimir Sementsov-Ogievskiy *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
58d71cc67dSVladimir Sementsov-Ogievskiy }
59d71cc67dSVladimir Sementsov-Ogievskiy
60d71cc67dSVladimir Sementsov-Ogievskiy static BlockDriver bdrv_exclusive_writer = {
61d71cc67dSVladimir Sementsov-Ogievskiy .format_name = "exclusive-writer",
621921b4f7SVladimir Sementsov-Ogievskiy .is_filter = true,
631921b4f7SVladimir Sementsov-Ogievskiy .filtered_child_is_backing = true,
64d71cc67dSVladimir Sementsov-Ogievskiy .bdrv_child_perm = exclusive_write_perms,
65d71cc67dSVladimir Sementsov-Ogievskiy };
66d71cc67dSVladimir Sementsov-Ogievskiy
no_perm_node(const char * name)67da668aa1SThomas Huth static BlockDriverState *no_perm_node(const char *name)
68da668aa1SThomas Huth {
69da668aa1SThomas Huth return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
70da668aa1SThomas Huth }
71da668aa1SThomas Huth
pass_through_node(const char * name)72da668aa1SThomas Huth static BlockDriverState *pass_through_node(const char *name)
73da668aa1SThomas Huth {
74da668aa1SThomas Huth return bdrv_new_open_driver(&bdrv_pass_through, name,
75da668aa1SThomas Huth BDRV_O_RDWR, &error_abort);
76da668aa1SThomas Huth }
77da668aa1SThomas Huth
exclusive_writer_node(const char * name)78d71cc67dSVladimir Sementsov-Ogievskiy static BlockDriverState *exclusive_writer_node(const char *name)
79d71cc67dSVladimir Sementsov-Ogievskiy {
80d71cc67dSVladimir Sementsov-Ogievskiy return bdrv_new_open_driver(&bdrv_exclusive_writer, name,
81d71cc67dSVladimir Sementsov-Ogievskiy BDRV_O_RDWR, &error_abort);
82d71cc67dSVladimir Sementsov-Ogievskiy }
83d71cc67dSVladimir Sementsov-Ogievskiy
84da668aa1SThomas Huth /*
85da668aa1SThomas Huth * test_update_perm_tree
86da668aa1SThomas Huth *
87da668aa1SThomas Huth * When checking node for a possibility to update permissions, it's subtree
88da668aa1SThomas Huth * should be correctly checked too. New permissions for each node should be
89da668aa1SThomas Huth * calculated and checked in context of permissions of other nodes. If we
90da668aa1SThomas Huth * check new permissions of the node only in context of old permissions of
91da668aa1SThomas Huth * its neighbors, we can finish up with wrong permission graph.
92da668aa1SThomas Huth *
93da668aa1SThomas Huth * This test firstly create the following graph:
94da668aa1SThomas Huth * +--------+
95da668aa1SThomas Huth * | root |
96da668aa1SThomas Huth * +--------+
97da668aa1SThomas Huth * |
98da668aa1SThomas Huth * | perm: write, read
99da668aa1SThomas Huth * | shared: except write
100da668aa1SThomas Huth * v
10196420a30SMichael Tokarev * +--------------------+ +----------------+
10296420a30SMichael Tokarev * | passthrough filter |--------->| null-co node |
10396420a30SMichael Tokarev * +--------------------+ +----------------+
104da668aa1SThomas Huth *
105da668aa1SThomas Huth *
106da668aa1SThomas Huth * and then, tries to append filter under node. Expected behavior: fail.
107da668aa1SThomas Huth * Otherwise we'll get the following picture, with two BdrvChild'ren, having
108da668aa1SThomas Huth * write permission to one node, without actually sharing it.
109da668aa1SThomas Huth *
110da668aa1SThomas Huth * +--------+
111da668aa1SThomas Huth * | root |
112da668aa1SThomas Huth * +--------+
113da668aa1SThomas Huth * |
114da668aa1SThomas Huth * | perm: write, read
115da668aa1SThomas Huth * | shared: except write
116da668aa1SThomas Huth * v
11796420a30SMichael Tokarev * +--------------------+
11896420a30SMichael Tokarev * | passthrough filter |
11996420a30SMichael Tokarev * +--------------------+
120da668aa1SThomas Huth * | |
121da668aa1SThomas Huth * perm: write, read | | perm: write, read
122da668aa1SThomas Huth * shared: except write | | shared: except write
123da668aa1SThomas Huth * v v
124da668aa1SThomas Huth * +----------------+
125da668aa1SThomas Huth * | null co node |
126da668aa1SThomas Huth * +----------------+
127da668aa1SThomas Huth */
test_update_perm_tree(void)128da668aa1SThomas Huth static void test_update_perm_tree(void)
129da668aa1SThomas Huth {
130da668aa1SThomas Huth int ret;
131da668aa1SThomas Huth
132da668aa1SThomas Huth BlockBackend *root = blk_new(qemu_get_aio_context(),
133da668aa1SThomas Huth BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ,
134da668aa1SThomas Huth BLK_PERM_ALL & ~BLK_PERM_WRITE);
135da668aa1SThomas Huth BlockDriverState *bs = no_perm_node("node");
136da668aa1SThomas Huth BlockDriverState *filter = pass_through_node("filter");
137da668aa1SThomas Huth
138da668aa1SThomas Huth blk_insert_bs(root, bs, &error_abort);
139da668aa1SThomas Huth
140*6bc30f19SStefan Hajnoczi bdrv_graph_wrlock();
141da668aa1SThomas Huth bdrv_attach_child(filter, bs, "child", &child_of_bds,
1421921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_DATA, &error_abort);
143*6bc30f19SStefan Hajnoczi bdrv_graph_wrunlock();
144da668aa1SThomas Huth
145da668aa1SThomas Huth ret = bdrv_append(filter, bs, NULL);
146da668aa1SThomas Huth g_assert_cmpint(ret, <, 0);
147da668aa1SThomas Huth
148ae9d4417SVladimir Sementsov-Ogievskiy bdrv_unref(filter);
149da668aa1SThomas Huth blk_unref(root);
150da668aa1SThomas Huth }
151da668aa1SThomas Huth
152da668aa1SThomas Huth /*
153da668aa1SThomas Huth * test_should_update_child
154da668aa1SThomas Huth *
155da668aa1SThomas Huth * Test that bdrv_replace_node, and concretely should_update_child
156da668aa1SThomas Huth * do the right thing, i.e. not creating loops on the graph.
157da668aa1SThomas Huth *
158da668aa1SThomas Huth * The test does the following:
159da668aa1SThomas Huth * 1. initial graph:
160da668aa1SThomas Huth *
161da668aa1SThomas Huth * +------+ +--------+
162da668aa1SThomas Huth * | root | | filter |
163da668aa1SThomas Huth * +------+ +--------+
164da668aa1SThomas Huth * | |
165da668aa1SThomas Huth * root| target|
166da668aa1SThomas Huth * v v
167da668aa1SThomas Huth * +------+ +--------+
168da668aa1SThomas Huth * | node |<---------| target |
169da668aa1SThomas Huth * +------+ backing +--------+
170da668aa1SThomas Huth *
171da668aa1SThomas Huth * 2. Append @filter above @node. If should_update_child works correctly,
172da668aa1SThomas Huth * it understands, that backing child of @target should not be updated,
173da668aa1SThomas Huth * as it will create a loop on node graph. Resulting picture should
174da668aa1SThomas Huth * be the left one, not the right:
175da668aa1SThomas Huth *
176da668aa1SThomas Huth * +------+ +------+
177da668aa1SThomas Huth * | root | | root |
178da668aa1SThomas Huth * +------+ +------+
179da668aa1SThomas Huth * | |
180da668aa1SThomas Huth * root| root|
181da668aa1SThomas Huth * v v
182da668aa1SThomas Huth * +--------+ target +--------+ target
183da668aa1SThomas Huth * | filter |--------------+ | filter |--------------+
184da668aa1SThomas Huth * +--------+ | +--------+ |
185da668aa1SThomas Huth * | | | ^ v
186da668aa1SThomas Huth * backing| | backing| | +--------+
187da668aa1SThomas Huth * v v | +-----------| target |
188da668aa1SThomas Huth * +------+ +--------+ v backing +--------+
189da668aa1SThomas Huth * | node |<---------| target | +------+
190da668aa1SThomas Huth * +------+ backing +--------+ | node |
191da668aa1SThomas Huth * +------+
192da668aa1SThomas Huth *
193da668aa1SThomas Huth * (good picture) (bad picture)
194da668aa1SThomas Huth *
195da668aa1SThomas Huth */
test_should_update_child(void)196da668aa1SThomas Huth static void test_should_update_child(void)
197da668aa1SThomas Huth {
198da668aa1SThomas Huth BlockBackend *root = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
199da668aa1SThomas Huth BlockDriverState *bs = no_perm_node("node");
200da668aa1SThomas Huth BlockDriverState *filter = no_perm_node("filter");
201da668aa1SThomas Huth BlockDriverState *target = no_perm_node("target");
202da668aa1SThomas Huth
203da668aa1SThomas Huth blk_insert_bs(root, bs, &error_abort);
204da668aa1SThomas Huth
205da668aa1SThomas Huth bdrv_set_backing_hd(target, bs, &error_abort);
206da668aa1SThomas Huth
207*6bc30f19SStefan Hajnoczi bdrv_graph_wrlock();
208004915a9SKevin Wolf g_assert(target->backing->bs == bs);
209da668aa1SThomas Huth bdrv_attach_child(filter, target, "target", &child_of_bds,
210da668aa1SThomas Huth BDRV_CHILD_DATA, &error_abort);
211*6bc30f19SStefan Hajnoczi bdrv_graph_wrunlock();
212da668aa1SThomas Huth bdrv_append(filter, bs, &error_abort);
213004915a9SKevin Wolf
214004915a9SKevin Wolf bdrv_graph_rdlock_main_loop();
215da668aa1SThomas Huth g_assert(target->backing->bs == bs);
216004915a9SKevin Wolf bdrv_graph_rdunlock_main_loop();
217da668aa1SThomas Huth
218ae9d4417SVladimir Sementsov-Ogievskiy bdrv_unref(filter);
219da668aa1SThomas Huth bdrv_unref(bs);
220da668aa1SThomas Huth blk_unref(root);
221da668aa1SThomas Huth }
222da668aa1SThomas Huth
223d71cc67dSVladimir Sementsov-Ogievskiy /*
224d71cc67dSVladimir Sementsov-Ogievskiy * test_parallel_exclusive_write
225d71cc67dSVladimir Sementsov-Ogievskiy *
226d71cc67dSVladimir Sementsov-Ogievskiy * Check that when we replace node, old permissions of the node being removed
227d71cc67dSVladimir Sementsov-Ogievskiy * doesn't break the replacement.
228d71cc67dSVladimir Sementsov-Ogievskiy */
test_parallel_exclusive_write(void)229d71cc67dSVladimir Sementsov-Ogievskiy static void test_parallel_exclusive_write(void)
230d71cc67dSVladimir Sementsov-Ogievskiy {
231d71cc67dSVladimir Sementsov-Ogievskiy BlockDriverState *top = exclusive_writer_node("top");
232d71cc67dSVladimir Sementsov-Ogievskiy BlockDriverState *base = no_perm_node("base");
233d71cc67dSVladimir Sementsov-Ogievskiy BlockDriverState *fl1 = pass_through_node("fl1");
234d71cc67dSVladimir Sementsov-Ogievskiy BlockDriverState *fl2 = pass_through_node("fl2");
235d71cc67dSVladimir Sementsov-Ogievskiy
236ccd6a379SKevin Wolf bdrv_drained_begin(fl1);
237ccd6a379SKevin Wolf bdrv_drained_begin(fl2);
238ccd6a379SKevin Wolf
239d71cc67dSVladimir Sementsov-Ogievskiy /*
240d71cc67dSVladimir Sementsov-Ogievskiy * bdrv_attach_child() eats child bs reference, so we need two @base
241ccd6a379SKevin Wolf * references for two filters. We also need an additional @fl1 reference so
242ccd6a379SKevin Wolf * that it still exists when we want to undrain it.
243d71cc67dSVladimir Sementsov-Ogievskiy */
244d71cc67dSVladimir Sementsov-Ogievskiy bdrv_ref(base);
245ccd6a379SKevin Wolf bdrv_ref(fl1);
246d71cc67dSVladimir Sementsov-Ogievskiy
247*6bc30f19SStefan Hajnoczi bdrv_graph_wrlock();
2481921b4f7SVladimir Sementsov-Ogievskiy bdrv_attach_child(top, fl1, "backing", &child_of_bds,
2491921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
250d71cc67dSVladimir Sementsov-Ogievskiy &error_abort);
2511921b4f7SVladimir Sementsov-Ogievskiy bdrv_attach_child(fl1, base, "backing", &child_of_bds,
2521921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
253d71cc67dSVladimir Sementsov-Ogievskiy &error_abort);
2541921b4f7SVladimir Sementsov-Ogievskiy bdrv_attach_child(fl2, base, "backing", &child_of_bds,
2551921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
256d71cc67dSVladimir Sementsov-Ogievskiy &error_abort);
257d71cc67dSVladimir Sementsov-Ogievskiy
258d71cc67dSVladimir Sementsov-Ogievskiy bdrv_replace_node(fl1, fl2, &error_abort);
259*6bc30f19SStefan Hajnoczi bdrv_graph_wrunlock();
260d71cc67dSVladimir Sementsov-Ogievskiy
261ccd6a379SKevin Wolf bdrv_drained_end(fl2);
262ccd6a379SKevin Wolf bdrv_drained_end(fl1);
263ccd6a379SKevin Wolf
264ccd6a379SKevin Wolf bdrv_unref(fl1);
265d71cc67dSVladimir Sementsov-Ogievskiy bdrv_unref(fl2);
266d71cc67dSVladimir Sementsov-Ogievskiy bdrv_unref(top);
267d71cc67dSVladimir Sementsov-Ogievskiy }
268d71cc67dSVladimir Sementsov-Ogievskiy
2691dcea719SVladimir Sementsov-Ogievskiy /*
2701dcea719SVladimir Sementsov-Ogievskiy * write-to-selected node may have several DATA children, one of them may be
2711dcea719SVladimir Sementsov-Ogievskiy * "selected". Exclusive write permission is taken on selected child.
2721dcea719SVladimir Sementsov-Ogievskiy *
2731dcea719SVladimir Sementsov-Ogievskiy * We don't realize write handler itself, as we need only to test how permission
2741dcea719SVladimir Sementsov-Ogievskiy * update works.
2751dcea719SVladimir Sementsov-Ogievskiy */
2761dcea719SVladimir Sementsov-Ogievskiy typedef struct BDRVWriteToSelectedState {
2771dcea719SVladimir Sementsov-Ogievskiy BdrvChild *selected;
2781dcea719SVladimir Sementsov-Ogievskiy } BDRVWriteToSelectedState;
2791dcea719SVladimir Sementsov-Ogievskiy
write_to_selected_perms(BlockDriverState * bs,BdrvChild * c,BdrvChildRole role,BlockReopenQueue * reopen_queue,uint64_t perm,uint64_t shared,uint64_t * nperm,uint64_t * nshared)2801dcea719SVladimir Sementsov-Ogievskiy static void write_to_selected_perms(BlockDriverState *bs, BdrvChild *c,
281e6af4f0eSVladimir Sementsov-Ogievskiy BdrvChildRole role,
282e6af4f0eSVladimir Sementsov-Ogievskiy BlockReopenQueue *reopen_queue,
283e6af4f0eSVladimir Sementsov-Ogievskiy uint64_t perm, uint64_t shared,
284e6af4f0eSVladimir Sementsov-Ogievskiy uint64_t *nperm, uint64_t *nshared)
285e6af4f0eSVladimir Sementsov-Ogievskiy {
2861dcea719SVladimir Sementsov-Ogievskiy BDRVWriteToSelectedState *s = bs->opaque;
2871dcea719SVladimir Sementsov-Ogievskiy
2881dcea719SVladimir Sementsov-Ogievskiy if (s->selected && c == s->selected) {
289e6af4f0eSVladimir Sementsov-Ogievskiy *nperm = BLK_PERM_WRITE;
290e6af4f0eSVladimir Sementsov-Ogievskiy *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
291e6af4f0eSVladimir Sementsov-Ogievskiy } else {
292e6af4f0eSVladimir Sementsov-Ogievskiy *nperm = 0;
293e6af4f0eSVladimir Sementsov-Ogievskiy *nshared = BLK_PERM_ALL;
294e6af4f0eSVladimir Sementsov-Ogievskiy }
295e6af4f0eSVladimir Sementsov-Ogievskiy }
296e6af4f0eSVladimir Sementsov-Ogievskiy
2971dcea719SVladimir Sementsov-Ogievskiy static BlockDriver bdrv_write_to_selected = {
2981dcea719SVladimir Sementsov-Ogievskiy .format_name = "write-to-selected",
2991dcea719SVladimir Sementsov-Ogievskiy .instance_size = sizeof(BDRVWriteToSelectedState),
3001dcea719SVladimir Sementsov-Ogievskiy .bdrv_child_perm = write_to_selected_perms,
301e6af4f0eSVladimir Sementsov-Ogievskiy };
302e6af4f0eSVladimir Sementsov-Ogievskiy
303e6af4f0eSVladimir Sementsov-Ogievskiy
304e6af4f0eSVladimir Sementsov-Ogievskiy /*
305e6af4f0eSVladimir Sementsov-Ogievskiy * The following test shows that topological-sort order is required for
306e6af4f0eSVladimir Sementsov-Ogievskiy * permission update, simple DFS is not enough.
307e6af4f0eSVladimir Sementsov-Ogievskiy *
3081dcea719SVladimir Sementsov-Ogievskiy * Consider the block driver (write-to-selected) which has two children: one is
3091dcea719SVladimir Sementsov-Ogievskiy * selected so we have exclusive write access to it and for the other one we
3101dcea719SVladimir Sementsov-Ogievskiy * don't need any specific permissions.
311e6af4f0eSVladimir Sementsov-Ogievskiy *
312e6af4f0eSVladimir Sementsov-Ogievskiy * And, these two children has a common base child, like this:
3131dcea719SVladimir Sementsov-Ogievskiy * (additional "top" on top is used in test just because the only public
3141dcea719SVladimir Sementsov-Ogievskiy * function to update permission should get a specific child to update.
3151dcea719SVladimir Sementsov-Ogievskiy * Making bdrv_refresh_perms() public just for this test isn't worth it)
316e6af4f0eSVladimir Sementsov-Ogievskiy *
3171dcea719SVladimir Sementsov-Ogievskiy * ┌─────┐ ┌───────────────────┐ ┌─────┐
3181dcea719SVladimir Sementsov-Ogievskiy * │ fl2 │ ◀── │ write-to-selected │ ◀── │ top │
3191dcea719SVladimir Sementsov-Ogievskiy * └─────┘ └───────────────────┘ └─────┘
320e6af4f0eSVladimir Sementsov-Ogievskiy * │ │
321e6af4f0eSVladimir Sementsov-Ogievskiy * │ │ w
322e6af4f0eSVladimir Sementsov-Ogievskiy * │ ▼
323e6af4f0eSVladimir Sementsov-Ogievskiy * │ ┌──────┐
324e6af4f0eSVladimir Sementsov-Ogievskiy * │ │ fl1 │
325e6af4f0eSVladimir Sementsov-Ogievskiy * │ └──────┘
326e6af4f0eSVladimir Sementsov-Ogievskiy * │ │
327e6af4f0eSVladimir Sementsov-Ogievskiy * │ │ w
328e6af4f0eSVladimir Sementsov-Ogievskiy * │ ▼
329e6af4f0eSVladimir Sementsov-Ogievskiy * │ ┌──────┐
330e6af4f0eSVladimir Sementsov-Ogievskiy * └───────▶ │ base │
331e6af4f0eSVladimir Sementsov-Ogievskiy * └──────┘
332e6af4f0eSVladimir Sementsov-Ogievskiy *
333e6af4f0eSVladimir Sementsov-Ogievskiy * So, exclusive write is propagated.
334e6af4f0eSVladimir Sementsov-Ogievskiy *
3351dcea719SVladimir Sementsov-Ogievskiy * Assume, we want to select fl2 instead of fl1.
3361dcea719SVladimir Sementsov-Ogievskiy * So, we set some option for write-to-selected driver and do permission update.
337e6af4f0eSVladimir Sementsov-Ogievskiy *
338e6af4f0eSVladimir Sementsov-Ogievskiy * With simple DFS, if permission update goes first through
3391dcea719SVladimir Sementsov-Ogievskiy * write-to-selected -> fl1 -> base branch it will succeed: it firstly drop
3401dcea719SVladimir Sementsov-Ogievskiy * exclusive write permissions and than apply them for another BdrvChildren.
3411dcea719SVladimir Sementsov-Ogievskiy * But if permission update goes first through write-to-selected -> fl2 -> base
3421dcea719SVladimir Sementsov-Ogievskiy * branch it will fail, as when we try to update fl2->base child, old not yet
343e6af4f0eSVladimir Sementsov-Ogievskiy * updated fl1->base child will be in conflict.
344e6af4f0eSVladimir Sementsov-Ogievskiy *
345e6af4f0eSVladimir Sementsov-Ogievskiy * With topological-sort order we always update parents before children, so fl1
346e6af4f0eSVladimir Sementsov-Ogievskiy * and fl2 are both updated when we update base and there is no conflict.
347e6af4f0eSVladimir Sementsov-Ogievskiy */
test_parallel_perm_update(void)348e6af4f0eSVladimir Sementsov-Ogievskiy static void test_parallel_perm_update(void)
349e6af4f0eSVladimir Sementsov-Ogievskiy {
350e6af4f0eSVladimir Sementsov-Ogievskiy BlockDriverState *top = no_perm_node("top");
3511dcea719SVladimir Sementsov-Ogievskiy BlockDriverState *ws =
3521dcea719SVladimir Sementsov-Ogievskiy bdrv_new_open_driver(&bdrv_write_to_selected, "ws", BDRV_O_RDWR,
353e6af4f0eSVladimir Sementsov-Ogievskiy &error_abort);
3541dcea719SVladimir Sementsov-Ogievskiy BDRVWriteToSelectedState *s = ws->opaque;
355e6af4f0eSVladimir Sementsov-Ogievskiy BlockDriverState *base = no_perm_node("base");
356e6af4f0eSVladimir Sementsov-Ogievskiy BlockDriverState *fl1 = pass_through_node("fl1");
357e6af4f0eSVladimir Sementsov-Ogievskiy BlockDriverState *fl2 = pass_through_node("fl2");
358e6af4f0eSVladimir Sementsov-Ogievskiy BdrvChild *c_fl1, *c_fl2;
359e6af4f0eSVladimir Sementsov-Ogievskiy
360e6af4f0eSVladimir Sementsov-Ogievskiy /*
361e6af4f0eSVladimir Sementsov-Ogievskiy * bdrv_attach_child() eats child bs reference, so we need two @base
362e6af4f0eSVladimir Sementsov-Ogievskiy * references for two filters:
363e6af4f0eSVladimir Sementsov-Ogievskiy */
364e6af4f0eSVladimir Sementsov-Ogievskiy bdrv_ref(base);
365e6af4f0eSVladimir Sementsov-Ogievskiy
366*6bc30f19SStefan Hajnoczi bdrv_graph_wrlock();
3671dcea719SVladimir Sementsov-Ogievskiy bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA,
368e6af4f0eSVladimir Sementsov-Ogievskiy &error_abort);
3691dcea719SVladimir Sementsov-Ogievskiy c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds,
3701dcea719SVladimir Sementsov-Ogievskiy BDRV_CHILD_DATA, &error_abort);
3711dcea719SVladimir Sementsov-Ogievskiy c_fl2 = bdrv_attach_child(ws, fl2, "second", &child_of_bds,
3721dcea719SVladimir Sementsov-Ogievskiy BDRV_CHILD_DATA, &error_abort);
3731921b4f7SVladimir Sementsov-Ogievskiy bdrv_attach_child(fl1, base, "backing", &child_of_bds,
3741921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
375e6af4f0eSVladimir Sementsov-Ogievskiy &error_abort);
3761921b4f7SVladimir Sementsov-Ogievskiy bdrv_attach_child(fl2, base, "backing", &child_of_bds,
3771921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
378e6af4f0eSVladimir Sementsov-Ogievskiy &error_abort);
379*6bc30f19SStefan Hajnoczi bdrv_graph_wrunlock();
380e6af4f0eSVladimir Sementsov-Ogievskiy
381e6af4f0eSVladimir Sementsov-Ogievskiy /* Select fl1 as first child to be active */
3821dcea719SVladimir Sementsov-Ogievskiy s->selected = c_fl1;
3833804e3cfSKevin Wolf
3843804e3cfSKevin Wolf bdrv_graph_rdlock_main_loop();
3853804e3cfSKevin Wolf
386e6af4f0eSVladimir Sementsov-Ogievskiy bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
387e6af4f0eSVladimir Sementsov-Ogievskiy
388e6af4f0eSVladimir Sementsov-Ogievskiy assert(c_fl1->perm & BLK_PERM_WRITE);
389e6af4f0eSVladimir Sementsov-Ogievskiy assert(!(c_fl2->perm & BLK_PERM_WRITE));
390e6af4f0eSVladimir Sementsov-Ogievskiy
391e6af4f0eSVladimir Sementsov-Ogievskiy /* Now, try to switch active child and update permissions */
3921dcea719SVladimir Sementsov-Ogievskiy s->selected = c_fl2;
393e6af4f0eSVladimir Sementsov-Ogievskiy bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
394e6af4f0eSVladimir Sementsov-Ogievskiy
395e6af4f0eSVladimir Sementsov-Ogievskiy assert(c_fl2->perm & BLK_PERM_WRITE);
396e6af4f0eSVladimir Sementsov-Ogievskiy assert(!(c_fl1->perm & BLK_PERM_WRITE));
397e6af4f0eSVladimir Sementsov-Ogievskiy
398e6af4f0eSVladimir Sementsov-Ogievskiy /* Switch once more, to not care about real child order in the list */
3991dcea719SVladimir Sementsov-Ogievskiy s->selected = c_fl1;
400e6af4f0eSVladimir Sementsov-Ogievskiy bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
401e6af4f0eSVladimir Sementsov-Ogievskiy
402e6af4f0eSVladimir Sementsov-Ogievskiy assert(c_fl1->perm & BLK_PERM_WRITE);
403e6af4f0eSVladimir Sementsov-Ogievskiy assert(!(c_fl2->perm & BLK_PERM_WRITE));
404e6af4f0eSVladimir Sementsov-Ogievskiy
4053804e3cfSKevin Wolf bdrv_graph_rdunlock_main_loop();
406e6af4f0eSVladimir Sementsov-Ogievskiy bdrv_unref(top);
407e6af4f0eSVladimir Sementsov-Ogievskiy }
408e6af4f0eSVladimir Sementsov-Ogievskiy
409397f7cc0SVladimir Sementsov-Ogievskiy /*
410397f7cc0SVladimir Sementsov-Ogievskiy * It's possible that filter required permissions allows to insert it to backing
411397f7cc0SVladimir Sementsov-Ogievskiy * chain, like:
412397f7cc0SVladimir Sementsov-Ogievskiy *
413397f7cc0SVladimir Sementsov-Ogievskiy * 1. [top] -> [filter] -> [base]
414397f7cc0SVladimir Sementsov-Ogievskiy *
415397f7cc0SVladimir Sementsov-Ogievskiy * but doesn't allow to add it as a branch:
416397f7cc0SVladimir Sementsov-Ogievskiy *
417397f7cc0SVladimir Sementsov-Ogievskiy * 2. [filter] --\
418397f7cc0SVladimir Sementsov-Ogievskiy * v
419397f7cc0SVladimir Sementsov-Ogievskiy * [top] -> [base]
420397f7cc0SVladimir Sementsov-Ogievskiy *
421397f7cc0SVladimir Sementsov-Ogievskiy * So, inserting such filter should do all graph modifications and only then
422397f7cc0SVladimir Sementsov-Ogievskiy * update permissions. If we try to go through intermediate state [2] and update
423397f7cc0SVladimir Sementsov-Ogievskiy * permissions on it we'll fail.
424397f7cc0SVladimir Sementsov-Ogievskiy *
425397f7cc0SVladimir Sementsov-Ogievskiy * Let's check that bdrv_append() can append such a filter.
426397f7cc0SVladimir Sementsov-Ogievskiy */
test_append_greedy_filter(void)427397f7cc0SVladimir Sementsov-Ogievskiy static void test_append_greedy_filter(void)
428397f7cc0SVladimir Sementsov-Ogievskiy {
429397f7cc0SVladimir Sementsov-Ogievskiy BlockDriverState *top = exclusive_writer_node("top");
430397f7cc0SVladimir Sementsov-Ogievskiy BlockDriverState *base = no_perm_node("base");
431397f7cc0SVladimir Sementsov-Ogievskiy BlockDriverState *fl = exclusive_writer_node("fl1");
432397f7cc0SVladimir Sementsov-Ogievskiy
433*6bc30f19SStefan Hajnoczi bdrv_graph_wrlock();
4341921b4f7SVladimir Sementsov-Ogievskiy bdrv_attach_child(top, base, "backing", &child_of_bds,
4351921b4f7SVladimir Sementsov-Ogievskiy BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
436397f7cc0SVladimir Sementsov-Ogievskiy &error_abort);
437*6bc30f19SStefan Hajnoczi bdrv_graph_wrunlock();
438397f7cc0SVladimir Sementsov-Ogievskiy
439397f7cc0SVladimir Sementsov-Ogievskiy bdrv_append(fl, base, &error_abort);
440ae9d4417SVladimir Sementsov-Ogievskiy bdrv_unref(fl);
441397f7cc0SVladimir Sementsov-Ogievskiy bdrv_unref(top);
442397f7cc0SVladimir Sementsov-Ogievskiy }
443397f7cc0SVladimir Sementsov-Ogievskiy
main(int argc,char * argv[])444da668aa1SThomas Huth int main(int argc, char *argv[])
445da668aa1SThomas Huth {
446da668aa1SThomas Huth bdrv_init();
447da668aa1SThomas Huth qemu_init_main_loop(&error_abort);
448da668aa1SThomas Huth
449da668aa1SThomas Huth g_test_init(&argc, &argv, NULL);
450da668aa1SThomas Huth
451da668aa1SThomas Huth g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
452da668aa1SThomas Huth g_test_add_func("/bdrv-graph-mod/should-update-child",
453da668aa1SThomas Huth test_should_update_child);
454bd57f8f7SVladimir Sementsov-Ogievskiy g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
455bd57f8f7SVladimir Sementsov-Ogievskiy test_parallel_perm_update);
456d71cc67dSVladimir Sementsov-Ogievskiy g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
457d71cc67dSVladimir Sementsov-Ogievskiy test_parallel_exclusive_write);
458397f7cc0SVladimir Sementsov-Ogievskiy g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
459397f7cc0SVladimir Sementsov-Ogievskiy test_append_greedy_filter);
460d71cc67dSVladimir Sementsov-Ogievskiy
461da668aa1SThomas Huth return g_test_run();
462da668aa1SThomas Huth }
463