xref: /openbmc/qemu/util/event.c (revision 96215036f47403438c7c7869b7cd419bd7a11f82)
1*69e10db8SAkihiko Odaki /* SPDX-License-Identifier: GPL-2.0-or-later */
2*69e10db8SAkihiko Odaki 
3*69e10db8SAkihiko Odaki #include "qemu/osdep.h"
4*69e10db8SAkihiko Odaki #include "qemu/thread.h"
5*69e10db8SAkihiko Odaki 
6*69e10db8SAkihiko Odaki /*
7*69e10db8SAkihiko Odaki  * Valid transitions:
8*69e10db8SAkihiko Odaki  * - FREE -> SET (qemu_event_set)
9*69e10db8SAkihiko Odaki  * - BUSY -> SET (qemu_event_set)
10*69e10db8SAkihiko Odaki  * - SET -> FREE (qemu_event_reset)
11*69e10db8SAkihiko Odaki  * - FREE -> BUSY (qemu_event_wait)
12*69e10db8SAkihiko Odaki  *
13*69e10db8SAkihiko Odaki  * With futex, the waking and blocking operations follow
14*69e10db8SAkihiko Odaki  * BUSY -> SET and FREE -> BUSY, respectively.
15*69e10db8SAkihiko Odaki  *
16*69e10db8SAkihiko Odaki  * Without futex, BUSY -> SET and FREE -> BUSY never happen. Instead, the waking
17*69e10db8SAkihiko Odaki  * operation follows FREE -> SET and the blocking operation will happen in
18*69e10db8SAkihiko Odaki  * qemu_event_wait() if the event is not SET.
19*69e10db8SAkihiko Odaki  *
20*69e10db8SAkihiko Odaki  * SET->BUSY does not happen (it can be observed from the outside but
21*69e10db8SAkihiko Odaki  * it really is SET->FREE->BUSY).
22*69e10db8SAkihiko Odaki  *
23*69e10db8SAkihiko Odaki  * busy->free provably cannot happen; to enforce it, the set->free transition
24*69e10db8SAkihiko Odaki  * is done with an OR, which becomes a no-op if the event has concurrently
25*69e10db8SAkihiko Odaki  * transitioned to free or busy.
26*69e10db8SAkihiko Odaki  */
27*69e10db8SAkihiko Odaki 
28*69e10db8SAkihiko Odaki #define EV_SET         0
29*69e10db8SAkihiko Odaki #define EV_FREE        1
30*69e10db8SAkihiko Odaki #define EV_BUSY       -1
31*69e10db8SAkihiko Odaki 
qemu_event_init(QemuEvent * ev,bool init)32*69e10db8SAkihiko Odaki void qemu_event_init(QemuEvent *ev, bool init)
33*69e10db8SAkihiko Odaki {
34*69e10db8SAkihiko Odaki #ifndef HAVE_FUTEX
35*69e10db8SAkihiko Odaki     pthread_mutex_init(&ev->lock, NULL);
36*69e10db8SAkihiko Odaki     pthread_cond_init(&ev->cond, NULL);
37*69e10db8SAkihiko Odaki #endif
38*69e10db8SAkihiko Odaki 
39*69e10db8SAkihiko Odaki     ev->value = (init ? EV_SET : EV_FREE);
40*69e10db8SAkihiko Odaki     ev->initialized = true;
41*69e10db8SAkihiko Odaki }
42*69e10db8SAkihiko Odaki 
qemu_event_destroy(QemuEvent * ev)43*69e10db8SAkihiko Odaki void qemu_event_destroy(QemuEvent *ev)
44*69e10db8SAkihiko Odaki {
45*69e10db8SAkihiko Odaki     assert(ev->initialized);
46*69e10db8SAkihiko Odaki     ev->initialized = false;
47*69e10db8SAkihiko Odaki #ifndef HAVE_FUTEX
48*69e10db8SAkihiko Odaki     pthread_mutex_destroy(&ev->lock);
49*69e10db8SAkihiko Odaki     pthread_cond_destroy(&ev->cond);
50*69e10db8SAkihiko Odaki #endif
51*69e10db8SAkihiko Odaki }
52*69e10db8SAkihiko Odaki 
qemu_event_set(QemuEvent * ev)53*69e10db8SAkihiko Odaki void qemu_event_set(QemuEvent *ev)
54*69e10db8SAkihiko Odaki {
55*69e10db8SAkihiko Odaki     assert(ev->initialized);
56*69e10db8SAkihiko Odaki 
57*69e10db8SAkihiko Odaki #ifdef HAVE_FUTEX
58*69e10db8SAkihiko Odaki     /*
59*69e10db8SAkihiko Odaki      * Pairs with both qemu_event_reset() and qemu_event_wait().
60*69e10db8SAkihiko Odaki      *
61*69e10db8SAkihiko Odaki      * qemu_event_set has release semantics, but because it *loads*
62*69e10db8SAkihiko Odaki      * ev->value we need a full memory barrier here.
63*69e10db8SAkihiko Odaki      */
64*69e10db8SAkihiko Odaki     smp_mb();
65*69e10db8SAkihiko Odaki     if (qatomic_read(&ev->value) != EV_SET) {
66*69e10db8SAkihiko Odaki         int old = qatomic_xchg(&ev->value, EV_SET);
67*69e10db8SAkihiko Odaki 
68*69e10db8SAkihiko Odaki         /* Pairs with memory barrier in kernel futex_wait system call.  */
69*69e10db8SAkihiko Odaki         smp_mb__after_rmw();
70*69e10db8SAkihiko Odaki         if (old == EV_BUSY) {
71*69e10db8SAkihiko Odaki             /* There were waiters, wake them up.  */
72*69e10db8SAkihiko Odaki             qemu_futex_wake_all(ev);
73*69e10db8SAkihiko Odaki         }
74*69e10db8SAkihiko Odaki     }
75*69e10db8SAkihiko Odaki #else
76*69e10db8SAkihiko Odaki     pthread_mutex_lock(&ev->lock);
77*69e10db8SAkihiko Odaki     /* Pairs with qemu_event_reset()'s load acquire.  */
78*69e10db8SAkihiko Odaki     qatomic_store_release(&ev->value, EV_SET);
79*69e10db8SAkihiko Odaki     pthread_cond_broadcast(&ev->cond);
80*69e10db8SAkihiko Odaki     pthread_mutex_unlock(&ev->lock);
81*69e10db8SAkihiko Odaki #endif
82*69e10db8SAkihiko Odaki }
83*69e10db8SAkihiko Odaki 
qemu_event_reset(QemuEvent * ev)84*69e10db8SAkihiko Odaki void qemu_event_reset(QemuEvent *ev)
85*69e10db8SAkihiko Odaki {
86*69e10db8SAkihiko Odaki     assert(ev->initialized);
87*69e10db8SAkihiko Odaki 
88*69e10db8SAkihiko Odaki #ifdef HAVE_FUTEX
89*69e10db8SAkihiko Odaki     /*
90*69e10db8SAkihiko Odaki      * If there was a concurrent reset (or even reset+wait),
91*69e10db8SAkihiko Odaki      * do nothing.  Otherwise change EV_SET->EV_FREE.
92*69e10db8SAkihiko Odaki      */
93*69e10db8SAkihiko Odaki     qatomic_or(&ev->value, EV_FREE);
94*69e10db8SAkihiko Odaki 
95*69e10db8SAkihiko Odaki     /*
96*69e10db8SAkihiko Odaki      * Order reset before checking the condition in the caller.
97*69e10db8SAkihiko Odaki      * Pairs with the first memory barrier in qemu_event_set().
98*69e10db8SAkihiko Odaki      */
99*69e10db8SAkihiko Odaki     smp_mb__after_rmw();
100*69e10db8SAkihiko Odaki #else
101*69e10db8SAkihiko Odaki     /*
102*69e10db8SAkihiko Odaki      * If futexes are not available, there are no EV_FREE->EV_BUSY
103*69e10db8SAkihiko Odaki      * transitions because wakeups are done entirely through the
104*69e10db8SAkihiko Odaki      * condition variable.  Since qatomic_set() only writes EV_FREE,
105*69e10db8SAkihiko Odaki      * the load seems useless but in reality, the acquire synchronizes
106*69e10db8SAkihiko Odaki      * with qemu_event_set()'s store release: if qemu_event_reset()
107*69e10db8SAkihiko Odaki      * sees EV_SET here, then the caller will certainly see a
108*69e10db8SAkihiko Odaki      * successful condition and skip qemu_event_wait():
109*69e10db8SAkihiko Odaki      *
110*69e10db8SAkihiko Odaki      * done = 1;                 if (done == 0)
111*69e10db8SAkihiko Odaki      * qemu_event_set() {          qemu_event_reset() {
112*69e10db8SAkihiko Odaki      *   lock();
113*69e10db8SAkihiko Odaki      *   ev->value = EV_SET ----->     load ev->value
114*69e10db8SAkihiko Odaki      *                                 ev->value = old value | EV_FREE
115*69e10db8SAkihiko Odaki      *   cond_broadcast()
116*69e10db8SAkihiko Odaki      *   unlock();                 }
117*69e10db8SAkihiko Odaki      * }                           if (done == 0)
118*69e10db8SAkihiko Odaki      *                               // qemu_event_wait() not called
119*69e10db8SAkihiko Odaki      */
120*69e10db8SAkihiko Odaki     qatomic_set(&ev->value, qatomic_load_acquire(&ev->value) | EV_FREE);
121*69e10db8SAkihiko Odaki #endif
122*69e10db8SAkihiko Odaki }
123*69e10db8SAkihiko Odaki 
qemu_event_wait(QemuEvent * ev)124*69e10db8SAkihiko Odaki void qemu_event_wait(QemuEvent *ev)
125*69e10db8SAkihiko Odaki {
126*69e10db8SAkihiko Odaki     assert(ev->initialized);
127*69e10db8SAkihiko Odaki 
128*69e10db8SAkihiko Odaki #ifdef HAVE_FUTEX
129*69e10db8SAkihiko Odaki     while (true) {
130*69e10db8SAkihiko Odaki         /*
131*69e10db8SAkihiko Odaki          * qemu_event_wait must synchronize with qemu_event_set even if it does
132*69e10db8SAkihiko Odaki          * not go down the slow path, so this load-acquire is needed that
133*69e10db8SAkihiko Odaki          * synchronizes with the first memory barrier in qemu_event_set().
134*69e10db8SAkihiko Odaki          */
135*69e10db8SAkihiko Odaki         unsigned value = qatomic_load_acquire(&ev->value);
136*69e10db8SAkihiko Odaki         if (value == EV_SET) {
137*69e10db8SAkihiko Odaki             break;
138*69e10db8SAkihiko Odaki         }
139*69e10db8SAkihiko Odaki 
140*69e10db8SAkihiko Odaki         if (value == EV_FREE) {
141*69e10db8SAkihiko Odaki             /*
142*69e10db8SAkihiko Odaki              * Leave the event reset and tell qemu_event_set that there are
143*69e10db8SAkihiko Odaki              * waiters.  No need to retry, because there cannot be a concurrent
144*69e10db8SAkihiko Odaki              * busy->free transition.  After the CAS, the event will be either
145*69e10db8SAkihiko Odaki              * set or busy.
146*69e10db8SAkihiko Odaki              *
147*69e10db8SAkihiko Odaki              * This cmpxchg doesn't have particular ordering requirements if it
148*69e10db8SAkihiko Odaki              * succeeds (moving the store earlier can only cause
149*69e10db8SAkihiko Odaki              * qemu_event_set() to issue _more_ wakeups), the failing case needs
150*69e10db8SAkihiko Odaki              * acquire semantics like the load above.
151*69e10db8SAkihiko Odaki              */
152*69e10db8SAkihiko Odaki             if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) {
153*69e10db8SAkihiko Odaki                 break;
154*69e10db8SAkihiko Odaki             }
155*69e10db8SAkihiko Odaki         }
156*69e10db8SAkihiko Odaki 
157*69e10db8SAkihiko Odaki         /*
158*69e10db8SAkihiko Odaki          * This is the final check for a concurrent set, so it does need
159*69e10db8SAkihiko Odaki          * a smp_mb() pairing with the second barrier of qemu_event_set().
160*69e10db8SAkihiko Odaki          * The barrier is inside the FUTEX_WAIT system call.
161*69e10db8SAkihiko Odaki          */
162*69e10db8SAkihiko Odaki         qemu_futex_wait(ev, EV_BUSY);
163*69e10db8SAkihiko Odaki     }
164*69e10db8SAkihiko Odaki #else
165*69e10db8SAkihiko Odaki     pthread_mutex_lock(&ev->lock);
166*69e10db8SAkihiko Odaki     while (qatomic_read(&ev->value) != EV_SET) {
167*69e10db8SAkihiko Odaki         pthread_cond_wait(&ev->cond, &ev->lock);
168*69e10db8SAkihiko Odaki     }
169*69e10db8SAkihiko Odaki     pthread_mutex_unlock(&ev->lock);
170*69e10db8SAkihiko Odaki #endif
171*69e10db8SAkihiko Odaki }
172