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#include "qemu/cacheinfo.h"
  11#include "qemu/memalign.h"
  12
  13#ifdef CONFIG_ATOMIC64
  14#error This file must only be compiled if !CONFIG_ATOMIC64
  15#endif
  16
  17/*
  18 * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks.
  19 * We use an array of spinlocks, with padding computed at run-time based on
  20 * the host's dcache line size.
  21 * We point to the array with a void * to simplify the padding's computation.
  22 * Each spinlock is located every lock_size bytes.
  23 */
  24static void *lock_array;
  25static size_t lock_size;
  26
  27/*
  28 * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a
  29 * small array of locks.
  30 */
  31#define NR_LOCKS 16
  32
  33static QemuSpin *addr_to_lock(const void *addr)
  34{
  35    uintptr_t a = (uintptr_t)addr;
  36    uintptr_t idx;
  37
  38    idx = a >> qemu_dcache_linesize_log;
  39    idx ^= (idx >> 8) ^ (idx >> 16);
  40    idx &= NR_LOCKS - 1;
  41    return lock_array + idx * lock_size;
  42}
  43
  44#define GEN_READ(name, type)                    \
  45    type name(const type *ptr)                  \
  46    {                                           \
  47        QemuSpin *lock = addr_to_lock(ptr);     \
  48        type ret;                               \
  49                                                \
  50        qemu_spin_lock(lock);                   \
  51        ret = *ptr;                             \
  52        qemu_spin_unlock(lock);                 \
  53        return ret;                             \
  54    }
  55
  56GEN_READ(qatomic_read_i64, int64_t)
  57GEN_READ(qatomic_read_u64, uint64_t)
  58#undef GEN_READ
  59
  60#define GEN_SET(name, type)                     \
  61    void name(type *ptr, type val)              \
  62    {                                           \
  63        QemuSpin *lock = addr_to_lock(ptr);     \
  64                                                \
  65        qemu_spin_lock(lock);                   \
  66        *ptr = val;                             \
  67        qemu_spin_unlock(lock);                 \
  68    }
  69
  70GEN_SET(qatomic_set_i64, int64_t)
  71GEN_SET(qatomic_set_u64, uint64_t)
  72#undef GEN_SET
  73
  74void qatomic64_init(void)
  75{
  76    int i;
  77
  78    lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize);
  79    lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS);
  80    for (i = 0; i < NR_LOCKS; i++) {
  81        QemuSpin *lock = lock_array + i * lock_size;
  82
  83        qemu_spin_init(lock);
  84    }
  85}
  86