summary refs log tree commit diff stats
path: root/util/qemu-coroutine-lock.c
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2018-03-22 15:28:33 +0000
committerStefan Hajnoczi <stefanha@redhat.com>2018-03-27 13:05:28 +0100
commitc40a2545700e9ad2ef67d5972484bbee4c83b2a6 (patch)
tree2c550ddaf9a59a58c3c07e40c02aded945384cd1 /util/qemu-coroutine-lock.c
parent67a74148d8828f611fc5a7a23277b8eceb4c9430 (diff)
downloadfocaccia-qemu-c40a2545700e9ad2ef67d5972484bbee4c83b2a6.tar.gz
focaccia-qemu-c40a2545700e9ad2ef67d5972484bbee4c83b2a6.zip
coroutine: avoid co_queue_wakeup recursion
qemu_aio_coroutine_enter() is (indirectly) called recursively when
processing co_queue_wakeup.  This can lead to stack exhaustion.

This patch rewrites co_queue_wakeup in an iterative fashion (instead of
recursive) with bounded memory usage to prevent stack exhaustion.

qemu_co_queue_run_restart() is inlined into qemu_aio_coroutine_enter()
and the qemu_coroutine_enter() call is turned into a loop to avoid
recursion.

There is one change that is worth mentioning:  Previously, when
coroutine A queued coroutine B, qemu_co_queue_run_restart() entered
coroutine B from coroutine A.  If A was terminating then it would still
stay alive until B yielded.  After this patch B is entered by A's parent
so that a A can be deleted immediately if it is terminating.

It is safe to make this change since B could never interact with A if it
was terminating anyway.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 20180322152834.12656-3-stefanha@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'util/qemu-coroutine-lock.c')
-rw-r--r--util/qemu-coroutine-lock.c34
1 files changed, 0 insertions, 34 deletions
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
index 5a80c10690..27438a1858 100644
--- a/util/qemu-coroutine-lock.c
+++ b/util/qemu-coroutine-lock.c
@@ -68,40 +68,6 @@ void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock)
     }
 }
 
-/**
- * qemu_co_queue_run_restart:
- *
- * Enter each coroutine that was previously marked for restart by
- * qemu_co_queue_next() or qemu_co_queue_restart_all().  This function is
- * invoked by the core coroutine code when the current coroutine yields or
- * terminates.
- */
-void qemu_co_queue_run_restart(Coroutine *co)
-{
-    Coroutine *next;
-    QSIMPLEQ_HEAD(, Coroutine) tmp_queue_wakeup =
-        QSIMPLEQ_HEAD_INITIALIZER(tmp_queue_wakeup);
-
-    trace_qemu_co_queue_run_restart(co);
-
-    /* Because "co" has yielded, any coroutine that we wakeup can resume it.
-     * If this happens and "co" terminates, co->co_queue_wakeup becomes
-     * invalid memory.  Therefore, use a temporary queue and do not touch
-     * the "co" coroutine as soon as you enter another one.
-     *
-     * In its turn resumed "co" can populate "co_queue_wakeup" queue with
-     * new coroutines to be woken up.  The caller, who has resumed "co",
-     * will be responsible for traversing the same queue, which may cause
-     * a different wakeup order but not any missing wakeups.
-     */
-    QSIMPLEQ_CONCAT(&tmp_queue_wakeup, &co->co_queue_wakeup);
-
-    while ((next = QSIMPLEQ_FIRST(&tmp_queue_wakeup))) {
-        QSIMPLEQ_REMOVE_HEAD(&tmp_queue_wakeup, co_queue_next);
-        qemu_coroutine_enter(next);
-    }
-}
-
 static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
 {
     Coroutine *next;