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 */ 24 static void *lock_array; 25 static 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 33 static 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 56 GEN_READ(qatomic_read_i64, int64_t) 57 GEN_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 70 GEN_SET(qatomic_set_i64, int64_t) 71 GEN_SET(qatomic_set_u64, uint64_t) 72 #undef GEN_SET 73 74 void 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