110817bf0SDaniel P. Berrange /*
210817bf0SDaniel P. Berrange * QEMU coroutines
310817bf0SDaniel P. Berrange *
410817bf0SDaniel P. Berrange * Copyright IBM, Corp. 2011
510817bf0SDaniel P. Berrange *
610817bf0SDaniel P. Berrange * Authors:
710817bf0SDaniel P. Berrange * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
810817bf0SDaniel P. Berrange * Kevin Wolf <kwolf@redhat.com>
910817bf0SDaniel P. Berrange *
1010817bf0SDaniel P. Berrange * This work is licensed under the terms of the GNU LGPL, version 2 or later.
1110817bf0SDaniel P. Berrange * See the COPYING.LIB file in the top-level directory.
1210817bf0SDaniel P. Berrange *
1310817bf0SDaniel P. Berrange */
1410817bf0SDaniel P. Berrange
15aafd7584SPeter Maydell #include "qemu/osdep.h"
1610817bf0SDaniel P. Berrange #include "trace.h"
1710817bf0SDaniel P. Berrange #include "qemu/thread.h"
1810817bf0SDaniel P. Berrange #include "qemu/atomic.h"
1910817bf0SDaniel P. Berrange #include "qemu/coroutine_int.h"
20ac387a08SStefan Hajnoczi #include "qemu/coroutine-tls.h"
2186a637e4SStefan Hajnoczi #include "qemu/cutils.h"
220c330a73SPaolo Bonzini #include "block/aio.h"
2310817bf0SDaniel P. Berrange
2410817bf0SDaniel P. Berrange enum {
2586a637e4SStefan Hajnoczi COROUTINE_POOL_BATCH_MAX_SIZE = 128,
2610817bf0SDaniel P. Berrange };
2710817bf0SDaniel P. Berrange
2886a637e4SStefan Hajnoczi /*
2986a637e4SStefan Hajnoczi * Coroutine creation and deletion is expensive so a pool of unused coroutines
3086a637e4SStefan Hajnoczi * is kept as a cache. When the pool has coroutines available, they are
3186a637e4SStefan Hajnoczi * recycled instead of creating new ones from scratch. Coroutines are added to
3286a637e4SStefan Hajnoczi * the pool upon termination.
3386a637e4SStefan Hajnoczi *
3486a637e4SStefan Hajnoczi * The pool is global but each thread maintains a small local pool to avoid
3586a637e4SStefan Hajnoczi * global pool contention. Threads fetch and return batches of coroutines from
3686a637e4SStefan Hajnoczi * the global pool to maintain their local pool. The local pool holds up to two
3786a637e4SStefan Hajnoczi * batches whereas the maximum size of the global pool is controlled by the
3886a637e4SStefan Hajnoczi * qemu_coroutine_inc_pool_size() API.
3986a637e4SStefan Hajnoczi *
4086a637e4SStefan Hajnoczi * .-----------------------------------.
4186a637e4SStefan Hajnoczi * | Batch 1 | Batch 2 | Batch 3 | ... | global_pool
4286a637e4SStefan Hajnoczi * `-----------------------------------'
4386a637e4SStefan Hajnoczi *
4486a637e4SStefan Hajnoczi * .-------------------.
4586a637e4SStefan Hajnoczi * | Batch 1 | Batch 2 | per-thread local_pool (maximum 2 batches)
4686a637e4SStefan Hajnoczi * `-------------------'
4786a637e4SStefan Hajnoczi */
4886a637e4SStefan Hajnoczi typedef struct CoroutinePoolBatch {
4986a637e4SStefan Hajnoczi /* Batches are kept in a list */
5086a637e4SStefan Hajnoczi QSLIST_ENTRY(CoroutinePoolBatch) next;
51ac387a08SStefan Hajnoczi
5286a637e4SStefan Hajnoczi /* This batch holds up to @COROUTINE_POOL_BATCH_MAX_SIZE coroutines */
5386a637e4SStefan Hajnoczi QSLIST_HEAD(, Coroutine) list;
5486a637e4SStefan Hajnoczi unsigned int size;
5586a637e4SStefan Hajnoczi } CoroutinePoolBatch;
5610817bf0SDaniel P. Berrange
5786a637e4SStefan Hajnoczi typedef QSLIST_HEAD(, CoroutinePoolBatch) CoroutinePool;
5886a637e4SStefan Hajnoczi
5986a637e4SStefan Hajnoczi /* Host operating system limit on number of pooled coroutines */
6086a637e4SStefan Hajnoczi static unsigned int global_pool_hard_max_size;
6186a637e4SStefan Hajnoczi
6286a637e4SStefan Hajnoczi static QemuMutex global_pool_lock; /* protects the following variables */
6386a637e4SStefan Hajnoczi static CoroutinePool global_pool = QSLIST_HEAD_INITIALIZER(global_pool);
6486a637e4SStefan Hajnoczi static unsigned int global_pool_size;
6586a637e4SStefan Hajnoczi static unsigned int global_pool_max_size = COROUTINE_POOL_BATCH_MAX_SIZE;
6686a637e4SStefan Hajnoczi
6786a637e4SStefan Hajnoczi QEMU_DEFINE_STATIC_CO_TLS(CoroutinePool, local_pool);
6886a637e4SStefan Hajnoczi QEMU_DEFINE_STATIC_CO_TLS(Notifier, local_pool_cleanup_notifier);
6986a637e4SStefan Hajnoczi
coroutine_pool_batch_new(void)7086a637e4SStefan Hajnoczi static CoroutinePoolBatch *coroutine_pool_batch_new(void)
7186a637e4SStefan Hajnoczi {
7286a637e4SStefan Hajnoczi CoroutinePoolBatch *batch = g_new(CoroutinePoolBatch, 1);
7386a637e4SStefan Hajnoczi
7486a637e4SStefan Hajnoczi QSLIST_INIT(&batch->list);
7586a637e4SStefan Hajnoczi batch->size = 0;
7686a637e4SStefan Hajnoczi return batch;
7786a637e4SStefan Hajnoczi }
7886a637e4SStefan Hajnoczi
coroutine_pool_batch_delete(CoroutinePoolBatch * batch)7986a637e4SStefan Hajnoczi static void coroutine_pool_batch_delete(CoroutinePoolBatch *batch)
8010817bf0SDaniel P. Berrange {
8110817bf0SDaniel P. Berrange Coroutine *co;
8210817bf0SDaniel P. Berrange Coroutine *tmp;
8310817bf0SDaniel P. Berrange
8486a637e4SStefan Hajnoczi QSLIST_FOREACH_SAFE(co, &batch->list, pool_next, tmp) {
8586a637e4SStefan Hajnoczi QSLIST_REMOVE_HEAD(&batch->list, pool_next);
8610817bf0SDaniel P. Berrange qemu_coroutine_delete(co);
8710817bf0SDaniel P. Berrange }
8886a637e4SStefan Hajnoczi g_free(batch);
8986a637e4SStefan Hajnoczi }
9086a637e4SStefan Hajnoczi
local_pool_cleanup(Notifier * n,void * value)9186a637e4SStefan Hajnoczi static void local_pool_cleanup(Notifier *n, void *value)
9286a637e4SStefan Hajnoczi {
9386a637e4SStefan Hajnoczi CoroutinePool *local_pool = get_ptr_local_pool();
9486a637e4SStefan Hajnoczi CoroutinePoolBatch *batch;
9586a637e4SStefan Hajnoczi CoroutinePoolBatch *tmp;
9686a637e4SStefan Hajnoczi
9786a637e4SStefan Hajnoczi QSLIST_FOREACH_SAFE(batch, local_pool, next, tmp) {
9886a637e4SStefan Hajnoczi QSLIST_REMOVE_HEAD(local_pool, next);
9986a637e4SStefan Hajnoczi coroutine_pool_batch_delete(batch);
10086a637e4SStefan Hajnoczi }
10186a637e4SStefan Hajnoczi }
10286a637e4SStefan Hajnoczi
10386a637e4SStefan Hajnoczi /* Ensure the atexit notifier is registered */
local_pool_cleanup_init_once(void)10486a637e4SStefan Hajnoczi static void local_pool_cleanup_init_once(void)
10586a637e4SStefan Hajnoczi {
10686a637e4SStefan Hajnoczi Notifier *notifier = get_ptr_local_pool_cleanup_notifier();
10786a637e4SStefan Hajnoczi if (!notifier->notify) {
10886a637e4SStefan Hajnoczi notifier->notify = local_pool_cleanup;
10986a637e4SStefan Hajnoczi qemu_thread_atexit_add(notifier);
11086a637e4SStefan Hajnoczi }
11186a637e4SStefan Hajnoczi }
11286a637e4SStefan Hajnoczi
11386a637e4SStefan Hajnoczi /* Helper to get the next unused coroutine from the local pool */
coroutine_pool_get_local(void)11486a637e4SStefan Hajnoczi static Coroutine *coroutine_pool_get_local(void)
11586a637e4SStefan Hajnoczi {
11686a637e4SStefan Hajnoczi CoroutinePool *local_pool = get_ptr_local_pool();
11786a637e4SStefan Hajnoczi CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool);
11886a637e4SStefan Hajnoczi Coroutine *co;
11986a637e4SStefan Hajnoczi
12086a637e4SStefan Hajnoczi if (unlikely(!batch)) {
12186a637e4SStefan Hajnoczi return NULL;
12286a637e4SStefan Hajnoczi }
12386a637e4SStefan Hajnoczi
12486a637e4SStefan Hajnoczi co = QSLIST_FIRST(&batch->list);
12586a637e4SStefan Hajnoczi QSLIST_REMOVE_HEAD(&batch->list, pool_next);
12686a637e4SStefan Hajnoczi batch->size--;
12786a637e4SStefan Hajnoczi
12886a637e4SStefan Hajnoczi if (batch->size == 0) {
12986a637e4SStefan Hajnoczi QSLIST_REMOVE_HEAD(local_pool, next);
13086a637e4SStefan Hajnoczi coroutine_pool_batch_delete(batch);
13186a637e4SStefan Hajnoczi }
13286a637e4SStefan Hajnoczi return co;
13386a637e4SStefan Hajnoczi }
13486a637e4SStefan Hajnoczi
13586a637e4SStefan Hajnoczi /* Get the next batch from the global pool */
coroutine_pool_refill_local(void)13686a637e4SStefan Hajnoczi static void coroutine_pool_refill_local(void)
13786a637e4SStefan Hajnoczi {
13886a637e4SStefan Hajnoczi CoroutinePool *local_pool = get_ptr_local_pool();
139*25bc7d16SMarc-André Lureau CoroutinePoolBatch *batch = NULL;
14086a637e4SStefan Hajnoczi
14186a637e4SStefan Hajnoczi WITH_QEMU_LOCK_GUARD(&global_pool_lock) {
14286a637e4SStefan Hajnoczi batch = QSLIST_FIRST(&global_pool);
14386a637e4SStefan Hajnoczi
14486a637e4SStefan Hajnoczi if (batch) {
14586a637e4SStefan Hajnoczi QSLIST_REMOVE_HEAD(&global_pool, next);
14686a637e4SStefan Hajnoczi global_pool_size -= batch->size;
14786a637e4SStefan Hajnoczi }
14886a637e4SStefan Hajnoczi }
14986a637e4SStefan Hajnoczi
15086a637e4SStefan Hajnoczi if (batch) {
15186a637e4SStefan Hajnoczi QSLIST_INSERT_HEAD(local_pool, batch, next);
15286a637e4SStefan Hajnoczi local_pool_cleanup_init_once();
15386a637e4SStefan Hajnoczi }
15486a637e4SStefan Hajnoczi }
15586a637e4SStefan Hajnoczi
15686a637e4SStefan Hajnoczi /* Add a batch of coroutines to the global pool */
coroutine_pool_put_global(CoroutinePoolBatch * batch)15786a637e4SStefan Hajnoczi static void coroutine_pool_put_global(CoroutinePoolBatch *batch)
15886a637e4SStefan Hajnoczi {
15986a637e4SStefan Hajnoczi WITH_QEMU_LOCK_GUARD(&global_pool_lock) {
16086a637e4SStefan Hajnoczi unsigned int max = MIN(global_pool_max_size,
16186a637e4SStefan Hajnoczi global_pool_hard_max_size);
16286a637e4SStefan Hajnoczi
16386a637e4SStefan Hajnoczi if (global_pool_size < max) {
16486a637e4SStefan Hajnoczi QSLIST_INSERT_HEAD(&global_pool, batch, next);
16586a637e4SStefan Hajnoczi
16686a637e4SStefan Hajnoczi /* Overshooting the max pool size is allowed */
16786a637e4SStefan Hajnoczi global_pool_size += batch->size;
16886a637e4SStefan Hajnoczi return;
16986a637e4SStefan Hajnoczi }
17086a637e4SStefan Hajnoczi }
17186a637e4SStefan Hajnoczi
17286a637e4SStefan Hajnoczi /* The global pool was full, so throw away this batch */
17386a637e4SStefan Hajnoczi coroutine_pool_batch_delete(batch);
17486a637e4SStefan Hajnoczi }
17586a637e4SStefan Hajnoczi
17686a637e4SStefan Hajnoczi /* Get the next unused coroutine from the pool or return NULL */
coroutine_pool_get(void)17786a637e4SStefan Hajnoczi static Coroutine *coroutine_pool_get(void)
17886a637e4SStefan Hajnoczi {
17986a637e4SStefan Hajnoczi Coroutine *co;
18086a637e4SStefan Hajnoczi
18186a637e4SStefan Hajnoczi co = coroutine_pool_get_local();
18286a637e4SStefan Hajnoczi if (!co) {
18386a637e4SStefan Hajnoczi coroutine_pool_refill_local();
18486a637e4SStefan Hajnoczi co = coroutine_pool_get_local();
18586a637e4SStefan Hajnoczi }
18686a637e4SStefan Hajnoczi return co;
18786a637e4SStefan Hajnoczi }
18886a637e4SStefan Hajnoczi
coroutine_pool_put(Coroutine * co)18986a637e4SStefan Hajnoczi static void coroutine_pool_put(Coroutine *co)
19086a637e4SStefan Hajnoczi {
19186a637e4SStefan Hajnoczi CoroutinePool *local_pool = get_ptr_local_pool();
19286a637e4SStefan Hajnoczi CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool);
19386a637e4SStefan Hajnoczi
19486a637e4SStefan Hajnoczi if (unlikely(!batch)) {
19586a637e4SStefan Hajnoczi batch = coroutine_pool_batch_new();
19686a637e4SStefan Hajnoczi QSLIST_INSERT_HEAD(local_pool, batch, next);
19786a637e4SStefan Hajnoczi local_pool_cleanup_init_once();
19886a637e4SStefan Hajnoczi }
19986a637e4SStefan Hajnoczi
20086a637e4SStefan Hajnoczi if (unlikely(batch->size >= COROUTINE_POOL_BATCH_MAX_SIZE)) {
20186a637e4SStefan Hajnoczi CoroutinePoolBatch *next = QSLIST_NEXT(batch, next);
20286a637e4SStefan Hajnoczi
20386a637e4SStefan Hajnoczi /* Is the local pool full? */
20486a637e4SStefan Hajnoczi if (next) {
20586a637e4SStefan Hajnoczi QSLIST_REMOVE_HEAD(local_pool, next);
20686a637e4SStefan Hajnoczi coroutine_pool_put_global(batch);
20786a637e4SStefan Hajnoczi }
20886a637e4SStefan Hajnoczi
20986a637e4SStefan Hajnoczi batch = coroutine_pool_batch_new();
21086a637e4SStefan Hajnoczi QSLIST_INSERT_HEAD(local_pool, batch, next);
21186a637e4SStefan Hajnoczi }
21286a637e4SStefan Hajnoczi
21386a637e4SStefan Hajnoczi QSLIST_INSERT_HEAD(&batch->list, co, pool_next);
21486a637e4SStefan Hajnoczi batch->size++;
21510817bf0SDaniel P. Berrange }
21610817bf0SDaniel P. Berrange
qemu_coroutine_create(CoroutineEntry * entry,void * opaque)2170b8b8753SPaolo Bonzini Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
21810817bf0SDaniel P. Berrange {
21910817bf0SDaniel P. Berrange Coroutine *co = NULL;
22010817bf0SDaniel P. Berrange
221230f6e06SPaolo Bonzini if (IS_ENABLED(CONFIG_COROUTINE_POOL)) {
22286a637e4SStefan Hajnoczi co = coroutine_pool_get();
22310817bf0SDaniel P. Berrange }
22410817bf0SDaniel P. Berrange
22510817bf0SDaniel P. Berrange if (!co) {
22610817bf0SDaniel P. Berrange co = qemu_coroutine_new();
22710817bf0SDaniel P. Berrange }
22810817bf0SDaniel P. Berrange
22910817bf0SDaniel P. Berrange co->entry = entry;
2300b8b8753SPaolo Bonzini co->entry_arg = opaque;
2317d9c8581SPaolo Bonzini QSIMPLEQ_INIT(&co->co_queue_wakeup);
23210817bf0SDaniel P. Berrange return co;
23310817bf0SDaniel P. Berrange }
23410817bf0SDaniel P. Berrange
coroutine_delete(Coroutine * co)23510817bf0SDaniel P. Berrange static void coroutine_delete(Coroutine *co)
23610817bf0SDaniel P. Berrange {
23710817bf0SDaniel P. Berrange co->caller = NULL;
23810817bf0SDaniel P. Berrange
239230f6e06SPaolo Bonzini if (IS_ENABLED(CONFIG_COROUTINE_POOL)) {
24086a637e4SStefan Hajnoczi coroutine_pool_put(co);
24186a637e4SStefan Hajnoczi } else {
24210817bf0SDaniel P. Berrange qemu_coroutine_delete(co);
24310817bf0SDaniel P. Berrange }
24486a637e4SStefan Hajnoczi }
24510817bf0SDaniel P. Berrange
qemu_aio_coroutine_enter(AioContext * ctx,Coroutine * co)246ba9e75ceSFam Zheng void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co)
24710817bf0SDaniel P. Berrange {
248c40a2545SStefan Hajnoczi QSIMPLEQ_HEAD(, Coroutine) pending = QSIMPLEQ_HEAD_INITIALIZER(pending);
249c40a2545SStefan Hajnoczi Coroutine *from = qemu_coroutine_self();
250c40a2545SStefan Hajnoczi
251c40a2545SStefan Hajnoczi QSIMPLEQ_INSERT_TAIL(&pending, co, co_queue_next);
252c40a2545SStefan Hajnoczi
253c40a2545SStefan Hajnoczi /* Run co and any queued coroutines */
254c40a2545SStefan Hajnoczi while (!QSIMPLEQ_EMPTY(&pending)) {
255c40a2545SStefan Hajnoczi Coroutine *to = QSIMPLEQ_FIRST(&pending);
25610817bf0SDaniel P. Berrange CoroutineAction ret;
25710817bf0SDaniel P. Berrange
258512c90c9SPaolo Bonzini /*
259512c90c9SPaolo Bonzini * Read to before to->scheduled; pairs with qatomic_cmpxchg in
260512c90c9SPaolo Bonzini * qemu_co_sleep(), aio_co_schedule() etc.
261512c90c9SPaolo Bonzini */
262512c90c9SPaolo Bonzini smp_read_barrier_depends();
263512c90c9SPaolo Bonzini
264512c90c9SPaolo Bonzini const char *scheduled = qatomic_read(&to->scheduled);
2656133b39fSJeff Cody
266c40a2545SStefan Hajnoczi QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next);
267c40a2545SStefan Hajnoczi
268c40a2545SStefan Hajnoczi trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg);
26910817bf0SDaniel P. Berrange
2706133b39fSJeff Cody /* if the Coroutine has already been scheduled, entering it again will
2716133b39fSJeff Cody * cause us to enter it twice, potentially even after the coroutine has
2726133b39fSJeff Cody * been deleted */
2736133b39fSJeff Cody if (scheduled) {
2746133b39fSJeff Cody fprintf(stderr,
2756133b39fSJeff Cody "%s: Co-routine was already scheduled in '%s'\n",
2766133b39fSJeff Cody __func__, scheduled);
2776133b39fSJeff Cody abort();
2786133b39fSJeff Cody }
2796133b39fSJeff Cody
280c40a2545SStefan Hajnoczi if (to->caller) {
28110817bf0SDaniel P. Berrange fprintf(stderr, "Co-routine re-entered recursively\n");
28210817bf0SDaniel P. Berrange abort();
28310817bf0SDaniel P. Berrange }
28410817bf0SDaniel P. Berrange
285c40a2545SStefan Hajnoczi to->caller = from;
286c40a2545SStefan Hajnoczi to->ctx = ctx;
2870c330a73SPaolo Bonzini
288c40a2545SStefan Hajnoczi /* Store to->ctx before anything that stores to. Matches
289480cff63SPaolo Bonzini * barrier in aio_co_wake and qemu_co_mutex_wake.
2900c330a73SPaolo Bonzini */
2910c330a73SPaolo Bonzini smp_wmb();
2920c330a73SPaolo Bonzini
293c40a2545SStefan Hajnoczi ret = qemu_coroutine_switch(from, to, COROUTINE_ENTER);
29410817bf0SDaniel P. Berrange
295c40a2545SStefan Hajnoczi /* Queued coroutines are run depth-first; previously pending coroutines
296c40a2545SStefan Hajnoczi * run after those queued more recently.
297528f449fSRoman Pen */
298c40a2545SStefan Hajnoczi QSIMPLEQ_PREPEND(&pending, &to->co_queue_wakeup);
299528f449fSRoman Pen
30010817bf0SDaniel P. Berrange switch (ret) {
30110817bf0SDaniel P. Berrange case COROUTINE_YIELD:
302c40a2545SStefan Hajnoczi break;
30310817bf0SDaniel P. Berrange case COROUTINE_TERMINATE:
304c40a2545SStefan Hajnoczi assert(!to->locks_held);
305c40a2545SStefan Hajnoczi trace_qemu_coroutine_terminate(to);
306c40a2545SStefan Hajnoczi coroutine_delete(to);
307c40a2545SStefan Hajnoczi break;
30810817bf0SDaniel P. Berrange default:
30910817bf0SDaniel P. Berrange abort();
31010817bf0SDaniel P. Berrange }
31110817bf0SDaniel P. Berrange }
312c40a2545SStefan Hajnoczi }
31310817bf0SDaniel P. Berrange
qemu_coroutine_enter(Coroutine * co)314ba9e75ceSFam Zheng void qemu_coroutine_enter(Coroutine *co)
315ba9e75ceSFam Zheng {
316ba9e75ceSFam Zheng qemu_aio_coroutine_enter(qemu_get_current_aio_context(), co);
317ba9e75ceSFam Zheng }
318ba9e75ceSFam Zheng
qemu_coroutine_enter_if_inactive(Coroutine * co)319536fca7fSKevin Wolf void qemu_coroutine_enter_if_inactive(Coroutine *co)
320536fca7fSKevin Wolf {
321536fca7fSKevin Wolf if (!qemu_coroutine_entered(co)) {
322536fca7fSKevin Wolf qemu_coroutine_enter(co);
323536fca7fSKevin Wolf }
324536fca7fSKevin Wolf }
325536fca7fSKevin Wolf
qemu_coroutine_yield(void)32610817bf0SDaniel P. Berrange void coroutine_fn qemu_coroutine_yield(void)
32710817bf0SDaniel P. Berrange {
32810817bf0SDaniel P. Berrange Coroutine *self = qemu_coroutine_self();
32910817bf0SDaniel P. Berrange Coroutine *to = self->caller;
33010817bf0SDaniel P. Berrange
33110817bf0SDaniel P. Berrange trace_qemu_coroutine_yield(self, to);
33210817bf0SDaniel P. Berrange
33310817bf0SDaniel P. Berrange if (!to) {
33410817bf0SDaniel P. Berrange fprintf(stderr, "Co-routine is yielding to no one\n");
33510817bf0SDaniel P. Berrange abort();
33610817bf0SDaniel P. Berrange }
33710817bf0SDaniel P. Berrange
33810817bf0SDaniel P. Berrange self->caller = NULL;
33910817bf0SDaniel P. Berrange qemu_coroutine_switch(self, to, COROUTINE_YIELD);
34010817bf0SDaniel P. Berrange }
341f643e469SStefan Hajnoczi
qemu_coroutine_entered(Coroutine * co)342f643e469SStefan Hajnoczi bool qemu_coroutine_entered(Coroutine *co)
343f643e469SStefan Hajnoczi {
344f643e469SStefan Hajnoczi return co->caller;
345f643e469SStefan Hajnoczi }
346aa1361d5SKevin Wolf
qemu_coroutine_get_aio_context(Coroutine * co)347a248b856SPaolo Bonzini AioContext *qemu_coroutine_get_aio_context(Coroutine *co)
348aa1361d5SKevin Wolf {
349aa1361d5SKevin Wolf return co->ctx;
350aa1361d5SKevin Wolf }
3514c41c69eSHiroki Narukawa
qemu_coroutine_inc_pool_size(unsigned int additional_pool_size)35298e3ab35SKevin Wolf void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size)
3534c41c69eSHiroki Narukawa {
35486a637e4SStefan Hajnoczi QEMU_LOCK_GUARD(&global_pool_lock);
35586a637e4SStefan Hajnoczi global_pool_max_size += additional_pool_size;
3564c41c69eSHiroki Narukawa }
3574c41c69eSHiroki Narukawa
qemu_coroutine_dec_pool_size(unsigned int removing_pool_size)35898e3ab35SKevin Wolf void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size)
3594c41c69eSHiroki Narukawa {
36086a637e4SStefan Hajnoczi QEMU_LOCK_GUARD(&global_pool_lock);
36186a637e4SStefan Hajnoczi global_pool_max_size -= removing_pool_size;
36286a637e4SStefan Hajnoczi }
36386a637e4SStefan Hajnoczi
get_global_pool_hard_max_size(void)36486a637e4SStefan Hajnoczi static unsigned int get_global_pool_hard_max_size(void)
36586a637e4SStefan Hajnoczi {
36686a637e4SStefan Hajnoczi #ifdef __linux__
36786a637e4SStefan Hajnoczi g_autofree char *contents = NULL;
36886a637e4SStefan Hajnoczi int max_map_count;
36986a637e4SStefan Hajnoczi
37086a637e4SStefan Hajnoczi /*
37186a637e4SStefan Hajnoczi * Linux processes can have up to max_map_count virtual memory areas
37286a637e4SStefan Hajnoczi * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We
37386a637e4SStefan Hajnoczi * must limit the coroutine pool to a safe size to avoid running out of
37486a637e4SStefan Hajnoczi * VMAs.
37586a637e4SStefan Hajnoczi */
37686a637e4SStefan Hajnoczi if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL,
37786a637e4SStefan Hajnoczi NULL) &&
37886a637e4SStefan Hajnoczi qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) {
37986a637e4SStefan Hajnoczi /*
3809352f80cSStefan Hajnoczi * This is an upper bound that avoids exceeding max_map_count. Leave a
3819352f80cSStefan Hajnoczi * fixed amount for non-coroutine users like library dependencies,
3829352f80cSStefan Hajnoczi * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the
3839352f80cSStefan Hajnoczi * remaining amount.
38486a637e4SStefan Hajnoczi */
3859352f80cSStefan Hajnoczi if (max_map_count > 5000) {
3869352f80cSStefan Hajnoczi return (max_map_count - 5000) / 2;
3879352f80cSStefan Hajnoczi } else {
3889352f80cSStefan Hajnoczi /* Disable the global pool but threads still have local pools */
3899352f80cSStefan Hajnoczi return 0;
3909352f80cSStefan Hajnoczi }
39186a637e4SStefan Hajnoczi }
39286a637e4SStefan Hajnoczi #endif
39386a637e4SStefan Hajnoczi
39486a637e4SStefan Hajnoczi return UINT_MAX;
39586a637e4SStefan Hajnoczi }
39686a637e4SStefan Hajnoczi
qemu_coroutine_init(void)39786a637e4SStefan Hajnoczi static void __attribute__((constructor)) qemu_coroutine_init(void)
39886a637e4SStefan Hajnoczi {
39986a637e4SStefan Hajnoczi qemu_mutex_init(&global_pool_lock);
40086a637e4SStefan Hajnoczi global_pool_hard_max_size = get_global_pool_hard_max_size();
4014c41c69eSHiroki Narukawa }
402