test-bdrv-graph-mod.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. * Block node graph modifications tests
  3. *
  4. * Copyright (c) 2019-2021 Virtuozzo International GmbH. All rights reserved.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "qemu/osdep.h"
  21. #include "qapi/error.h"
  22. #include "qemu/main-loop.h"
  23. #include "block/block_int.h"
  24. #include "sysemu/block-backend.h"
  25. static BlockDriver bdrv_pass_through = {
  26. .format_name = "pass-through",
  27. .is_filter = true,
  28. .filtered_child_is_backing = true,
  29. .bdrv_child_perm = bdrv_default_perms,
  30. };
  31. static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c,
  32. BdrvChildRole role,
  33. BlockReopenQueue *reopen_queue,
  34. uint64_t perm, uint64_t shared,
  35. uint64_t *nperm, uint64_t *nshared)
  36. {
  37. *nperm = 0;
  38. *nshared = BLK_PERM_ALL;
  39. }
  40. static BlockDriver bdrv_no_perm = {
  41. .format_name = "no-perm",
  42. .supports_backing = true,
  43. .bdrv_child_perm = no_perm_default_perms,
  44. };
  45. static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c,
  46. BdrvChildRole role,
  47. BlockReopenQueue *reopen_queue,
  48. uint64_t perm, uint64_t shared,
  49. uint64_t *nperm, uint64_t *nshared)
  50. {
  51. *nperm = BLK_PERM_WRITE;
  52. *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
  53. }
  54. static BlockDriver bdrv_exclusive_writer = {
  55. .format_name = "exclusive-writer",
  56. .is_filter = true,
  57. .filtered_child_is_backing = true,
  58. .bdrv_child_perm = exclusive_write_perms,
  59. };
  60. static BlockDriverState *no_perm_node(const char *name)
  61. {
  62. return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
  63. }
  64. static BlockDriverState *pass_through_node(const char *name)
  65. {
  66. return bdrv_new_open_driver(&bdrv_pass_through, name,
  67. BDRV_O_RDWR, &error_abort);
  68. }
  69. static BlockDriverState *exclusive_writer_node(const char *name)
  70. {
  71. return bdrv_new_open_driver(&bdrv_exclusive_writer, name,
  72. BDRV_O_RDWR, &error_abort);
  73. }
  74. /*
  75. * test_update_perm_tree
  76. *
  77. * When checking node for a possibility to update permissions, it's subtree
  78. * should be correctly checked too. New permissions for each node should be
  79. * calculated and checked in context of permissions of other nodes. If we
  80. * check new permissions of the node only in context of old permissions of
  81. * its neighbors, we can finish up with wrong permission graph.
  82. *
  83. * This test firstly create the following graph:
  84. * +--------+
  85. * | root |
  86. * +--------+
  87. * |
  88. * | perm: write, read
  89. * | shared: except write
  90. * v
  91. * +--------------------+ +----------------+
  92. * | passthrough filter |--------->| null-co node |
  93. * +--------------------+ +----------------+
  94. *
  95. *
  96. * and then, tries to append filter under node. Expected behavior: fail.
  97. * Otherwise we'll get the following picture, with two BdrvChild'ren, having
  98. * write permission to one node, without actually sharing it.
  99. *
  100. * +--------+
  101. * | root |
  102. * +--------+
  103. * |
  104. * | perm: write, read
  105. * | shared: except write
  106. * v
  107. * +--------------------+
  108. * | passthrough filter |
  109. * +--------------------+
  110. * | |
  111. * perm: write, read | | perm: write, read
  112. * shared: except write | | shared: except write
  113. * v v
  114. * +----------------+
  115. * | null co node |
  116. * +----------------+
  117. */
  118. static void test_update_perm_tree(void)
  119. {
  120. int ret;
  121. BlockBackend *root = blk_new(qemu_get_aio_context(),
  122. BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ,
  123. BLK_PERM_ALL & ~BLK_PERM_WRITE);
  124. BlockDriverState *bs = no_perm_node("node");
  125. BlockDriverState *filter = pass_through_node("filter");
  126. blk_insert_bs(root, bs, &error_abort);
  127. bdrv_graph_wrlock();
  128. bdrv_attach_child(filter, bs, "child", &child_of_bds,
  129. BDRV_CHILD_DATA, &error_abort);
  130. bdrv_graph_wrunlock();
  131. ret = bdrv_append(filter, bs, NULL);
  132. g_assert_cmpint(ret, <, 0);
  133. bdrv_unref(filter);
  134. blk_unref(root);
  135. }
  136. /*
  137. * test_should_update_child
  138. *
  139. * Test that bdrv_replace_node, and concretely should_update_child
  140. * do the right thing, i.e. not creating loops on the graph.
  141. *
  142. * The test does the following:
  143. * 1. initial graph:
  144. *
  145. * +------+ +--------+
  146. * | root | | filter |
  147. * +------+ +--------+
  148. * | |
  149. * root| target|
  150. * v v
  151. * +------+ +--------+
  152. * | node |<---------| target |
  153. * +------+ backing +--------+
  154. *
  155. * 2. Append @filter above @node. If should_update_child works correctly,
  156. * it understands, that backing child of @target should not be updated,
  157. * as it will create a loop on node graph. Resulting picture should
  158. * be the left one, not the right:
  159. *
  160. * +------+ +------+
  161. * | root | | root |
  162. * +------+ +------+
  163. * | |
  164. * root| root|
  165. * v v
  166. * +--------+ target +--------+ target
  167. * | filter |--------------+ | filter |--------------+
  168. * +--------+ | +--------+ |
  169. * | | | ^ v
  170. * backing| | backing| | +--------+
  171. * v v | +-----------| target |
  172. * +------+ +--------+ v backing +--------+
  173. * | node |<---------| target | +------+
  174. * +------+ backing +--------+ | node |
  175. * +------+
  176. *
  177. * (good picture) (bad picture)
  178. *
  179. */
  180. static void test_should_update_child(void)
  181. {
  182. BlockBackend *root = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
  183. BlockDriverState *bs = no_perm_node("node");
  184. BlockDriverState *filter = no_perm_node("filter");
  185. BlockDriverState *target = no_perm_node("target");
  186. blk_insert_bs(root, bs, &error_abort);
  187. bdrv_set_backing_hd(target, bs, &error_abort);
  188. bdrv_graph_wrlock();
  189. g_assert(target->backing->bs == bs);
  190. bdrv_attach_child(filter, target, "target", &child_of_bds,
  191. BDRV_CHILD_DATA, &error_abort);
  192. bdrv_graph_wrunlock();
  193. bdrv_append(filter, bs, &error_abort);
  194. bdrv_graph_rdlock_main_loop();
  195. g_assert(target->backing->bs == bs);
  196. bdrv_graph_rdunlock_main_loop();
  197. bdrv_unref(filter);
  198. bdrv_unref(bs);
  199. blk_unref(root);
  200. }
  201. /*
  202. * test_parallel_exclusive_write
  203. *
  204. * Check that when we replace node, old permissions of the node being removed
  205. * doesn't break the replacement.
  206. */
  207. static void test_parallel_exclusive_write(void)
  208. {
  209. BlockDriverState *top = exclusive_writer_node("top");
  210. BlockDriverState *base = no_perm_node("base");
  211. BlockDriverState *fl1 = pass_through_node("fl1");
  212. BlockDriverState *fl2 = pass_through_node("fl2");
  213. bdrv_drained_begin(fl1);
  214. bdrv_drained_begin(fl2);
  215. /*
  216. * bdrv_attach_child() eats child bs reference, so we need two @base
  217. * references for two filters. We also need an additional @fl1 reference so
  218. * that it still exists when we want to undrain it.
  219. */
  220. bdrv_ref(base);
  221. bdrv_ref(fl1);
  222. bdrv_graph_wrlock();
  223. bdrv_attach_child(top, fl1, "backing", &child_of_bds,
  224. BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  225. &error_abort);
  226. bdrv_attach_child(fl1, base, "backing", &child_of_bds,
  227. BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  228. &error_abort);
  229. bdrv_attach_child(fl2, base, "backing", &child_of_bds,
  230. BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  231. &error_abort);
  232. bdrv_replace_node(fl1, fl2, &error_abort);
  233. bdrv_graph_wrunlock();
  234. bdrv_drained_end(fl2);
  235. bdrv_drained_end(fl1);
  236. bdrv_unref(fl1);
  237. bdrv_unref(fl2);
  238. bdrv_unref(top);
  239. }
  240. /*
  241. * write-to-selected node may have several DATA children, one of them may be
  242. * "selected". Exclusive write permission is taken on selected child.
  243. *
  244. * We don't realize write handler itself, as we need only to test how permission
  245. * update works.
  246. */
  247. typedef struct BDRVWriteToSelectedState {
  248. BdrvChild *selected;
  249. } BDRVWriteToSelectedState;
  250. static void write_to_selected_perms(BlockDriverState *bs, BdrvChild *c,
  251. BdrvChildRole role,
  252. BlockReopenQueue *reopen_queue,
  253. uint64_t perm, uint64_t shared,
  254. uint64_t *nperm, uint64_t *nshared)
  255. {
  256. BDRVWriteToSelectedState *s = bs->opaque;
  257. if (s->selected && c == s->selected) {
  258. *nperm = BLK_PERM_WRITE;
  259. *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
  260. } else {
  261. *nperm = 0;
  262. *nshared = BLK_PERM_ALL;
  263. }
  264. }
  265. static BlockDriver bdrv_write_to_selected = {
  266. .format_name = "write-to-selected",
  267. .instance_size = sizeof(BDRVWriteToSelectedState),
  268. .bdrv_child_perm = write_to_selected_perms,
  269. };
  270. /*
  271. * The following test shows that topological-sort order is required for
  272. * permission update, simple DFS is not enough.
  273. *
  274. * Consider the block driver (write-to-selected) which has two children: one is
  275. * selected so we have exclusive write access to it and for the other one we
  276. * don't need any specific permissions.
  277. *
  278. * And, these two children has a common base child, like this:
  279. * (additional "top" on top is used in test just because the only public
  280. * function to update permission should get a specific child to update.
  281. * Making bdrv_refresh_perms() public just for this test isn't worth it)
  282. *
  283. * ┌─────┐ ┌───────────────────┐ ┌─────┐
  284. * │ fl2 │ ◀── │ write-to-selected │ ◀── │ top │
  285. * └─────┘ └───────────────────┘ └─────┘
  286. * │ │
  287. * │ │ w
  288. * │ ▼
  289. * │ ┌──────┐
  290. * │ │ fl1 │
  291. * │ └──────┘
  292. * │ │
  293. * │ │ w
  294. * │ ▼
  295. * │ ┌──────┐
  296. * └───────▶ │ base │
  297. * └──────┘
  298. *
  299. * So, exclusive write is propagated.
  300. *
  301. * Assume, we want to select fl2 instead of fl1.
  302. * So, we set some option for write-to-selected driver and do permission update.
  303. *
  304. * With simple DFS, if permission update goes first through
  305. * write-to-selected -> fl1 -> base branch it will succeed: it firstly drop
  306. * exclusive write permissions and than apply them for another BdrvChildren.
  307. * But if permission update goes first through write-to-selected -> fl2 -> base
  308. * branch it will fail, as when we try to update fl2->base child, old not yet
  309. * updated fl1->base child will be in conflict.
  310. *
  311. * With topological-sort order we always update parents before children, so fl1
  312. * and fl2 are both updated when we update base and there is no conflict.
  313. */
  314. static void test_parallel_perm_update(void)
  315. {
  316. BlockDriverState *top = no_perm_node("top");
  317. BlockDriverState *ws =
  318. bdrv_new_open_driver(&bdrv_write_to_selected, "ws", BDRV_O_RDWR,
  319. &error_abort);
  320. BDRVWriteToSelectedState *s = ws->opaque;
  321. BlockDriverState *base = no_perm_node("base");
  322. BlockDriverState *fl1 = pass_through_node("fl1");
  323. BlockDriverState *fl2 = pass_through_node("fl2");
  324. BdrvChild *c_fl1, *c_fl2;
  325. /*
  326. * bdrv_attach_child() eats child bs reference, so we need two @base
  327. * references for two filters:
  328. */
  329. bdrv_ref(base);
  330. bdrv_graph_wrlock();
  331. bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA,
  332. &error_abort);
  333. c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds,
  334. BDRV_CHILD_DATA, &error_abort);
  335. c_fl2 = bdrv_attach_child(ws, fl2, "second", &child_of_bds,
  336. BDRV_CHILD_DATA, &error_abort);
  337. bdrv_attach_child(fl1, base, "backing", &child_of_bds,
  338. BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  339. &error_abort);
  340. bdrv_attach_child(fl2, base, "backing", &child_of_bds,
  341. BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  342. &error_abort);
  343. bdrv_graph_wrunlock();
  344. /* Select fl1 as first child to be active */
  345. s->selected = c_fl1;
  346. bdrv_graph_rdlock_main_loop();
  347. bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
  348. assert(c_fl1->perm & BLK_PERM_WRITE);
  349. assert(!(c_fl2->perm & BLK_PERM_WRITE));
  350. /* Now, try to switch active child and update permissions */
  351. s->selected = c_fl2;
  352. bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
  353. assert(c_fl2->perm & BLK_PERM_WRITE);
  354. assert(!(c_fl1->perm & BLK_PERM_WRITE));
  355. /* Switch once more, to not care about real child order in the list */
  356. s->selected = c_fl1;
  357. bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
  358. assert(c_fl1->perm & BLK_PERM_WRITE);
  359. assert(!(c_fl2->perm & BLK_PERM_WRITE));
  360. bdrv_graph_rdunlock_main_loop();
  361. bdrv_unref(top);
  362. }
  363. /*
  364. * It's possible that filter required permissions allows to insert it to backing
  365. * chain, like:
  366. *
  367. * 1. [top] -> [filter] -> [base]
  368. *
  369. * but doesn't allow to add it as a branch:
  370. *
  371. * 2. [filter] --\
  372. * v
  373. * [top] -> [base]
  374. *
  375. * So, inserting such filter should do all graph modifications and only then
  376. * update permissions. If we try to go through intermediate state [2] and update
  377. * permissions on it we'll fail.
  378. *
  379. * Let's check that bdrv_append() can append such a filter.
  380. */
  381. static void test_append_greedy_filter(void)
  382. {
  383. BlockDriverState *top = exclusive_writer_node("top");
  384. BlockDriverState *base = no_perm_node("base");
  385. BlockDriverState *fl = exclusive_writer_node("fl1");
  386. bdrv_graph_wrlock();
  387. bdrv_attach_child(top, base, "backing", &child_of_bds,
  388. BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  389. &error_abort);
  390. bdrv_graph_wrunlock();
  391. bdrv_append(fl, base, &error_abort);
  392. bdrv_unref(fl);
  393. bdrv_unref(top);
  394. }
  395. int main(int argc, char *argv[])
  396. {
  397. bdrv_init();
  398. qemu_init_main_loop(&error_abort);
  399. g_test_init(&argc, &argv, NULL);
  400. g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
  401. g_test_add_func("/bdrv-graph-mod/should-update-child",
  402. test_should_update_child);
  403. g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
  404. test_parallel_perm_update);
  405. g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
  406. test_parallel_exclusive_write);
  407. g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
  408. test_append_greedy_filter);
  409. return g_test_run();
  410. }