| /* | 
 |  * 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); | 
 |     } | 
 | } |