|  | /* SPDX-License-Identifier: GPL-2.0-or-later */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/thread.h" | 
|  |  | 
|  | /* | 
|  | * Valid transitions: | 
|  | * - FREE -> SET (qemu_event_set) | 
|  | * - BUSY -> SET (qemu_event_set) | 
|  | * - SET -> FREE (qemu_event_reset) | 
|  | * - FREE -> BUSY (qemu_event_wait) | 
|  | * | 
|  | * With futex, the waking and blocking operations follow | 
|  | * BUSY -> SET and FREE -> BUSY, respectively. | 
|  | * | 
|  | * Without futex, BUSY -> SET and FREE -> BUSY never happen. Instead, the waking | 
|  | * operation follows FREE -> SET and the blocking operation will happen in | 
|  | * qemu_event_wait() if the event is not SET. | 
|  | * | 
|  | * SET->BUSY does not happen (it can be observed from the outside but | 
|  | * it really is SET->FREE->BUSY). | 
|  | * | 
|  | * busy->free provably cannot happen; to enforce it, the set->free transition | 
|  | * is done with an OR, which becomes a no-op if the event has concurrently | 
|  | * transitioned to free or busy. | 
|  | */ | 
|  |  | 
|  | #define EV_SET         0 | 
|  | #define EV_FREE        1 | 
|  | #define EV_BUSY       -1 | 
|  |  | 
|  | void qemu_event_init(QemuEvent *ev, bool init) | 
|  | { | 
|  | #ifndef HAVE_FUTEX | 
|  | pthread_mutex_init(&ev->lock, NULL); | 
|  | pthread_cond_init(&ev->cond, NULL); | 
|  | #endif | 
|  |  | 
|  | ev->value = (init ? EV_SET : EV_FREE); | 
|  | ev->initialized = true; | 
|  | } | 
|  |  | 
|  | void qemu_event_destroy(QemuEvent *ev) | 
|  | { | 
|  | assert(ev->initialized); | 
|  | ev->initialized = false; | 
|  | #ifndef HAVE_FUTEX | 
|  | pthread_mutex_destroy(&ev->lock); | 
|  | pthread_cond_destroy(&ev->cond); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void qemu_event_set(QemuEvent *ev) | 
|  | { | 
|  | assert(ev->initialized); | 
|  |  | 
|  | #ifdef HAVE_FUTEX | 
|  | /* | 
|  | * Pairs with both qemu_event_reset() and qemu_event_wait(). | 
|  | * | 
|  | * qemu_event_set has release semantics, but because it *loads* | 
|  | * ev->value we need a full memory barrier here. | 
|  | */ | 
|  | smp_mb(); | 
|  | if (qatomic_read(&ev->value) != EV_SET) { | 
|  | int old = qatomic_xchg(&ev->value, EV_SET); | 
|  |  | 
|  | /* Pairs with memory barrier in kernel futex_wait system call.  */ | 
|  | smp_mb__after_rmw(); | 
|  | if (old == EV_BUSY) { | 
|  | /* There were waiters, wake them up.  */ | 
|  | qemu_futex_wake_all(ev); | 
|  | } | 
|  | } | 
|  | #else | 
|  | pthread_mutex_lock(&ev->lock); | 
|  | /* Pairs with qemu_event_reset()'s load acquire.  */ | 
|  | qatomic_store_release(&ev->value, EV_SET); | 
|  | pthread_cond_broadcast(&ev->cond); | 
|  | pthread_mutex_unlock(&ev->lock); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void qemu_event_reset(QemuEvent *ev) | 
|  | { | 
|  | assert(ev->initialized); | 
|  |  | 
|  | #ifdef HAVE_FUTEX | 
|  | /* | 
|  | * If there was a concurrent reset (or even reset+wait), | 
|  | * do nothing.  Otherwise change EV_SET->EV_FREE. | 
|  | */ | 
|  | qatomic_or(&ev->value, EV_FREE); | 
|  |  | 
|  | /* | 
|  | * Order reset before checking the condition in the caller. | 
|  | * Pairs with the first memory barrier in qemu_event_set(). | 
|  | */ | 
|  | smp_mb__after_rmw(); | 
|  | #else | 
|  | /* | 
|  | * If futexes are not available, there are no EV_FREE->EV_BUSY | 
|  | * transitions because wakeups are done entirely through the | 
|  | * condition variable.  Since qatomic_set() only writes EV_FREE, | 
|  | * the load seems useless but in reality, the acquire synchronizes | 
|  | * with qemu_event_set()'s store release: if qemu_event_reset() | 
|  | * sees EV_SET here, then the caller will certainly see a | 
|  | * successful condition and skip qemu_event_wait(): | 
|  | * | 
|  | * done = 1;                 if (done == 0) | 
|  | * qemu_event_set() {          qemu_event_reset() { | 
|  | *   lock(); | 
|  | *   ev->value = EV_SET ----->     load ev->value | 
|  | *                                 ev->value = old value | EV_FREE | 
|  | *   cond_broadcast() | 
|  | *   unlock();                 } | 
|  | * }                           if (done == 0) | 
|  | *                               // qemu_event_wait() not called | 
|  | */ | 
|  | qatomic_set(&ev->value, qatomic_load_acquire(&ev->value) | EV_FREE); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void qemu_event_wait(QemuEvent *ev) | 
|  | { | 
|  | assert(ev->initialized); | 
|  |  | 
|  | #ifdef HAVE_FUTEX | 
|  | while (true) { | 
|  | /* | 
|  | * qemu_event_wait must synchronize with qemu_event_set even if it does | 
|  | * not go down the slow path, so this load-acquire is needed that | 
|  | * synchronizes with the first memory barrier in qemu_event_set(). | 
|  | */ | 
|  | unsigned value = qatomic_load_acquire(&ev->value); | 
|  | if (value == EV_SET) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (value == EV_FREE) { | 
|  | /* | 
|  | * Leave the event reset and tell qemu_event_set that there are | 
|  | * waiters.  No need to retry, because there cannot be a concurrent | 
|  | * busy->free transition.  After the CAS, the event will be either | 
|  | * set or busy. | 
|  | * | 
|  | * This cmpxchg doesn't have particular ordering requirements if it | 
|  | * succeeds (moving the store earlier can only cause | 
|  | * qemu_event_set() to issue _more_ wakeups), the failing case needs | 
|  | * acquire semantics like the load above. | 
|  | */ | 
|  | if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is the final check for a concurrent set, so it does need | 
|  | * a smp_mb() pairing with the second barrier of qemu_event_set(). | 
|  | * The barrier is inside the FUTEX_WAIT system call. | 
|  | */ | 
|  | qemu_futex_wait(ev, EV_BUSY); | 
|  | } | 
|  | #else | 
|  | pthread_mutex_lock(&ev->lock); | 
|  | while (qatomic_read(&ev->value) != EV_SET) { | 
|  | pthread_cond_wait(&ev->cond, &ev->lock); | 
|  | } | 
|  | pthread_mutex_unlock(&ev->lock); | 
|  | #endif | 
|  | } |