- kvm-migration-multifd-move-macros-to-multifd-header.patch [RHEL-59697] - kvm-migration-refactor-channel-discovery-mechanism.patch [RHEL-59697] - kvm-migration-Add-save_postcopy_prepare-savevm-handler.patch [RHEL-59697] - kvm-migration-ram-Implement-save_postcopy_prepare.patch [RHEL-59697] - kvm-tests-qtest-migration-consolidate-set-capabilities.patch [RHEL-59697] - kvm-migration-write-zero-pages-when-postcopy-enabled.patch [RHEL-59697] - kvm-migration-enable-multifd-and-postcopy-together.patch [RHEL-59697] - kvm-migration-Add-qtest-for-migration-over-RDMA.patch [RHEL-59697] - kvm-qtest-migration-rdma-Enforce-RLIMIT_MEMLOCK-128MB-re.patch [RHEL-59697] - kvm-qtest-migration-rdma-Add-test-for-rdma-migration-wit.patch [RHEL-59697] - kvm-tests-qtest-migration-add-postcopy-tests-with-multif.patch [RHEL-59697] - kvm-file-posix-Fix-aio-threads-performance-regression-af.patch [RHEL-96854] - kvm-block-remove-outdated-comments-about-AioContext-lock.patch [RHEL-88561] - kvm-block-move-drain-outside-of-read-locked-bdrv_reopen_.patch [RHEL-88561] - kvm-block-snapshot-move-drain-outside-of-read-locked-bdr.patch [RHEL-88561] - kvm-block-move-drain-outside-of-read-locked-bdrv_inactiv.patch [RHEL-88561] - kvm-block-mark-bdrv_parent_change_aio_context-GRAPH_RDLO.patch [RHEL-88561] - kvm-block-mark-change_aio_ctx-callback-and-instances-as-.patch [RHEL-88561] - kvm-block-mark-bdrv_child_change_aio_context-GRAPH_RDLOC.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_change_aio_context-.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_try_change_aio_cont.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_attach_child_common.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_set_backing_hd_drai.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_root_attach_child.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_attach_child.patch [RHEL-88561] - kvm-block-move-drain-outside-of-quorum_add_child.patch [RHEL-88561] - kvm-block-move-drain-outside-of-bdrv_root_unref_child.patch [RHEL-88561] - kvm-block-move-drain-outside-of-quorum_del_child.patch [RHEL-88561] - kvm-blockdev-drain-while-unlocked-in-internal_snapshot_a.patch [RHEL-88561] - kvm-blockdev-drain-while-unlocked-in-external_snapshot_a.patch [RHEL-88561] - kvm-block-mark-bdrv_drained_begin-and-friends-as-GRAPH_U.patch [RHEL-88561] - kvm-iotests-graph-changes-while-io-remove-image-file-aft.patch [RHEL-88561] - kvm-iotests-graph-changes-while-io-add-test-case-with-re.patch [RHEL-88561] - Resolves: RHEL-59697 (Allow multifd+postcopy features being enabled together, but only use multifd during precopy ) - Resolves: RHEL-96854 (Performance Degradation(aio=threads) between Upstream Commit b75c5f9 and 984a32f) - Resolves: RHEL-88561 (qemu graph deadlock during job-dismiss)
258 lines
9.9 KiB
Diff
258 lines
9.9 KiB
Diff
From 0fc67b52f8d95a10d0bbc1d49eaf2f93c603d967 Mon Sep 17 00:00:00 2001
|
|
From: Fiona Ebner <f.ebner@proxmox.com>
|
|
Date: Fri, 30 May 2025 17:10:46 +0200
|
|
Subject: [PATCH 21/33] block: move drain outside of
|
|
bdrv_try_change_aio_context()
|
|
|
|
RH-Author: Kevin Wolf <kwolf@redhat.com>
|
|
RH-MergeRequest: 393: block: do not drain while holding the graph lock
|
|
RH-Jira: RHEL-88561
|
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
|
|
RH-Commit: [9/21] 9e421c675137364d37f9694b7df6ed5c434f20c6 (kmwolf/centos-qemu-kvm)
|
|
|
|
This is part of resolving the deadlock mentioned in commit "block:
|
|
move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK".
|
|
|
|
Convert the function to a _locked() version that has to be called with
|
|
the graph lock held and add a convenience wrapper that has to be
|
|
called with the graph unlocked, which drains and takes the lock
|
|
itself. Since bdrv_try_change_aio_context() is global state code, the
|
|
wrapper is too.
|
|
|
|
Callers are adapted to use the appropriate variant, depending on
|
|
whether the caller already holds the lock. In the
|
|
test_set_aio_context() unit test, prior drains can be removed, because
|
|
draining already happens inside the new wrapper.
|
|
|
|
Note that bdrv_attach_child_common_abort(), bdrv_attach_child_common()
|
|
and bdrv_root_unref_child() hold the graph lock and are not actually
|
|
allowed to drain either. This will be addressed in the following
|
|
commits.
|
|
|
|
Functions like qmp_blockdev_mirror() query the nodes to act on before
|
|
draining and locking. In theory, draining could invalidate those nodes.
|
|
This kind of issue is not addressed by these commits.
|
|
|
|
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
|
|
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
|
Message-ID: <20250530151125.955508-10-f.ebner@proxmox.com>
|
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
|
(cherry picked from commit a1ea8eb5912256c0b2be16fae5d3786aebc80cb1)
|
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
|
---
|
|
block.c | 58 ++++++++++++++++++++++--------
|
|
blockdev.c | 15 +++++---
|
|
include/block/block-global-state.h | 8 +++--
|
|
tests/unit/test-bdrv-drain.c | 4 ---
|
|
4 files changed, 59 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/block.c b/block.c
|
|
index af438ae7ff..3c2e8c5592 100644
|
|
--- a/block.c
|
|
+++ b/block.c
|
|
@@ -3028,7 +3028,10 @@ static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque)
|
|
bdrv_replace_child_noperm(s->child, NULL);
|
|
|
|
if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
|
|
- bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort);
|
|
+ bdrv_drain_all_begin();
|
|
+ bdrv_try_change_aio_context_locked(bs, s->old_child_ctx, NULL,
|
|
+ &error_abort);
|
|
+ bdrv_drain_all_end();
|
|
}
|
|
|
|
if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) {
|
|
@@ -3115,8 +3118,10 @@ bdrv_attach_child_common(BlockDriverState *child_bs,
|
|
parent_ctx = bdrv_child_get_parent_aio_context(new_child);
|
|
if (child_ctx != parent_ctx) {
|
|
Error *local_err = NULL;
|
|
- int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL,
|
|
- &local_err);
|
|
+ bdrv_drain_all_begin();
|
|
+ int ret = bdrv_try_change_aio_context_locked(child_bs, parent_ctx, NULL,
|
|
+ &local_err);
|
|
+ bdrv_drain_all_end();
|
|
|
|
if (ret < 0 && child_class->change_aio_ctx) {
|
|
Transaction *aio_ctx_tran = tran_new();
|
|
@@ -3319,8 +3324,10 @@ void bdrv_root_unref_child(BdrvChild *child)
|
|
* When the parent requiring a non-default AioContext is removed, the
|
|
* node moves back to the main AioContext
|
|
*/
|
|
- bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL,
|
|
- NULL);
|
|
+ bdrv_drain_all_begin();
|
|
+ bdrv_try_change_aio_context_locked(child_bs, qemu_get_aio_context(),
|
|
+ NULL, NULL);
|
|
+ bdrv_drain_all_end();
|
|
}
|
|
|
|
bdrv_schedule_unref(child_bs);
|
|
@@ -7719,9 +7726,13 @@ bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
*
|
|
* If ignore_child is not NULL, that child (and its subgraph) will not
|
|
* be touched.
|
|
+ *
|
|
+ * Called with the graph lock held.
|
|
+ *
|
|
+ * Called while all bs are drained.
|
|
*/
|
|
-int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
- BdrvChild *ignore_child, Error **errp)
|
|
+int bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx,
|
|
+ BdrvChild *ignore_child, Error **errp)
|
|
{
|
|
Transaction *tran;
|
|
GHashTable *visited;
|
|
@@ -7730,17 +7741,15 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
|
|
/*
|
|
* Recursion phase: go through all nodes of the graph.
|
|
- * Take care of checking that all nodes support changing AioContext
|
|
- * and drain them, building a linear list of callbacks to run if everything
|
|
- * is successful (the transaction itself).
|
|
+ * Take care of checking that all nodes support changing AioContext,
|
|
+ * building a linear list of callbacks to run if everything is successful
|
|
+ * (the transaction itself).
|
|
*/
|
|
tran = tran_new();
|
|
visited = g_hash_table_new(NULL, NULL);
|
|
if (ignore_child) {
|
|
g_hash_table_add(visited, ignore_child);
|
|
}
|
|
- bdrv_drain_all_begin();
|
|
- bdrv_graph_rdlock_main_loop();
|
|
ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp);
|
|
g_hash_table_destroy(visited);
|
|
|
|
@@ -7754,15 +7763,34 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
if (!ret) {
|
|
/* Just run clean() callbacks. No AioContext changed. */
|
|
tran_abort(tran);
|
|
- bdrv_graph_rdunlock_main_loop();
|
|
- bdrv_drain_all_end();
|
|
return -EPERM;
|
|
}
|
|
|
|
tran_commit(tran);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Change bs's and recursively all of its parents' and children's AioContext
|
|
+ * to the given new context, returning an error if that isn't possible.
|
|
+ *
|
|
+ * If ignore_child is not NULL, that child (and its subgraph) will not
|
|
+ * be touched.
|
|
+ */
|
|
+int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
+ BdrvChild *ignore_child, Error **errp)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ GLOBAL_STATE_CODE();
|
|
+
|
|
+ bdrv_drain_all_begin();
|
|
+ bdrv_graph_rdlock_main_loop();
|
|
+ ret = bdrv_try_change_aio_context_locked(bs, ctx, ignore_child, errp);
|
|
bdrv_graph_rdunlock_main_loop();
|
|
bdrv_drain_all_end();
|
|
- return 0;
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
void bdrv_add_aio_context_notifier(BlockDriverState *bs,
|
|
diff --git a/blockdev.c b/blockdev.c
|
|
index efa7d1d0b2..41b6481c9a 100644
|
|
--- a/blockdev.c
|
|
+++ b/blockdev.c
|
|
@@ -3595,12 +3595,13 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
|
AioContext *new_context;
|
|
BlockDriverState *bs;
|
|
|
|
- GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
+ bdrv_drain_all_begin();
|
|
+ bdrv_graph_rdlock_main_loop();
|
|
|
|
bs = bdrv_find_node(node_name);
|
|
if (!bs) {
|
|
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
|
|
- return;
|
|
+ goto out;
|
|
}
|
|
|
|
/* Protects against accidents. */
|
|
@@ -3608,14 +3609,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
|
error_setg(errp, "Node %s is associated with a BlockBackend and could "
|
|
"be in use (use force=true to override this check)",
|
|
node_name);
|
|
- return;
|
|
+ goto out;
|
|
}
|
|
|
|
if (iothread->type == QTYPE_QSTRING) {
|
|
IOThread *obj = iothread_by_id(iothread->u.s);
|
|
if (!obj) {
|
|
error_setg(errp, "Cannot find iothread %s", iothread->u.s);
|
|
- return;
|
|
+ goto out;
|
|
}
|
|
|
|
new_context = iothread_get_aio_context(obj);
|
|
@@ -3623,7 +3624,11 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
|
new_context = qemu_get_aio_context();
|
|
}
|
|
|
|
- bdrv_try_change_aio_context(bs, new_context, NULL, errp);
|
|
+ bdrv_try_change_aio_context_locked(bs, new_context, NULL, errp);
|
|
+
|
|
+out:
|
|
+ bdrv_graph_rdunlock_main_loop();
|
|
+ bdrv_drain_all_end();
|
|
}
|
|
|
|
QemuOptsList qemu_common_drive_opts = {
|
|
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
|
|
index aad160956a..91f249b5ad 100644
|
|
--- a/include/block/block-global-state.h
|
|
+++ b/include/block/block-global-state.h
|
|
@@ -278,8 +278,12 @@ bool GRAPH_RDLOCK
|
|
bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
|
|
GHashTable *visited, Transaction *tran,
|
|
Error **errp);
|
|
-int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
- BdrvChild *ignore_child, Error **errp);
|
|
+int GRAPH_UNLOCKED
|
|
+bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
|
|
+ BdrvChild *ignore_child, Error **errp);
|
|
+int GRAPH_RDLOCK
|
|
+bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx,
|
|
+ BdrvChild *ignore_child, Error **errp);
|
|
|
|
int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
|
|
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
|
|
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
|
|
index 290cd2a70e..3185f3f429 100644
|
|
--- a/tests/unit/test-bdrv-drain.c
|
|
+++ b/tests/unit/test-bdrv-drain.c
|
|
@@ -1396,14 +1396,10 @@ static void test_set_aio_context(void)
|
|
bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
|
|
&error_abort);
|
|
|
|
- bdrv_drained_begin(bs);
|
|
bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort);
|
|
- bdrv_drained_end(bs);
|
|
|
|
- bdrv_drained_begin(bs);
|
|
bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort);
|
|
bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort);
|
|
- bdrv_drained_end(bs);
|
|
|
|
bdrv_unref(bs);
|
|
iothread_join(a);
|
|
--
|
|
2.39.3
|
|
|