From c40a2545700e9ad2ef67d5972484bbee4c83b2a6 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 22 Mar 2018 15:28:33 +0000 Subject: 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 Reviewed-by: Paolo Bonzini Message-id: 20180322152834.12656-3-stefanha@redhat.com Signed-off-by: Stefan Hajnoczi --- util/qemu-coroutine-lock.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) (limited to 'util/qemu-coroutine-lock.c') 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; -- cgit v1.2.3-55-g7522