cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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
     31void qemu_lockcnt_init(QemuLockCnt *lockcnt)
     32{
     33    lockcnt->count = 0;
     34}
     35
     36void 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 */
     56static 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
    105static void lockcnt_wake(QemuLockCnt *lockcnt)
    106{
    107    trace_lockcnt_futex_wake(lockcnt);
    108    qemu_futex_wake(&lockcnt->count, 1);
    109}
    110
    111void 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
    144void 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 */
    153bool 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 */
    202bool 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
    236void 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
    256void 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
    274void 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
    292unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt)
    293{
    294    return qatomic_read(&lockcnt->count) >> QEMU_LOCKCNT_COUNT_SHIFT;
    295}
    296#else
    297void qemu_lockcnt_init(QemuLockCnt *lockcnt)
    298{
    299    qemu_mutex_init(&lockcnt->mutex);
    300    lockcnt->count = 0;
    301}
    302
    303void qemu_lockcnt_destroy(QemuLockCnt *lockcnt)
    304{
    305    qemu_mutex_destroy(&lockcnt->mutex);
    306}
    307
    308void 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
    325void 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 */
    334bool 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 */
    362bool 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
    379void qemu_lockcnt_lock(QemuLockCnt *lockcnt)
    380{
    381    qemu_mutex_lock(&lockcnt->mutex);
    382}
    383
    384void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt)
    385{
    386    qatomic_inc(&lockcnt->count);
    387    qemu_mutex_unlock(&lockcnt->mutex);
    388}
    389
    390void qemu_lockcnt_unlock(QemuLockCnt *lockcnt)
    391{
    392    qemu_mutex_unlock(&lockcnt->mutex);
    393}
    394
    395unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt)
    396{
    397    return qatomic_read(&lockcnt->count);
    398}
    399#endif