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