xref: /openbmc/qemu/util/atomic64.c (revision 5df022cf)
1782da5b2SEmilio G. Cota /*
2782da5b2SEmilio G. Cota  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
3782da5b2SEmilio G. Cota  *
4782da5b2SEmilio G. Cota  * License: GNU GPL, version 2 or later.
5782da5b2SEmilio G. Cota  *   See the COPYING file in the top-level directory.
6782da5b2SEmilio G. Cota  */
7782da5b2SEmilio G. Cota #include "qemu/osdep.h"
8782da5b2SEmilio G. Cota #include "qemu/atomic.h"
9782da5b2SEmilio G. Cota #include "qemu/thread.h"
10ad768e6fSPeter Maydell #include "qemu/cacheinfo.h"
11*5df022cfSPeter Maydell #include "qemu/memalign.h"
12782da5b2SEmilio G. Cota 
13782da5b2SEmilio G. Cota #ifdef CONFIG_ATOMIC64
14782da5b2SEmilio G. Cota #error This file must only be compiled if !CONFIG_ATOMIC64
15782da5b2SEmilio G. Cota #endif
16782da5b2SEmilio G. Cota 
17782da5b2SEmilio G. Cota /*
18782da5b2SEmilio G. Cota  * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks.
19782da5b2SEmilio G. Cota  * We use an array of spinlocks, with padding computed at run-time based on
20782da5b2SEmilio G. Cota  * the host's dcache line size.
21782da5b2SEmilio G. Cota  * We point to the array with a void * to simplify the padding's computation.
22782da5b2SEmilio G. Cota  * Each spinlock is located every lock_size bytes.
23782da5b2SEmilio G. Cota  */
24782da5b2SEmilio G. Cota static void *lock_array;
25782da5b2SEmilio G. Cota static size_t lock_size;
26782da5b2SEmilio G. Cota 
27782da5b2SEmilio G. Cota /*
28782da5b2SEmilio G. Cota  * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a
29782da5b2SEmilio G. Cota  * small array of locks.
30782da5b2SEmilio G. Cota  */
31782da5b2SEmilio G. Cota #define NR_LOCKS 16
32782da5b2SEmilio G. Cota 
addr_to_lock(const void * addr)33782da5b2SEmilio G. Cota static QemuSpin *addr_to_lock(const void *addr)
34782da5b2SEmilio G. Cota {
35782da5b2SEmilio G. Cota     uintptr_t a = (uintptr_t)addr;
36782da5b2SEmilio G. Cota     uintptr_t idx;
37782da5b2SEmilio G. Cota 
38782da5b2SEmilio G. Cota     idx = a >> qemu_dcache_linesize_log;
39782da5b2SEmilio G. Cota     idx ^= (idx >> 8) ^ (idx >> 16);
40782da5b2SEmilio G. Cota     idx &= NR_LOCKS - 1;
41782da5b2SEmilio G. Cota     return lock_array + idx * lock_size;
42782da5b2SEmilio G. Cota }
43782da5b2SEmilio G. Cota 
44782da5b2SEmilio G. Cota #define GEN_READ(name, type)                    \
45782da5b2SEmilio G. Cota     type name(const type *ptr)                  \
46782da5b2SEmilio G. Cota     {                                           \
47782da5b2SEmilio G. Cota         QemuSpin *lock = addr_to_lock(ptr);     \
48782da5b2SEmilio G. Cota         type ret;                               \
49782da5b2SEmilio G. Cota                                                 \
50782da5b2SEmilio G. Cota         qemu_spin_lock(lock);                   \
51782da5b2SEmilio G. Cota         ret = *ptr;                             \
52782da5b2SEmilio G. Cota         qemu_spin_unlock(lock);                 \
53782da5b2SEmilio G. Cota         return ret;                             \
54782da5b2SEmilio G. Cota     }
55782da5b2SEmilio G. Cota 
GEN_READ(qatomic_read_i64,int64_t)56d73415a3SStefan Hajnoczi GEN_READ(qatomic_read_i64, int64_t)
57d73415a3SStefan Hajnoczi GEN_READ(qatomic_read_u64, uint64_t)
58782da5b2SEmilio G. Cota #undef GEN_READ
59782da5b2SEmilio G. Cota 
60782da5b2SEmilio G. Cota #define GEN_SET(name, type)                     \
61782da5b2SEmilio G. Cota     void name(type *ptr, type val)              \
62782da5b2SEmilio G. Cota     {                                           \
63782da5b2SEmilio G. Cota         QemuSpin *lock = addr_to_lock(ptr);     \
64782da5b2SEmilio G. Cota                                                 \
65782da5b2SEmilio G. Cota         qemu_spin_lock(lock);                   \
66782da5b2SEmilio G. Cota         *ptr = val;                             \
67782da5b2SEmilio G. Cota         qemu_spin_unlock(lock);                 \
68782da5b2SEmilio G. Cota     }
69782da5b2SEmilio G. Cota 
70d73415a3SStefan Hajnoczi GEN_SET(qatomic_set_i64, int64_t)
71d73415a3SStefan Hajnoczi GEN_SET(qatomic_set_u64, uint64_t)
72782da5b2SEmilio G. Cota #undef GEN_SET
73782da5b2SEmilio G. Cota 
74d73415a3SStefan Hajnoczi void qatomic64_init(void)
75782da5b2SEmilio G. Cota {
76782da5b2SEmilio G. Cota     int i;
77782da5b2SEmilio G. Cota 
78782da5b2SEmilio G. Cota     lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize);
79782da5b2SEmilio G. Cota     lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS);
80782da5b2SEmilio G. Cota     for (i = 0; i < NR_LOCKS; i++) {
81782da5b2SEmilio G. Cota         QemuSpin *lock = lock_array + i * lock_size;
82782da5b2SEmilio G. Cota 
83782da5b2SEmilio G. Cota         qemu_spin_init(lock);
84782da5b2SEmilio G. Cota     }
85782da5b2SEmilio G. Cota }
86