xref: /openbmc/qemu/tests/unit/test-bdrv-graph-mod.c (revision 6370d13c62c300826f8eb80e4ed9d2e67bad3fa7)
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