summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergio Lopez2021-02-01 13:50:31 +0100
committerKevin Wolf2021-02-02 13:23:47 +0100
commit722d8e73d65cb54f39d360ecb2147ac58f43c399 (patch)
tree398b0df1336f93e5b9892307ee6aee916e9822ab
parentMAINTAINERS: Add Vladimir as co-maintainer for Block Jobs (diff)
downloadqemu-722d8e73d65cb54f39d360ecb2147ac58f43c399.tar.gz
qemu-722d8e73d65cb54f39d360ecb2147ac58f43c399.tar.xz
qemu-722d8e73d65cb54f39d360ecb2147ac58f43c399.zip
block: Avoid processing BDS twice in bdrv_set_aio_context_ignore()
Some graphs may contain an indirect reference to the first BDS in the chain that can be reached while walking it bottom->up from one its children. Doubling-processing of a BDS is especially problematic for the aio_notifiers, as they might attempt to work on both the old and the new AIO contexts. To avoid this problem, add every child and parent to the ignore list before actually processing them. Suggested-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Sergio Lopez <slp@redhat.com> Message-Id: <20210201125032.44713-2-slp@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-rw-r--r--block.c34
1 files changed, 27 insertions, 7 deletions
diff --git a/block.c b/block.c
index 91a66d4f3e..5c428e1595 100644
--- a/block.c
+++ b/block.c
@@ -6439,7 +6439,10 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs,
AioContext *new_context, GSList **ignore)
{
AioContext *old_context = bdrv_get_aio_context(bs);
- BdrvChild *child;
+ GSList *children_to_process = NULL;
+ GSList *parents_to_process = NULL;
+ GSList *entry;
+ BdrvChild *child, *parent;
g_assert(qemu_get_current_aio_context() == qemu_get_aio_context());
@@ -6454,16 +6457,33 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs,
continue;
}
*ignore = g_slist_prepend(*ignore, child);
- bdrv_set_aio_context_ignore(child->bs, new_context, ignore);
+ children_to_process = g_slist_prepend(children_to_process, child);
}
- QLIST_FOREACH(child, &bs->parents, next_parent) {
- if (g_slist_find(*ignore, child)) {
+
+ QLIST_FOREACH(parent, &bs->parents, next_parent) {
+ if (g_slist_find(*ignore, parent)) {
continue;
}
- assert(child->klass->set_aio_ctx);
- *ignore = g_slist_prepend(*ignore, child);
- child->klass->set_aio_ctx(child, new_context, ignore);
+ *ignore = g_slist_prepend(*ignore, parent);
+ parents_to_process = g_slist_prepend(parents_to_process, parent);
+ }
+
+ for (entry = children_to_process;
+ entry != NULL;
+ entry = g_slist_next(entry)) {
+ child = entry->data;
+ bdrv_set_aio_context_ignore(child->bs, new_context, ignore);
+ }
+ g_slist_free(children_to_process);
+
+ for (entry = parents_to_process;
+ entry != NULL;
+ entry = g_slist_next(entry)) {
+ parent = entry->data;
+ assert(parent->klass->set_aio_ctx);
+ parent->klass->set_aio_ctx(parent, new_context, ignore);
}
+ g_slist_free(parents_to_process);
bdrv_detach_aio_context(bs);