|  | /* | 
|  | * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> | 
|  | * | 
|  | * License: GNU GPL, version 2 or later. | 
|  | *   See the COPYING file in the top-level directory. | 
|  | */ | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/atomic.h" | 
|  | #include "qemu/thread.h" | 
|  | #include "qemu/cacheinfo.h" | 
|  | #include "qemu/memalign.h" | 
|  |  | 
|  | #ifdef CONFIG_ATOMIC64 | 
|  | #error This file must only be compiled if !CONFIG_ATOMIC64 | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks. | 
|  | * We use an array of spinlocks, with padding computed at run-time based on | 
|  | * the host's dcache line size. | 
|  | * We point to the array with a void * to simplify the padding's computation. | 
|  | * Each spinlock is located every lock_size bytes. | 
|  | */ | 
|  | static void *lock_array; | 
|  | static size_t lock_size; | 
|  |  | 
|  | /* | 
|  | * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a | 
|  | * small array of locks. | 
|  | */ | 
|  | #define NR_LOCKS 16 | 
|  |  | 
|  | static QemuSpin *addr_to_lock(const void *addr) | 
|  | { | 
|  | uintptr_t a = (uintptr_t)addr; | 
|  | uintptr_t idx; | 
|  |  | 
|  | idx = a >> qemu_dcache_linesize_log; | 
|  | idx ^= (idx >> 8) ^ (idx >> 16); | 
|  | idx &= NR_LOCKS - 1; | 
|  | return lock_array + idx * lock_size; | 
|  | } | 
|  |  | 
|  | #define GEN_READ(name, type)                    \ | 
|  | type name(const type *ptr)                  \ | 
|  | {                                           \ | 
|  | QemuSpin *lock = addr_to_lock(ptr);     \ | 
|  | type ret;                               \ | 
|  | \ | 
|  | qemu_spin_lock(lock);                   \ | 
|  | ret = *ptr;                             \ | 
|  | qemu_spin_unlock(lock);                 \ | 
|  | return ret;                             \ | 
|  | } | 
|  |  | 
|  | GEN_READ(qatomic_read_i64, int64_t) | 
|  | GEN_READ(qatomic_read_u64, uint64_t) | 
|  | #undef GEN_READ | 
|  |  | 
|  | #define GEN_SET(name, type)                     \ | 
|  | void name(type *ptr, type val)              \ | 
|  | {                                           \ | 
|  | QemuSpin *lock = addr_to_lock(ptr);     \ | 
|  | \ | 
|  | qemu_spin_lock(lock);                   \ | 
|  | *ptr = val;                             \ | 
|  | qemu_spin_unlock(lock);                 \ | 
|  | } | 
|  |  | 
|  | GEN_SET(qatomic_set_i64, int64_t) | 
|  | GEN_SET(qatomic_set_u64, uint64_t) | 
|  | #undef GEN_SET | 
|  |  | 
|  | void qatomic64_init(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize); | 
|  | lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS); | 
|  | for (i = 0; i < NR_LOCKS; i++) { | 
|  | QemuSpin *lock = lock_array + i * lock_size; | 
|  |  | 
|  | qemu_spin_init(lock); | 
|  | } | 
|  | } |