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" 29*3c18a92dSPaolo Bonzini #include "qemu/main-loop.h" 307719f3c9SStefan Hajnoczi 317719f3c9SStefan Hajnoczi /** 327719f3c9SStefan Hajnoczi * AioWait: 337719f3c9SStefan Hajnoczi * 34cfe29d82SKevin Wolf * An object that facilitates synchronous waiting on a condition. A single 35cfe29d82SKevin Wolf * global AioWait object (global_aio_wait) is used internally. 367719f3c9SStefan Hajnoczi * 37cfe29d82SKevin Wolf * The main loop can wait on an operation running in an IOThread as follows: 38cfe29d82SKevin Wolf * 397719f3c9SStefan Hajnoczi * AioContext *ctx = ...; 407719f3c9SStefan Hajnoczi * MyWork work = { .done = false }; 417719f3c9SStefan Hajnoczi * schedule_my_work_in_iothread(ctx, &work); 42cfe29d82SKevin Wolf * AIO_WAIT_WHILE(ctx, !work.done); 437719f3c9SStefan Hajnoczi * 447719f3c9SStefan Hajnoczi * The IOThread must call aio_wait_kick() to notify the main loop when 457719f3c9SStefan Hajnoczi * work.done changes: 467719f3c9SStefan Hajnoczi * 477719f3c9SStefan Hajnoczi * static void do_work(...) 487719f3c9SStefan Hajnoczi * { 497719f3c9SStefan Hajnoczi * ... 507719f3c9SStefan Hajnoczi * work.done = true; 51cfe29d82SKevin Wolf * aio_wait_kick(); 527719f3c9SStefan Hajnoczi * } 537719f3c9SStefan Hajnoczi */ 547719f3c9SStefan Hajnoczi typedef struct { 557376eda7SStefan Hajnoczi /* Number of waiting AIO_WAIT_WHILE() callers. Accessed with atomic ops. */ 567376eda7SStefan Hajnoczi unsigned num_waiters; 577719f3c9SStefan Hajnoczi } AioWait; 587719f3c9SStefan Hajnoczi 59cfe29d82SKevin Wolf extern AioWait global_aio_wait; 60cfe29d82SKevin Wolf 617719f3c9SStefan Hajnoczi /** 627719f3c9SStefan Hajnoczi * AIO_WAIT_WHILE: 634d22bbf4SKevin Wolf * @ctx: the aio context, or NULL if multiple aio contexts (for which the 644d22bbf4SKevin Wolf * caller does not hold a lock) are involved in the polling condition. 657719f3c9SStefan Hajnoczi * @cond: wait while this conditional expression is true 667719f3c9SStefan Hajnoczi * 677719f3c9SStefan Hajnoczi * Wait while a condition is true. Use this to implement synchronous 687719f3c9SStefan Hajnoczi * operations that require event loop activity. 697719f3c9SStefan Hajnoczi * 707719f3c9SStefan Hajnoczi * The caller must be sure that something calls aio_wait_kick() when the value 717719f3c9SStefan Hajnoczi * of @cond might have changed. 727719f3c9SStefan Hajnoczi * 737719f3c9SStefan Hajnoczi * The caller's thread must be the IOThread that owns @ctx or the main loop 747719f3c9SStefan Hajnoczi * thread (with @ctx acquired exactly once). This function cannot be used to 757719f3c9SStefan Hajnoczi * wait on conditions between two IOThreads since that could lead to deadlock, 767719f3c9SStefan Hajnoczi * go via the main loop instead. 777719f3c9SStefan Hajnoczi */ 78cfe29d82SKevin Wolf #define AIO_WAIT_WHILE(ctx, cond) ({ \ 797719f3c9SStefan Hajnoczi bool waited_ = false; \ 80cfe29d82SKevin Wolf AioWait *wait_ = &global_aio_wait; \ 817719f3c9SStefan Hajnoczi AioContext *ctx_ = (ctx); \ 8248657448SKevin Wolf /* Increment wait_->num_waiters before evaluating cond. */ \ 8348657448SKevin Wolf atomic_inc(&wait_->num_waiters); \ 844d22bbf4SKevin Wolf if (ctx_ && in_aio_context_home_thread(ctx_)) { \ 851cc8e54aSKevin Wolf while ((cond)) { \ 861cc8e54aSKevin Wolf aio_poll(ctx_, true); \ 871cc8e54aSKevin Wolf waited_ = true; \ 887719f3c9SStefan Hajnoczi } \ 897719f3c9SStefan Hajnoczi } else { \ 907719f3c9SStefan Hajnoczi assert(qemu_get_current_aio_context() == \ 917719f3c9SStefan Hajnoczi qemu_get_aio_context()); \ 921cc8e54aSKevin Wolf while ((cond)) { \ 934d22bbf4SKevin Wolf if (ctx_) { \ 947719f3c9SStefan Hajnoczi aio_context_release(ctx_); \ 954d22bbf4SKevin Wolf } \ 967719f3c9SStefan Hajnoczi aio_poll(qemu_get_aio_context(), true); \ 974d22bbf4SKevin Wolf if (ctx_) { \ 987719f3c9SStefan Hajnoczi aio_context_acquire(ctx_); \ 994d22bbf4SKevin Wolf } \ 1001cc8e54aSKevin Wolf waited_ = true; \ 1017719f3c9SStefan Hajnoczi } \ 1027719f3c9SStefan Hajnoczi } \ 10348657448SKevin Wolf atomic_dec(&wait_->num_waiters); \ 1047719f3c9SStefan Hajnoczi waited_; }) 1057719f3c9SStefan Hajnoczi 1067719f3c9SStefan Hajnoczi /** 1077719f3c9SStefan Hajnoczi * aio_wait_kick: 1087719f3c9SStefan Hajnoczi * Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During 1097719f3c9SStefan Hajnoczi * synchronous operations performed in an IOThread, the main thread lets the 1107719f3c9SStefan Hajnoczi * IOThread's event loop run, waiting for the operation to complete. A 1117719f3c9SStefan Hajnoczi * aio_wait_kick() call will wake up the main thread. 1127719f3c9SStefan Hajnoczi */ 113cfe29d82SKevin Wolf void aio_wait_kick(void); 1147719f3c9SStefan Hajnoczi 115b89d92f3SStefan Hajnoczi /** 116b89d92f3SStefan Hajnoczi * aio_wait_bh_oneshot: 117b89d92f3SStefan Hajnoczi * @ctx: the aio context 118b89d92f3SStefan Hajnoczi * @cb: the BH callback function 119b89d92f3SStefan Hajnoczi * @opaque: user data for the BH callback function 120b89d92f3SStefan Hajnoczi * 121b89d92f3SStefan Hajnoczi * Run a BH in @ctx and wait for it to complete. 122b89d92f3SStefan Hajnoczi * 123b89d92f3SStefan Hajnoczi * Must be called from the main loop thread with @ctx acquired exactly once. 124b89d92f3SStefan Hajnoczi * Note that main loop event processing may occur. 125b89d92f3SStefan Hajnoczi */ 126b89d92f3SStefan Hajnoczi void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); 127b89d92f3SStefan Hajnoczi 128*3c18a92dSPaolo Bonzini /** 129*3c18a92dSPaolo Bonzini * in_aio_context_home_thread: 130*3c18a92dSPaolo Bonzini * @ctx: the aio context 131*3c18a92dSPaolo Bonzini * 132*3c18a92dSPaolo Bonzini * Return whether we are running in the thread that normally runs @ctx. Note 133*3c18a92dSPaolo Bonzini * that acquiring/releasing ctx does not affect the outcome, each AioContext 134*3c18a92dSPaolo Bonzini * still only has one home thread that is responsible for running it. 135*3c18a92dSPaolo Bonzini */ 136*3c18a92dSPaolo Bonzini static inline bool in_aio_context_home_thread(AioContext *ctx) 137*3c18a92dSPaolo Bonzini { 138*3c18a92dSPaolo Bonzini if (ctx == qemu_get_current_aio_context()) { 139*3c18a92dSPaolo Bonzini return true; 140*3c18a92dSPaolo Bonzini } 141*3c18a92dSPaolo Bonzini 142*3c18a92dSPaolo Bonzini if (ctx == qemu_get_aio_context()) { 143*3c18a92dSPaolo Bonzini return qemu_mutex_iothread_locked(); 144*3c18a92dSPaolo Bonzini } else { 145*3c18a92dSPaolo Bonzini return false; 146*3c18a92dSPaolo Bonzini } 147*3c18a92dSPaolo Bonzini } 148*3c18a92dSPaolo Bonzini 1496834c3f4SMarkus Armbruster #endif /* QEMU_AIO_WAIT_H */ 150