qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

lockcnt.c (11936B)


      1 /*
      2  * QemuLockCnt implementation
      3  *
      4  * Copyright Red Hat, Inc. 2017
      5  *
      6  * Author:
      7  *   Paolo Bonzini <pbonzini@redhat.com>
      8  */
      9 #include "qemu/osdep.h"
     10 #include "qemu/thread.h"
     11 #include "qemu/atomic.h"
     12 #include "trace.h"
     13 
     14 #ifdef CONFIG_LINUX
     15 #include "qemu/futex.h"
     16 
     17 /* On Linux, bits 0-1 are a futex-based lock, bits 2-31 are the counter.
     18  * For the mutex algorithm see Ulrich Drepper's "Futexes Are Tricky" (ok,
     19  * this is not the most relaxing citation I could make...).  It is similar
     20  * to mutex2 in the paper.
     21  */
     22 
     23 #define QEMU_LOCKCNT_STATE_MASK    3
     24 #define QEMU_LOCKCNT_STATE_FREE    0   /* free, uncontended */
     25 #define QEMU_LOCKCNT_STATE_LOCKED  1   /* locked, uncontended */
     26 #define QEMU_LOCKCNT_STATE_WAITING 2   /* locked, contended */
     27 
     28 #define QEMU_LOCKCNT_COUNT_STEP    4
     29 #define QEMU_LOCKCNT_COUNT_SHIFT   2
     30 
     31 void qemu_lockcnt_init(QemuLockCnt *lockcnt)
     32 {
     33     lockcnt->count = 0;
     34 }
     35 
     36 void qemu_lockcnt_destroy(QemuLockCnt *lockcnt)
     37 {
     38 }
     39 
     40 /* *val is the current value of lockcnt->count.
     41  *
     42  * If the lock is free, try a cmpxchg from *val to new_if_free; return
     43  * true and set *val to the old value found by the cmpxchg in
     44  * lockcnt->count.
     45  *
     46  * If the lock is taken, wait for it to be released and return false
     47  * *without trying again to take the lock*.  Again, set *val to the
     48  * new value of lockcnt->count.
     49  *
     50  * If *waited is true on return, new_if_free's bottom two bits must not
     51  * be QEMU_LOCKCNT_STATE_LOCKED on subsequent calls, because the caller
     52  * does not know if there are other waiters.  Furthermore, after *waited
     53  * is set the caller has effectively acquired the lock.  If it returns
     54  * with the lock not taken, it must wake another futex waiter.
     55  */
     56 static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val,
     57                                          int new_if_free, bool *waited)
     58 {
     59     /* Fast path for when the lock is free.  */
     60     if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_FREE) {
     61         int expected = *val;
     62 
     63         trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free);
     64         *val = qatomic_cmpxchg(&lockcnt->count, expected, new_if_free);
     65         if (*val == expected) {
     66             trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free);
     67             *val = new_if_free;
     68             return true;
     69         }
     70     }
     71 
     72     /* The slow path moves from locked to waiting if necessary, then
     73      * does a futex wait.  Both steps can be repeated ad nauseam,
     74      * only getting out of the loop if we can have another shot at the
     75      * fast path.  Once we can, get out to compute the new destination
     76      * value for the fast path.
     77      */
     78     while ((*val & QEMU_LOCKCNT_STATE_MASK) != QEMU_LOCKCNT_STATE_FREE) {
     79         if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_LOCKED) {
     80             int expected = *val;
     81             int new = expected - QEMU_LOCKCNT_STATE_LOCKED + QEMU_LOCKCNT_STATE_WAITING;
     82 
     83             trace_lockcnt_futex_wait_prepare(lockcnt, expected, new);
     84             *val = qatomic_cmpxchg(&lockcnt->count, expected, new);
     85             if (*val == expected) {
     86                 *val = new;
     87             }
     88             continue;
     89         }
     90 
     91         if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_WAITING) {
     92             *waited = true;
     93             trace_lockcnt_futex_wait(lockcnt, *val);
     94             qemu_futex_wait(&lockcnt->count, *val);
     95             *val = qatomic_read(&lockcnt->count);
     96             trace_lockcnt_futex_wait_resume(lockcnt, *val);
     97             continue;
     98         }
     99 
    100         abort();
    101     }
    102     return false;
    103 }
    104 
    105 static void lockcnt_wake(QemuLockCnt *lockcnt)
    106 {
    107     trace_lockcnt_futex_wake(lockcnt);
    108     qemu_futex_wake(&lockcnt->count, 1);
    109 }
    110 
    111 void qemu_lockcnt_inc(QemuLockCnt *lockcnt)
    112 {
    113     int val = qatomic_read(&lockcnt->count);
    114     bool waited = false;
    115 
    116     for (;;) {
    117         if (val >= QEMU_LOCKCNT_COUNT_STEP) {
    118             int expected = val;
    119             val = qatomic_cmpxchg(&lockcnt->count, val,
    120                                   val + QEMU_LOCKCNT_COUNT_STEP);
    121             if (val == expected) {
    122                 break;
    123             }
    124         } else {
    125             /* The fast path is (0, unlocked)->(1, unlocked).  */
    126             if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, QEMU_LOCKCNT_COUNT_STEP,
    127                                              &waited)) {
    128                 break;
    129             }
    130         }
    131     }
    132 
    133     /* If we were woken by another thread, we should also wake one because
    134      * we are effectively releasing the lock that was given to us.  This is
    135      * the case where qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING
    136      * in the low bits, and qemu_lockcnt_inc_and_unlock would find it and
    137      * wake someone.
    138      */
    139     if (waited) {
    140         lockcnt_wake(lockcnt);
    141     }
    142 }
    143 
    144 void qemu_lockcnt_dec(QemuLockCnt *lockcnt)
    145 {
    146     qatomic_sub(&lockcnt->count, QEMU_LOCKCNT_COUNT_STEP);
    147 }
    148 
    149 /* Decrement a counter, and return locked if it is decremented to zero.
    150  * If the function returns true, it is impossible for the counter to
    151  * become nonzero until the next qemu_lockcnt_unlock.
    152  */
    153 bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt)
    154 {
    155     int val = qatomic_read(&lockcnt->count);
    156     int locked_state = QEMU_LOCKCNT_STATE_LOCKED;
    157     bool waited = false;
    158 
    159     for (;;) {
    160         if (val >= 2 * QEMU_LOCKCNT_COUNT_STEP) {
    161             int expected = val;
    162             val = qatomic_cmpxchg(&lockcnt->count, val,
    163                                   val - QEMU_LOCKCNT_COUNT_STEP);
    164             if (val == expected) {
    165                 break;
    166             }
    167         } else {
    168             /* If count is going 1->0, take the lock. The fast path is
    169              * (1, unlocked)->(0, locked) or (1, unlocked)->(0, waiting).
    170              */
    171             if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, locked_state, &waited)) {
    172                 return true;
    173             }
    174 
    175             if (waited) {
    176                 /* At this point we do not know if there are more waiters.  Assume
    177                  * there are.
    178                  */
    179                 locked_state = QEMU_LOCKCNT_STATE_WAITING;
    180             }
    181         }
    182     }
    183 
    184     /* If we were woken by another thread, but we're returning in unlocked
    185      * state, we should also wake a thread because we are effectively
    186      * releasing the lock that was given to us.  This is the case where
    187      * qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING in the low
    188      * bits, and qemu_lockcnt_unlock would find it and wake someone.
    189      */
    190     if (waited) {
    191         lockcnt_wake(lockcnt);
    192     }
    193     return false;
    194 }
    195 
    196 /* If the counter is one, decrement it and return locked.  Otherwise do
    197  * nothing.
    198  *
    199  * If the function returns true, it is impossible for the counter to
    200  * become nonzero until the next qemu_lockcnt_unlock.
    201  */
    202 bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt)
    203 {
    204     int val = qatomic_read(&lockcnt->count);
    205     int locked_state = QEMU_LOCKCNT_STATE_LOCKED;
    206     bool waited = false;
    207 
    208     while (val < 2 * QEMU_LOCKCNT_COUNT_STEP) {
    209         /* If count is going 1->0, take the lock. The fast path is
    210          * (1, unlocked)->(0, locked) or (1, unlocked)->(0, waiting).
    211          */
    212         if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, locked_state, &waited)) {
    213             return true;
    214         }
    215 
    216         if (waited) {
    217             /* At this point we do not know if there are more waiters.  Assume
    218              * there are.
    219              */
    220             locked_state = QEMU_LOCKCNT_STATE_WAITING;
    221         }
    222     }
    223 
    224     /* If we were woken by another thread, but we're returning in unlocked
    225      * state, we should also wake a thread because we are effectively
    226      * releasing the lock that was given to us.  This is the case where
    227      * qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING in the low
    228      * bits, and qemu_lockcnt_inc_and_unlock would find it and wake someone.
    229      */
    230     if (waited) {
    231         lockcnt_wake(lockcnt);
    232     }
    233     return false;
    234 }
    235 
    236 void qemu_lockcnt_lock(QemuLockCnt *lockcnt)
    237 {
    238     int val = qatomic_read(&lockcnt->count);
    239     int step = QEMU_LOCKCNT_STATE_LOCKED;
    240     bool waited = false;
    241 
    242     /* The third argument is only used if the low bits of val are 0
    243      * (QEMU_LOCKCNT_STATE_FREE), so just blindly mix in the desired
    244      * state.
    245      */
    246     while (!qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, val + step, &waited)) {
    247         if (waited) {
    248             /* At this point we do not know if there are more waiters.  Assume
    249              * there are.
    250              */
    251             step = QEMU_LOCKCNT_STATE_WAITING;
    252         }
    253     }
    254 }
    255 
    256 void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt)
    257 {
    258     int expected, new, val;
    259 
    260     val = qatomic_read(&lockcnt->count);
    261     do {
    262         expected = val;
    263         new = (val + QEMU_LOCKCNT_COUNT_STEP) & ~QEMU_LOCKCNT_STATE_MASK;
    264         trace_lockcnt_unlock_attempt(lockcnt, val, new);
    265         val = qatomic_cmpxchg(&lockcnt->count, val, new);
    266     } while (val != expected);
    267 
    268     trace_lockcnt_unlock_success(lockcnt, val, new);
    269     if (val & QEMU_LOCKCNT_STATE_WAITING) {
    270         lockcnt_wake(lockcnt);
    271     }
    272 }
    273 
    274 void qemu_lockcnt_unlock(QemuLockCnt *lockcnt)
    275 {
    276     int expected, new, val;
    277 
    278     val = qatomic_read(&lockcnt->count);
    279     do {
    280         expected = val;
    281         new = val & ~QEMU_LOCKCNT_STATE_MASK;
    282         trace_lockcnt_unlock_attempt(lockcnt, val, new);
    283         val = qatomic_cmpxchg(&lockcnt->count, val, new);
    284     } while (val != expected);
    285 
    286     trace_lockcnt_unlock_success(lockcnt, val, new);
    287     if (val & QEMU_LOCKCNT_STATE_WAITING) {
    288         lockcnt_wake(lockcnt);
    289     }
    290 }
    291 
    292 unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt)
    293 {
    294     return qatomic_read(&lockcnt->count) >> QEMU_LOCKCNT_COUNT_SHIFT;
    295 }
    296 #else
    297 void qemu_lockcnt_init(QemuLockCnt *lockcnt)
    298 {
    299     qemu_mutex_init(&lockcnt->mutex);
    300     lockcnt->count = 0;
    301 }
    302 
    303 void qemu_lockcnt_destroy(QemuLockCnt *lockcnt)
    304 {
    305     qemu_mutex_destroy(&lockcnt->mutex);
    306 }
    307 
    308 void qemu_lockcnt_inc(QemuLockCnt *lockcnt)
    309 {
    310     int old;
    311     for (;;) {
    312         old = qatomic_read(&lockcnt->count);
    313         if (old == 0) {
    314             qemu_lockcnt_lock(lockcnt);
    315             qemu_lockcnt_inc_and_unlock(lockcnt);
    316             return;
    317         } else {
    318             if (qatomic_cmpxchg(&lockcnt->count, old, old + 1) == old) {
    319                 return;
    320             }
    321         }
    322     }
    323 }
    324 
    325 void qemu_lockcnt_dec(QemuLockCnt *lockcnt)
    326 {
    327     qatomic_dec(&lockcnt->count);
    328 }
    329 
    330 /* Decrement a counter, and return locked if it is decremented to zero.
    331  * It is impossible for the counter to become nonzero while the mutex
    332  * is taken.
    333  */
    334 bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt)
    335 {
    336     int val = qatomic_read(&lockcnt->count);
    337     while (val > 1) {
    338         int old = qatomic_cmpxchg(&lockcnt->count, val, val - 1);
    339         if (old != val) {
    340             val = old;
    341             continue;
    342         }
    343 
    344         return false;
    345     }
    346 
    347     qemu_lockcnt_lock(lockcnt);
    348     if (qatomic_fetch_dec(&lockcnt->count) == 1) {
    349         return true;
    350     }
    351 
    352     qemu_lockcnt_unlock(lockcnt);
    353     return false;
    354 }
    355 
    356 /* Decrement a counter and return locked if it is decremented to zero.
    357  * Otherwise do nothing.
    358  *
    359  * It is impossible for the counter to become nonzero while the mutex
    360  * is taken.
    361  */
    362 bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt)
    363 {
    364     /* No need for acquire semantics if we return false.  */
    365     int val = qatomic_read(&lockcnt->count);
    366     if (val > 1) {
    367         return false;
    368     }
    369 
    370     qemu_lockcnt_lock(lockcnt);
    371     if (qatomic_fetch_dec(&lockcnt->count) == 1) {
    372         return true;
    373     }
    374 
    375     qemu_lockcnt_inc_and_unlock(lockcnt);
    376     return false;
    377 }
    378 
    379 void qemu_lockcnt_lock(QemuLockCnt *lockcnt)
    380 {
    381     qemu_mutex_lock(&lockcnt->mutex);
    382 }
    383 
    384 void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt)
    385 {
    386     qatomic_inc(&lockcnt->count);
    387     qemu_mutex_unlock(&lockcnt->mutex);
    388 }
    389 
    390 void qemu_lockcnt_unlock(QemuLockCnt *lockcnt)
    391 {
    392     qemu_mutex_unlock(&lockcnt->mutex);
    393 }
    394 
    395 unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt)
    396 {
    397     return qatomic_read(&lockcnt->count);
    398 }
    399 #endif