17719f3c9SStefan Hajnoczi /* 27719f3c9SStefan Hajnoczi * AioContext wait support 37719f3c9SStefan Hajnoczi * 47719f3c9SStefan Hajnoczi * Copyright (C) 2018 Red Hat, Inc. 57719f3c9SStefan Hajnoczi * 67719f3c9SStefan Hajnoczi * Permission is hereby granted, free of charge, to any person obtaining a copy 77719f3c9SStefan Hajnoczi * of this software and associated documentation files (the "Software"), to deal 87719f3c9SStefan Hajnoczi * in the Software without restriction, including without limitation the rights 97719f3c9SStefan Hajnoczi * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 107719f3c9SStefan Hajnoczi * copies of the Software, and to permit persons to whom the Software is 117719f3c9SStefan Hajnoczi * furnished to do so, subject to the following conditions: 127719f3c9SStefan Hajnoczi * 137719f3c9SStefan Hajnoczi * The above copyright notice and this permission notice shall be included in 147719f3c9SStefan Hajnoczi * all copies or substantial portions of the Software. 157719f3c9SStefan Hajnoczi * 167719f3c9SStefan Hajnoczi * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 177719f3c9SStefan Hajnoczi * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 187719f3c9SStefan Hajnoczi * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 197719f3c9SStefan Hajnoczi * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207719f3c9SStefan Hajnoczi * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 217719f3c9SStefan Hajnoczi * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 227719f3c9SStefan Hajnoczi * THE SOFTWARE. 237719f3c9SStefan Hajnoczi */ 247719f3c9SStefan Hajnoczi 257719f3c9SStefan Hajnoczi #ifndef QEMU_AIO_WAIT_H 267719f3c9SStefan Hajnoczi #define QEMU_AIO_WAIT_H 277719f3c9SStefan Hajnoczi 287719f3c9SStefan Hajnoczi #include "block/aio.h" 297719f3c9SStefan Hajnoczi 307719f3c9SStefan Hajnoczi /** 317719f3c9SStefan Hajnoczi * AioWait: 327719f3c9SStefan Hajnoczi * 33*cfe29d82SKevin Wolf * An object that facilitates synchronous waiting on a condition. A single 34*cfe29d82SKevin Wolf * global AioWait object (global_aio_wait) is used internally. 357719f3c9SStefan Hajnoczi * 36*cfe29d82SKevin Wolf * The main loop can wait on an operation running in an IOThread as follows: 37*cfe29d82SKevin Wolf * 387719f3c9SStefan Hajnoczi * AioContext *ctx = ...; 397719f3c9SStefan Hajnoczi * MyWork work = { .done = false }; 407719f3c9SStefan Hajnoczi * schedule_my_work_in_iothread(ctx, &work); 41*cfe29d82SKevin Wolf * AIO_WAIT_WHILE(ctx, !work.done); 427719f3c9SStefan Hajnoczi * 437719f3c9SStefan Hajnoczi * The IOThread must call aio_wait_kick() to notify the main loop when 447719f3c9SStefan Hajnoczi * work.done changes: 457719f3c9SStefan Hajnoczi * 467719f3c9SStefan Hajnoczi * static void do_work(...) 477719f3c9SStefan Hajnoczi * { 487719f3c9SStefan Hajnoczi * ... 497719f3c9SStefan Hajnoczi * work.done = true; 50*cfe29d82SKevin Wolf * aio_wait_kick(); 517719f3c9SStefan Hajnoczi * } 527719f3c9SStefan Hajnoczi */ 537719f3c9SStefan Hajnoczi typedef struct { 547376eda7SStefan Hajnoczi /* Number of waiting AIO_WAIT_WHILE() callers. Accessed with atomic ops. */ 557376eda7SStefan Hajnoczi unsigned num_waiters; 567719f3c9SStefan Hajnoczi } AioWait; 577719f3c9SStefan Hajnoczi 58*cfe29d82SKevin Wolf extern AioWait global_aio_wait; 59*cfe29d82SKevin Wolf 607719f3c9SStefan Hajnoczi /** 617719f3c9SStefan Hajnoczi * AIO_WAIT_WHILE: 624d22bbf4SKevin Wolf * @ctx: the aio context, or NULL if multiple aio contexts (for which the 634d22bbf4SKevin Wolf * caller does not hold a lock) are involved in the polling condition. 647719f3c9SStefan Hajnoczi * @cond: wait while this conditional expression is true 657719f3c9SStefan Hajnoczi * 667719f3c9SStefan Hajnoczi * Wait while a condition is true. Use this to implement synchronous 677719f3c9SStefan Hajnoczi * operations that require event loop activity. 687719f3c9SStefan Hajnoczi * 697719f3c9SStefan Hajnoczi * The caller must be sure that something calls aio_wait_kick() when the value 707719f3c9SStefan Hajnoczi * of @cond might have changed. 717719f3c9SStefan Hajnoczi * 727719f3c9SStefan Hajnoczi * The caller's thread must be the IOThread that owns @ctx or the main loop 737719f3c9SStefan Hajnoczi * thread (with @ctx acquired exactly once). This function cannot be used to 747719f3c9SStefan Hajnoczi * wait on conditions between two IOThreads since that could lead to deadlock, 757719f3c9SStefan Hajnoczi * go via the main loop instead. 767719f3c9SStefan Hajnoczi */ 77*cfe29d82SKevin Wolf #define AIO_WAIT_WHILE(ctx, cond) ({ \ 787719f3c9SStefan Hajnoczi bool waited_ = false; \ 79*cfe29d82SKevin Wolf AioWait *wait_ = &global_aio_wait; \ 807719f3c9SStefan Hajnoczi AioContext *ctx_ = (ctx); \ 8148657448SKevin Wolf /* Increment wait_->num_waiters before evaluating cond. */ \ 8248657448SKevin Wolf atomic_inc(&wait_->num_waiters); \ 834d22bbf4SKevin Wolf if (ctx_ && in_aio_context_home_thread(ctx_)) { \ 841cc8e54aSKevin Wolf while ((cond)) { \ 851cc8e54aSKevin Wolf aio_poll(ctx_, true); \ 861cc8e54aSKevin Wolf waited_ = true; \ 877719f3c9SStefan Hajnoczi } \ 887719f3c9SStefan Hajnoczi } else { \ 897719f3c9SStefan Hajnoczi assert(qemu_get_current_aio_context() == \ 907719f3c9SStefan Hajnoczi qemu_get_aio_context()); \ 911cc8e54aSKevin Wolf while ((cond)) { \ 924d22bbf4SKevin Wolf if (ctx_) { \ 937719f3c9SStefan Hajnoczi aio_context_release(ctx_); \ 944d22bbf4SKevin Wolf } \ 957719f3c9SStefan Hajnoczi aio_poll(qemu_get_aio_context(), true); \ 964d22bbf4SKevin Wolf if (ctx_) { \ 977719f3c9SStefan Hajnoczi aio_context_acquire(ctx_); \ 984d22bbf4SKevin Wolf } \ 991cc8e54aSKevin Wolf waited_ = true; \ 1007719f3c9SStefan Hajnoczi } \ 1017719f3c9SStefan Hajnoczi } \ 10248657448SKevin Wolf atomic_dec(&wait_->num_waiters); \ 1037719f3c9SStefan Hajnoczi waited_; }) 1047719f3c9SStefan Hajnoczi 1057719f3c9SStefan Hajnoczi /** 1067719f3c9SStefan Hajnoczi * aio_wait_kick: 1077719f3c9SStefan Hajnoczi * Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During 1087719f3c9SStefan Hajnoczi * synchronous operations performed in an IOThread, the main thread lets the 1097719f3c9SStefan Hajnoczi * IOThread's event loop run, waiting for the operation to complete. A 1107719f3c9SStefan Hajnoczi * aio_wait_kick() call will wake up the main thread. 1117719f3c9SStefan Hajnoczi */ 112*cfe29d82SKevin Wolf void aio_wait_kick(void); 1137719f3c9SStefan Hajnoczi 114b89d92f3SStefan Hajnoczi /** 115b89d92f3SStefan Hajnoczi * aio_wait_bh_oneshot: 116b89d92f3SStefan Hajnoczi * @ctx: the aio context 117b89d92f3SStefan Hajnoczi * @cb: the BH callback function 118b89d92f3SStefan Hajnoczi * @opaque: user data for the BH callback function 119b89d92f3SStefan Hajnoczi * 120b89d92f3SStefan Hajnoczi * Run a BH in @ctx and wait for it to complete. 121b89d92f3SStefan Hajnoczi * 122b89d92f3SStefan Hajnoczi * Must be called from the main loop thread with @ctx acquired exactly once. 123b89d92f3SStefan Hajnoczi * Note that main loop event processing may occur. 124b89d92f3SStefan Hajnoczi */ 125b89d92f3SStefan Hajnoczi void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); 126b89d92f3SStefan Hajnoczi 1277719f3c9SStefan Hajnoczi #endif /* QEMU_AIO_WAIT */ 128