xref: /qemu/util/async.c (revision 2f78e491d7b46542158ce0b8132ee4e05bc0ade4)
14f999d05SKevin Wolf /*
24f999d05SKevin Wolf  * QEMU System Emulator
34f999d05SKevin Wolf  *
44f999d05SKevin Wolf  * Copyright (c) 2003-2008 Fabrice Bellard
54f999d05SKevin Wolf  *
64f999d05SKevin Wolf  * Permission is hereby granted, free of charge, to any person obtaining a copy
74f999d05SKevin Wolf  * of this software and associated documentation files (the "Software"), to deal
84f999d05SKevin Wolf  * in the Software without restriction, including without limitation the rights
94f999d05SKevin Wolf  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
104f999d05SKevin Wolf  * copies of the Software, and to permit persons to whom the Software is
114f999d05SKevin Wolf  * furnished to do so, subject to the following conditions:
124f999d05SKevin Wolf  *
134f999d05SKevin Wolf  * The above copyright notice and this permission notice shall be included in
144f999d05SKevin Wolf  * all copies or substantial portions of the Software.
154f999d05SKevin Wolf  *
164f999d05SKevin Wolf  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
174f999d05SKevin Wolf  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
184f999d05SKevin Wolf  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
194f999d05SKevin Wolf  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
204f999d05SKevin Wolf  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
214f999d05SKevin Wolf  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
224f999d05SKevin Wolf  * THE SOFTWARE.
234f999d05SKevin Wolf  */
244f999d05SKevin Wolf 
254f999d05SKevin Wolf #include "qemu-common.h"
26737e150eSPaolo Bonzini #include "block/aio.h"
279b34277dSStefan Hajnoczi #include "block/thread-pool.h"
281de7afc9SPaolo Bonzini #include "qemu/main-loop.h"
290ceb849bSPaolo Bonzini #include "qemu/atomic.h"
309a1e9481SKevin Wolf 
314f999d05SKevin Wolf /***********************************************************/
324f999d05SKevin Wolf /* bottom halves (can be seen as timers which expire ASAP) */
334f999d05SKevin Wolf 
344f999d05SKevin Wolf struct QEMUBH {
352f4dc3c1SPaolo Bonzini     AioContext *ctx;
364f999d05SKevin Wolf     QEMUBHFunc *cb;
374f999d05SKevin Wolf     void *opaque;
384f999d05SKevin Wolf     QEMUBH *next;
399b47b17eSStefan Weil     bool scheduled;
409b47b17eSStefan Weil     bool idle;
419b47b17eSStefan Weil     bool deleted;
424f999d05SKevin Wolf };
434f999d05SKevin Wolf 
44f627aab1SPaolo Bonzini QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
454f999d05SKevin Wolf {
464f999d05SKevin Wolf     QEMUBH *bh;
477267c094SAnthony Liguori     bh = g_malloc0(sizeof(QEMUBH));
482f4dc3c1SPaolo Bonzini     bh->ctx = ctx;
494f999d05SKevin Wolf     bh->cb = cb;
504f999d05SKevin Wolf     bh->opaque = opaque;
51dcc772e2SLiu Ping Fan     qemu_mutex_lock(&ctx->bh_lock);
52f627aab1SPaolo Bonzini     bh->next = ctx->first_bh;
53dcc772e2SLiu Ping Fan     /* Make sure that the members are ready before putting bh into list */
54dcc772e2SLiu Ping Fan     smp_wmb();
55f627aab1SPaolo Bonzini     ctx->first_bh = bh;
56dcc772e2SLiu Ping Fan     qemu_mutex_unlock(&ctx->bh_lock);
574f999d05SKevin Wolf     return bh;
584f999d05SKevin Wolf }
594f999d05SKevin Wolf 
60dcc772e2SLiu Ping Fan /* Multiple occurrences of aio_bh_poll cannot be called concurrently */
61f627aab1SPaolo Bonzini int aio_bh_poll(AioContext *ctx)
624f999d05SKevin Wolf {
637887f620SKevin Wolf     QEMUBH *bh, **bhp, *next;
644f999d05SKevin Wolf     int ret;
65648fb0eaSKevin Wolf 
66f627aab1SPaolo Bonzini     ctx->walking_bh++;
674f999d05SKevin Wolf 
684f999d05SKevin Wolf     ret = 0;
69f627aab1SPaolo Bonzini     for (bh = ctx->first_bh; bh; bh = next) {
70dcc772e2SLiu Ping Fan         /* Make sure that fetching bh happens before accessing its members */
71dcc772e2SLiu Ping Fan         smp_read_barrier_depends();
727887f620SKevin Wolf         next = bh->next;
734f999d05SKevin Wolf         if (!bh->deleted && bh->scheduled) {
744f999d05SKevin Wolf             bh->scheduled = 0;
75dcc772e2SLiu Ping Fan             /* Paired with write barrier in bh schedule to ensure reading for
76dcc772e2SLiu Ping Fan              * idle & callbacks coming after bh's scheduling.
77dcc772e2SLiu Ping Fan              */
78dcc772e2SLiu Ping Fan             smp_rmb();
794f999d05SKevin Wolf             if (!bh->idle)
804f999d05SKevin Wolf                 ret = 1;
814f999d05SKevin Wolf             bh->idle = 0;
824f999d05SKevin Wolf             bh->cb(bh->opaque);
834f999d05SKevin Wolf         }
844f999d05SKevin Wolf     }
854f999d05SKevin Wolf 
86f627aab1SPaolo Bonzini     ctx->walking_bh--;
87648fb0eaSKevin Wolf 
884f999d05SKevin Wolf     /* remove deleted bhs */
89f627aab1SPaolo Bonzini     if (!ctx->walking_bh) {
90dcc772e2SLiu Ping Fan         qemu_mutex_lock(&ctx->bh_lock);
91f627aab1SPaolo Bonzini         bhp = &ctx->first_bh;
924f999d05SKevin Wolf         while (*bhp) {
934f999d05SKevin Wolf             bh = *bhp;
944f999d05SKevin Wolf             if (bh->deleted) {
954f999d05SKevin Wolf                 *bhp = bh->next;
967267c094SAnthony Liguori                 g_free(bh);
97648fb0eaSKevin Wolf             } else {
984f999d05SKevin Wolf                 bhp = &bh->next;
994f999d05SKevin Wolf             }
100648fb0eaSKevin Wolf         }
101dcc772e2SLiu Ping Fan         qemu_mutex_unlock(&ctx->bh_lock);
102648fb0eaSKevin Wolf     }
1034f999d05SKevin Wolf 
1044f999d05SKevin Wolf     return ret;
1054f999d05SKevin Wolf }
1064f999d05SKevin Wolf 
1074f999d05SKevin Wolf void qemu_bh_schedule_idle(QEMUBH *bh)
1084f999d05SKevin Wolf {
1094f999d05SKevin Wolf     if (bh->scheduled)
1104f999d05SKevin Wolf         return;
1114f999d05SKevin Wolf     bh->idle = 1;
112dcc772e2SLiu Ping Fan     /* Make sure that idle & any writes needed by the callback are done
113dcc772e2SLiu Ping Fan      * before the locations are read in the aio_bh_poll.
114dcc772e2SLiu Ping Fan      */
115dcc772e2SLiu Ping Fan     smp_wmb();
116dcc772e2SLiu Ping Fan     bh->scheduled = 1;
1174f999d05SKevin Wolf }
1184f999d05SKevin Wolf 
1194f999d05SKevin Wolf void qemu_bh_schedule(QEMUBH *bh)
1204f999d05SKevin Wolf {
121924fe129SStefan Hajnoczi     AioContext *ctx;
122924fe129SStefan Hajnoczi 
1234f999d05SKevin Wolf     if (bh->scheduled)
1244f999d05SKevin Wolf         return;
125924fe129SStefan Hajnoczi     ctx = bh->ctx;
1264f999d05SKevin Wolf     bh->idle = 0;
127924fe129SStefan Hajnoczi     /* Make sure that:
128924fe129SStefan Hajnoczi      * 1. idle & any writes needed by the callback are done before the
129924fe129SStefan Hajnoczi      *    locations are read in the aio_bh_poll.
130924fe129SStefan Hajnoczi      * 2. ctx is loaded before scheduled is set and the callback has a chance
131924fe129SStefan Hajnoczi      *    to execute.
132dcc772e2SLiu Ping Fan      */
133924fe129SStefan Hajnoczi     smp_mb();
134dcc772e2SLiu Ping Fan     bh->scheduled = 1;
135924fe129SStefan Hajnoczi     aio_notify(ctx);
1364f999d05SKevin Wolf }
1374f999d05SKevin Wolf 
138dcc772e2SLiu Ping Fan 
139dcc772e2SLiu Ping Fan /* This func is async.
140dcc772e2SLiu Ping Fan  */
1414f999d05SKevin Wolf void qemu_bh_cancel(QEMUBH *bh)
1424f999d05SKevin Wolf {
1434f999d05SKevin Wolf     bh->scheduled = 0;
1444f999d05SKevin Wolf }
1454f999d05SKevin Wolf 
146dcc772e2SLiu Ping Fan /* This func is async.The bottom half will do the delete action at the finial
147dcc772e2SLiu Ping Fan  * end.
148dcc772e2SLiu Ping Fan  */
1494f999d05SKevin Wolf void qemu_bh_delete(QEMUBH *bh)
1504f999d05SKevin Wolf {
1514f999d05SKevin Wolf     bh->scheduled = 0;
1524f999d05SKevin Wolf     bh->deleted = 1;
1534f999d05SKevin Wolf }
1544f999d05SKevin Wolf 
155845ca10dSPaolo Bonzini int64_t
156845ca10dSPaolo Bonzini aio_compute_timeout(AioContext *ctx)
1574f999d05SKevin Wolf {
158845ca10dSPaolo Bonzini     int64_t deadline;
159845ca10dSPaolo Bonzini     int timeout = -1;
1604f999d05SKevin Wolf     QEMUBH *bh;
1614f999d05SKevin Wolf 
162f627aab1SPaolo Bonzini     for (bh = ctx->first_bh; bh; bh = bh->next) {
1634f999d05SKevin Wolf         if (!bh->deleted && bh->scheduled) {
1644f999d05SKevin Wolf             if (bh->idle) {
1654f999d05SKevin Wolf                 /* idle bottom halves will be polled at least
1664f999d05SKevin Wolf                  * every 10ms */
167845ca10dSPaolo Bonzini                 timeout = 10000000;
1684f999d05SKevin Wolf             } else {
1694f999d05SKevin Wolf                 /* non-idle bottom halves will be executed
1704f999d05SKevin Wolf                  * immediately */
171845ca10dSPaolo Bonzini                 return 0;
1724f999d05SKevin Wolf             }
1734f999d05SKevin Wolf         }
1744f999d05SKevin Wolf     }
1754f999d05SKevin Wolf 
176845ca10dSPaolo Bonzini     deadline = timerlistgroup_deadline_ns(&ctx->tlg);
177533a8cf3SAlex Bligh     if (deadline == 0) {
178845ca10dSPaolo Bonzini         return 0;
179533a8cf3SAlex Bligh     } else {
180845ca10dSPaolo Bonzini         return qemu_soonest_timeout(timeout, deadline);
181845ca10dSPaolo Bonzini     }
182533a8cf3SAlex Bligh }
183533a8cf3SAlex Bligh 
184845ca10dSPaolo Bonzini static gboolean
185845ca10dSPaolo Bonzini aio_ctx_prepare(GSource *source, gint    *timeout)
186845ca10dSPaolo Bonzini {
187845ca10dSPaolo Bonzini     AioContext *ctx = (AioContext *) source;
188845ca10dSPaolo Bonzini 
189845ca10dSPaolo Bonzini     /* We assume there is no timeout already supplied */
190845ca10dSPaolo Bonzini     *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx));
191a3462c65SPaolo Bonzini 
192a3462c65SPaolo Bonzini     if (aio_prepare(ctx)) {
193a3462c65SPaolo Bonzini         *timeout = 0;
194a3462c65SPaolo Bonzini     }
195a3462c65SPaolo Bonzini 
196845ca10dSPaolo Bonzini     return *timeout == 0;
197e3713e00SPaolo Bonzini }
198e3713e00SPaolo Bonzini 
199e3713e00SPaolo Bonzini static gboolean
200e3713e00SPaolo Bonzini aio_ctx_check(GSource *source)
201e3713e00SPaolo Bonzini {
202e3713e00SPaolo Bonzini     AioContext *ctx = (AioContext *) source;
203e3713e00SPaolo Bonzini     QEMUBH *bh;
204e3713e00SPaolo Bonzini 
205e3713e00SPaolo Bonzini     for (bh = ctx->first_bh; bh; bh = bh->next) {
206e3713e00SPaolo Bonzini         if (!bh->deleted && bh->scheduled) {
207e3713e00SPaolo Bonzini             return true;
208e3713e00SPaolo Bonzini 	}
209e3713e00SPaolo Bonzini     }
210533a8cf3SAlex Bligh     return aio_pending(ctx) || (timerlistgroup_deadline_ns(&ctx->tlg) == 0);
211e3713e00SPaolo Bonzini }
212e3713e00SPaolo Bonzini 
213e3713e00SPaolo Bonzini static gboolean
214e3713e00SPaolo Bonzini aio_ctx_dispatch(GSource     *source,
215e3713e00SPaolo Bonzini                  GSourceFunc  callback,
216e3713e00SPaolo Bonzini                  gpointer     user_data)
217e3713e00SPaolo Bonzini {
218e3713e00SPaolo Bonzini     AioContext *ctx = (AioContext *) source;
219e3713e00SPaolo Bonzini 
220e3713e00SPaolo Bonzini     assert(callback == NULL);
221e4c7e2d1SPaolo Bonzini     aio_dispatch(ctx);
222e3713e00SPaolo Bonzini     return true;
223e3713e00SPaolo Bonzini }
224e3713e00SPaolo Bonzini 
2252f4dc3c1SPaolo Bonzini static void
2262f4dc3c1SPaolo Bonzini aio_ctx_finalize(GSource     *source)
2272f4dc3c1SPaolo Bonzini {
2282f4dc3c1SPaolo Bonzini     AioContext *ctx = (AioContext *) source;
2292f4dc3c1SPaolo Bonzini 
2309b34277dSStefan Hajnoczi     thread_pool_free(ctx->thread_pool);
231f2e5dca4SStefan Hajnoczi     aio_set_event_notifier(ctx, &ctx->notifier, NULL);
2322f4dc3c1SPaolo Bonzini     event_notifier_cleanup(&ctx->notifier);
23398563fc3SStefan Hajnoczi     rfifolock_destroy(&ctx->lock);
234dcc772e2SLiu Ping Fan     qemu_mutex_destroy(&ctx->bh_lock);
2356b5f8762SStefan Hajnoczi     g_array_free(ctx->pollfds, TRUE);
236dae21b98SAlex Bligh     timerlistgroup_deinit(&ctx->tlg);
2372f4dc3c1SPaolo Bonzini }
2382f4dc3c1SPaolo Bonzini 
239e3713e00SPaolo Bonzini static GSourceFuncs aio_source_funcs = {
240e3713e00SPaolo Bonzini     aio_ctx_prepare,
241e3713e00SPaolo Bonzini     aio_ctx_check,
242e3713e00SPaolo Bonzini     aio_ctx_dispatch,
2432f4dc3c1SPaolo Bonzini     aio_ctx_finalize
244e3713e00SPaolo Bonzini };
245e3713e00SPaolo Bonzini 
246e3713e00SPaolo Bonzini GSource *aio_get_g_source(AioContext *ctx)
247e3713e00SPaolo Bonzini {
248e3713e00SPaolo Bonzini     g_source_ref(&ctx->source);
249e3713e00SPaolo Bonzini     return &ctx->source;
250e3713e00SPaolo Bonzini }
251a915f4bcSPaolo Bonzini 
2529b34277dSStefan Hajnoczi ThreadPool *aio_get_thread_pool(AioContext *ctx)
2539b34277dSStefan Hajnoczi {
2549b34277dSStefan Hajnoczi     if (!ctx->thread_pool) {
2559b34277dSStefan Hajnoczi         ctx->thread_pool = thread_pool_new(ctx);
2569b34277dSStefan Hajnoczi     }
2579b34277dSStefan Hajnoczi     return ctx->thread_pool;
2589b34277dSStefan Hajnoczi }
2599b34277dSStefan Hajnoczi 
2600ceb849bSPaolo Bonzini void aio_set_dispatching(AioContext *ctx, bool dispatching)
2610ceb849bSPaolo Bonzini {
2620ceb849bSPaolo Bonzini     ctx->dispatching = dispatching;
2630ceb849bSPaolo Bonzini     if (!dispatching) {
2640ceb849bSPaolo Bonzini         /* Write ctx->dispatching before reading e.g. bh->scheduled.
2650ceb849bSPaolo Bonzini          * Optimization: this is only needed when we're entering the "unsafe"
2660ceb849bSPaolo Bonzini          * phase where other threads must call event_notifier_set.
2670ceb849bSPaolo Bonzini          */
2680ceb849bSPaolo Bonzini         smp_mb();
2690ceb849bSPaolo Bonzini     }
2700ceb849bSPaolo Bonzini }
2710ceb849bSPaolo Bonzini 
2722f4dc3c1SPaolo Bonzini void aio_notify(AioContext *ctx)
2732f4dc3c1SPaolo Bonzini {
2740ceb849bSPaolo Bonzini     /* Write e.g. bh->scheduled before reading ctx->dispatching.  */
2750ceb849bSPaolo Bonzini     smp_mb();
2760ceb849bSPaolo Bonzini     if (!ctx->dispatching) {
2772f4dc3c1SPaolo Bonzini         event_notifier_set(&ctx->notifier);
2782f4dc3c1SPaolo Bonzini     }
2790ceb849bSPaolo Bonzini }
2802f4dc3c1SPaolo Bonzini 
281d5541d86SAlex Bligh static void aio_timerlist_notify(void *opaque)
282d5541d86SAlex Bligh {
283d5541d86SAlex Bligh     aio_notify(opaque);
284d5541d86SAlex Bligh }
285d5541d86SAlex Bligh 
28698563fc3SStefan Hajnoczi static void aio_rfifolock_cb(void *opaque)
28798563fc3SStefan Hajnoczi {
28898563fc3SStefan Hajnoczi     /* Kick owner thread in case they are blocked in aio_poll() */
28998563fc3SStefan Hajnoczi     aio_notify(opaque);
29098563fc3SStefan Hajnoczi }
29198563fc3SStefan Hajnoczi 
292*2f78e491SChrysostomos Nanakos AioContext *aio_context_new(Error **errp)
293f627aab1SPaolo Bonzini {
294*2f78e491SChrysostomos Nanakos     int ret;
2952f4dc3c1SPaolo Bonzini     AioContext *ctx;
2962f4dc3c1SPaolo Bonzini     ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
297*2f78e491SChrysostomos Nanakos     ret = event_notifier_init(&ctx->notifier, false);
298*2f78e491SChrysostomos Nanakos     if (ret < 0) {
299*2f78e491SChrysostomos Nanakos         g_source_destroy(&ctx->source);
300*2f78e491SChrysostomos Nanakos         error_setg_errno(errp, -ret, "Failed to initialize event notifier");
301*2f78e491SChrysostomos Nanakos         return NULL;
302*2f78e491SChrysostomos Nanakos     }
303*2f78e491SChrysostomos Nanakos     aio_set_event_notifier(ctx, &ctx->notifier,
304*2f78e491SChrysostomos Nanakos                            (EventNotifierHandler *)
305*2f78e491SChrysostomos Nanakos                            event_notifier_test_and_clear);
3066b5f8762SStefan Hajnoczi     ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
3079b34277dSStefan Hajnoczi     ctx->thread_pool = NULL;
308dcc772e2SLiu Ping Fan     qemu_mutex_init(&ctx->bh_lock);
30998563fc3SStefan Hajnoczi     rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
310d5541d86SAlex Bligh     timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
3112f4dc3c1SPaolo Bonzini 
3122f4dc3c1SPaolo Bonzini     return ctx;
313e3713e00SPaolo Bonzini }
314e3713e00SPaolo Bonzini 
315e3713e00SPaolo Bonzini void aio_context_ref(AioContext *ctx)
316e3713e00SPaolo Bonzini {
317e3713e00SPaolo Bonzini     g_source_ref(&ctx->source);
318e3713e00SPaolo Bonzini }
319e3713e00SPaolo Bonzini 
320e3713e00SPaolo Bonzini void aio_context_unref(AioContext *ctx)
321e3713e00SPaolo Bonzini {
322e3713e00SPaolo Bonzini     g_source_unref(&ctx->source);
323f627aab1SPaolo Bonzini }
32498563fc3SStefan Hajnoczi 
32598563fc3SStefan Hajnoczi void aio_context_acquire(AioContext *ctx)
32698563fc3SStefan Hajnoczi {
32798563fc3SStefan Hajnoczi     rfifolock_lock(&ctx->lock);
32898563fc3SStefan Hajnoczi }
32998563fc3SStefan Hajnoczi 
33098563fc3SStefan Hajnoczi void aio_context_release(AioContext *ctx)
33198563fc3SStefan Hajnoczi {
33298563fc3SStefan Hajnoczi     rfifolock_unlock(&ctx->lock);
33398563fc3SStefan Hajnoczi }
334