|
@@ -241,13 +241,26 @@ 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)
|
|
|
+/*
|
|
|
+ * write-to-selected node may have several DATA children, one of them may be
|
|
|
+ * "selected". Exclusive write permission is taken on selected child.
|
|
|
+ *
|
|
|
+ * We don't realize write handler itself, as we need only to test how permission
|
|
|
+ * update works.
|
|
|
+ */
|
|
|
+typedef struct BDRVWriteToSelectedState {
|
|
|
+ BdrvChild *selected;
|
|
|
+} BDRVWriteToSelectedState;
|
|
|
+
|
|
|
+static void 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)
|
|
|
{
|
|
|
- if (bs->file && c == bs->file) {
|
|
|
+ BDRVWriteToSelectedState *s = bs->opaque;
|
|
|
+
|
|
|
+ if (s->selected && c == s->selected) {
|
|
|
*nperm = BLK_PERM_WRITE;
|
|
|
*nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
|
|
|
} else {
|
|
@@ -256,9 +269,10 @@ static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static BlockDriver bdrv_write_to_file = {
|
|
|
- .format_name = "tricky-perm",
|
|
|
- .bdrv_child_perm = write_to_file_perms,
|
|
|
+static BlockDriver bdrv_write_to_selected = {
|
|
|
+ .format_name = "write-to-selected",
|
|
|
+ .instance_size = sizeof(BDRVWriteToSelectedState),
|
|
|
+ .bdrv_child_perm = write_to_selected_perms,
|
|
|
};
|
|
|
|
|
|
|
|
@@ -266,15 +280,18 @@ static BlockDriver bdrv_write_to_file = {
|
|
|
* 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.
|
|
|
+ * Consider the block driver (write-to-selected) which has two children: one is
|
|
|
+ * selected so we have exclusive write access to it and for the other one we
|
|
|
+ * don't need any specific permissions.
|
|
|
*
|
|
|
* And, these two children has a common base child, like this:
|
|
|
+ * (additional "top" on top is used in test just because the only public
|
|
|
+ * function to update permission should get a specific child to update.
|
|
|
+ * Making bdrv_refresh_perms() public just for this test isn't worth it)
|
|
|
*
|
|
|
- * ┌─────┐ ┌──────┐
|
|
|
- * │ fl2 │ ◀── │ top │
|
|
|
- * └─────┘ └──────┘
|
|
|
+ * ┌─────┐ ┌───────────────────┐ ┌─────┐
|
|
|
+ * │ fl2 │ ◀── │ write-to-selected │ ◀── │ top │
|
|
|
+ * └─────┘ └───────────────────┘ └─────┘
|
|
|
* │ │
|
|
|
* │ │ w
|
|
|
* │ ▼
|
|
@@ -290,14 +307,14 @@ static BlockDriver bdrv_write_to_file = {
|
|
|
*
|
|
|
* 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.
|
|
|
+ * Assume, we want to select fl2 instead of fl1.
|
|
|
+ * So, we set some option for write-to-selected 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
|
|
|
+ * write-to-selected -> 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 write-to-selected -> 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
|
|
@@ -306,9 +323,10 @@ static BlockDriver bdrv_write_to_file = {
|
|
|
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,
|
|
|
+ BlockDriverState *ws =
|
|
|
+ bdrv_new_open_driver(&bdrv_write_to_selected, "ws", BDRV_O_RDWR,
|
|
|
&error_abort);
|
|
|
+ BDRVWriteToSelectedState *s = ws->opaque;
|
|
|
BlockDriverState *base = no_perm_node("base");
|
|
|
BlockDriverState *fl1 = pass_through_node("fl1");
|
|
|
BlockDriverState *fl2 = pass_through_node("fl2");
|
|
@@ -320,33 +338,33 @@ static void test_parallel_perm_update(void)
|
|
|
*/
|
|
|
bdrv_ref(base);
|
|
|
|
|
|
- bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA,
|
|
|
+ bdrv_attach_child(top, ws, "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);
|
|
|
+ c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds,
|
|
|
+ BDRV_CHILD_DATA, &error_abort);
|
|
|
+ c_fl2 = bdrv_attach_child(ws, fl2, "second", &child_of_bds,
|
|
|
+ BDRV_CHILD_DATA, &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;
|
|
|
+ s->selected = 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;
|
|
|
+ s->selected = 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;
|
|
|
+ s->selected = c_fl1;
|
|
|
bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
|
|
|
|
|
|
assert(c_fl1->perm & BLK_PERM_WRITE);
|