qemu/util/atomic64.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
   3 *
   4 * License: GNU GPL, version 2 or later.
   5 *   See the COPYING file in the top-level directory.
   6 */
   7#include "qemu/osdep.h"
   8#include "qemu/atomic.h"
   9#include "qemu/thread.h"
  10
  11#ifdef CONFIG_ATOMIC64
  12#error This file must only be compiled if !CONFIG_ATOMIC64
  13#endif
  14
  15/*
  16 * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks.
  17 * We use an array of spinlocks, with padding computed at run-time based on
  18 * the host's dcache line size.
  19 * We point to the array with a void * to simplify the padding's computation.
  20 * Each spinlock is located every lock_size bytes.
  21 */
  22static void *lock_array;
  23static size_t lock_size;
  24
  25/*
  26 * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a
  27 * small array of locks.
  28 */
  29#define NR_LOCKS 16
  30
  31static QemuSpin *addr_to_lock(const void *addr)
  32{
  33    uintptr_t a = (uintptr_t)addr;
  34    uintptr_t idx;
  35
  36    idx = a >> qemu_dcache_linesize_log;
  37    idx ^= (idx >> 8) ^ (idx >> 16);
  38    idx &= NR_LOCKS - 1;
  39    return lock_array + idx * lock_size;
  40}
  41
  42#define GEN_READ(name, type)                    \
  43    type name(const type *ptr)                  \
  44    {                                           \
  45        QemuSpin *lock = addr_to_lock(ptr);     \
  46        type ret;                               \
  47                                                \
  48        qemu_spin_lock(lock);                   \
  49        ret = *ptr;                             \
  50        qemu_spin_unlock(lock);                 \
  51        return ret;                             \
  52    }
  53
  54GEN_READ(atomic_read_i64, int64_t)
  55GEN_READ(atomic_read_u64, uint64_t)
  56#undef GEN_READ
  57
  58#define GEN_SET(name, type)                     \
  59    void name(type *ptr, type val)              \
  60    {                                           \
  61        QemuSpin *lock = addr_to_lock(ptr);     \
  62                                                \
  63        qemu_spin_lock(lock);                   \
  64        *ptr = val;                             \
  65        qemu_spin_unlock(lock);                 \
  66    }
  67
  68GEN_SET(atomic_set_i64, int64_t)
  69GEN_SET(atomic_set_u64, uint64_t)
  70#undef GEN_SET
  71
  72void atomic64_init(void)
  73{
  74    int i;
  75
  76    lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize);
  77    lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS);
  78    for (i = 0; i < NR_LOCKS; i++) {
  79        QemuSpin *lock = lock_array + i * lock_size;
  80
  81        qemu_spin_init(lock);
  82    }
  83}
  84