mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-30 13:31:52 -06:00
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>
This commit is contained in:
parent
67a74148d8
commit
c40a254570
4 changed files with 50 additions and 78 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue