[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v4 02/36] tests/test-bdrv-graph-mod: add test_parallel_perm_updat
From: |
Vladimir Sementsov-Ogievskiy |
Subject: |
[PATCH v4 02/36] tests/test-bdrv-graph-mod: add test_parallel_perm_update |
Date: |
Wed, 28 Apr 2021 18:17:30 +0300 |
Add test to show that simple DFS recursion order is not correct for
permission update. Correct order is topological-sort order, which will
be introduced later.
Consider the block driver which has two filter children: one active
with exclusive write access and one inactive with no specific
permissions.
And, these two children has a common base child, like this:
┌─────┐ ┌──────┐
│ fl2 │ ◀── │ top │
└─────┘ └──────┘
│ │
│ │ w
│ ▼
│ ┌──────┐
│ │ fl1 │
│ └──────┘
│ │
│ │ w
│ ▼
│ ┌──────┐
└───────▶ │ base │
└──────┘
So, exclusive write is propagated.
Assume, we want to make fl2 active instead of fl1.
So, we set some option for top driver and do permission update.
If permission update (remember, it's DFS) goes first through
top->fl1->base branch it will succeed: it firstly drop exclusive write
permissions and than apply them for another BdrvChildren.
But if permission update goes first through top->fl2->base branch it
will fail, as when we try to update fl2->base child, old not yet
updated fl1->base child will be in conflict.
Now test fails, so it runs only with -d flag. To run do
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-perm-update
from <build-directory>/tests.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
---
tests/unit/test-bdrv-graph-mod.c | 116 +++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
index 80a9a20066..a8219b131e 100644
--- a/tests/unit/test-bdrv-graph-mod.c
+++ b/tests/unit/test-bdrv-graph-mod.c
@@ -238,6 +238,120 @@ static void test_parallel_exclusive_write(void)
bdrv_unref(top);
}
+static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ if (bs->file && c == bs->file) {
+ *nperm = BLK_PERM_WRITE;
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
+ } else {
+ *nperm = 0;
+ *nshared = BLK_PERM_ALL;
+ }
+}
+
+static BlockDriver bdrv_write_to_file = {
+ .format_name = "tricky-perm",
+ .bdrv_child_perm = write_to_file_perms,
+};
+
+
+/*
+ * The following test shows that topological-sort order is required for
+ * permission update, simple DFS is not enough.
+ *
+ * Consider the block driver which has two filter children: one active
+ * with exclusive write access and one inactive with no specific
+ * permissions.
+ *
+ * And, these two children has a common base child, like this:
+ *
+ * ┌─────┐ ┌──────┐
+ * │ fl2 │ ◀── │ top │
+ * └─────┘ └──────┘
+ * │ │
+ * │ │ w
+ * │ ▼
+ * │ ┌──────┐
+ * │ │ fl1 │
+ * │ └──────┘
+ * │ │
+ * │ │ w
+ * │ ▼
+ * │ ┌──────┐
+ * └───────▶ │ base │
+ * └──────┘
+ *
+ * So, exclusive write is propagated.
+ *
+ * Assume, we want to make fl2 active instead of fl1.
+ * So, we set some option for top driver and do permission update.
+ *
+ * With simple DFS, if permission update goes first through
+ * top->fl1->base branch it will succeed: it firstly drop exclusive write
+ * permissions and than apply them for another BdrvChildren.
+ * But if permission update goes first through top->fl2->base branch it
+ * will fail, as when we try to update fl2->base child, old not yet
+ * updated fl1->base child will be in conflict.
+ *
+ * With topological-sort order we always update parents before children, so fl1
+ * and fl2 are both updated when we update base and there is no conflict.
+ */
+static void test_parallel_perm_update(void)
+{
+ BlockDriverState *top = no_perm_node("top");
+ BlockDriverState *tricky =
+ bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR,
+ &error_abort);
+ BlockDriverState *base = no_perm_node("base");
+ BlockDriverState *fl1 = pass_through_node("fl1");
+ BlockDriverState *fl2 = pass_through_node("fl2");
+ BdrvChild *c_fl1, *c_fl2;
+
+ /*
+ * bdrv_attach_child() eats child bs reference, so we need two @base
+ * references for two filters:
+ */
+ bdrv_ref(base);
+
+ bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA,
+ &error_abort);
+ c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds,
+ BDRV_CHILD_FILTERED, &error_abort);
+ c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds,
+ BDRV_CHILD_FILTERED, &error_abort);
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ &error_abort);
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ &error_abort);
+
+ /* Select fl1 as first child to be active */
+ tricky->file = c_fl1;
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
+
+ assert(c_fl1->perm & BLK_PERM_WRITE);
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
+
+ /* Now, try to switch active child and update permissions */
+ tricky->file = c_fl2;
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
+
+ assert(c_fl2->perm & BLK_PERM_WRITE);
+ assert(!(c_fl1->perm & BLK_PERM_WRITE));
+
+ /* Switch once more, to not care about real child order in the list */
+ tricky->file = c_fl1;
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
+
+ assert(c_fl1->perm & BLK_PERM_WRITE);
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
+
+ bdrv_unref(top);
+}
+
int main(int argc, char *argv[])
{
int i;
@@ -262,6 +376,8 @@ int main(int argc, char *argv[])
if (debug) {
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
test_parallel_exclusive_write);
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
+ test_parallel_perm_update);
}
return g_test_run();
--
2.29.2
- [PATCH v4 00/36] block: update graph permissions update, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 01/36] tests/test-bdrv-graph-mod: add test_parallel_exclusive_write, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 13/36] block: use topological sort for permission update, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 12/36] block: inline bdrv_child_*() permission functions calls, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 03/36] tests/test-bdrv-graph-mod: add test_append_greedy_filter, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 02/36] tests/test-bdrv-graph-mod: add test_parallel_perm_update,
Vladimir Sementsov-Ogievskiy <=
- [PATCH v4 05/36] block: BdrvChildClass: add .get_parent_aio_context handler, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 14/36] block: add bdrv_drv_set_perm transaction action, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 06/36] block: drop ctx argument from bdrv_root_attach_child, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 16/36] block: add bdrv_replace_child_safe() transaction action, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 15/36] block: add bdrv_list_* permission update functions, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 08/36] util: add transactions.c, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 17/36] block: fix bdrv_replace_node_common, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 18/36] block: add bdrv_attach_child_common() transaction action, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 04/36] block: bdrv_append(): don't consume reference, Vladimir Sementsov-Ogievskiy, 2021/04/28
- [PATCH v4 09/36] block: bdrv_refresh_perms: check for parents permissions conflict, Vladimir Sementsov-Ogievskiy, 2021/04/28