]>
Commit | Line | Data |
---|---|---|
782da5b2 EC |
1 | /* |
2 | * Copyright (C) 2018, Emilio G. Cota <[email protected]> | |
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" | |
ad768e6f | 10 | #include "qemu/cacheinfo.h" |
5df022cf | 11 | #include "qemu/memalign.h" |
782da5b2 EC |
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 | ||
d73415a3 SH |
56 | GEN_READ(qatomic_read_i64, int64_t) |
57 | GEN_READ(qatomic_read_u64, uint64_t) | |
782da5b2 EC |
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 | ||
d73415a3 SH |
70 | GEN_SET(qatomic_set_i64, int64_t) |
71 | GEN_SET(qatomic_set_u64, uint64_t) | |
782da5b2 EC |
72 | #undef GEN_SET |
73 | ||
d73415a3 | 74 | void qatomic64_init(void) |
782da5b2 EC |
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 | } |