]>
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" | |
10 | ||
11 | #ifdef CONFIG_ATOMIC64 | |
12 | #error This file must only be compiled if !CONFIG_ATOMIC64 | |
13 | #endif | |
14 | ||
15 | /* | |
16 | * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks. | |
17 | * We use an array of spinlocks, with padding computed at run-time based on | |
18 | * the host's dcache line size. | |
19 | * We point to the array with a void * to simplify the padding's computation. | |
20 | * Each spinlock is located every lock_size bytes. | |
21 | */ | |
22 | static void *lock_array; | |
23 | static size_t lock_size; | |
24 | ||
25 | /* | |
26 | * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a | |
27 | * small array of locks. | |
28 | */ | |
29 | #define NR_LOCKS 16 | |
30 | ||
31 | static QemuSpin *addr_to_lock(const void *addr) | |
32 | { | |
33 | uintptr_t a = (uintptr_t)addr; | |
34 | uintptr_t idx; | |
35 | ||
36 | idx = a >> qemu_dcache_linesize_log; | |
37 | idx ^= (idx >> 8) ^ (idx >> 16); | |
38 | idx &= NR_LOCKS - 1; | |
39 | return lock_array + idx * lock_size; | |
40 | } | |
41 | ||
42 | #define GEN_READ(name, type) \ | |
43 | type name(const type *ptr) \ | |
44 | { \ | |
45 | QemuSpin *lock = addr_to_lock(ptr); \ | |
46 | type ret; \ | |
47 | \ | |
48 | qemu_spin_lock(lock); \ | |
49 | ret = *ptr; \ | |
50 | qemu_spin_unlock(lock); \ | |
51 | return ret; \ | |
52 | } | |
53 | ||
54 | GEN_READ(atomic_read_i64, int64_t) | |
55 | GEN_READ(atomic_read_u64, uint64_t) | |
56 | #undef GEN_READ | |
57 | ||
58 | #define GEN_SET(name, type) \ | |
59 | void name(type *ptr, type val) \ | |
60 | { \ | |
61 | QemuSpin *lock = addr_to_lock(ptr); \ | |
62 | \ | |
63 | qemu_spin_lock(lock); \ | |
64 | *ptr = val; \ | |
65 | qemu_spin_unlock(lock); \ | |
66 | } | |
67 | ||
68 | GEN_SET(atomic_set_i64, int64_t) | |
69 | GEN_SET(atomic_set_u64, uint64_t) | |
70 | #undef GEN_SET | |
71 | ||
72 | void atomic64_init(void) | |
73 | { | |
74 | int i; | |
75 | ||
76 | lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize); | |
77 | lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS); | |
78 | for (i = 0; i < NR_LOCKS; i++) { | |
79 | QemuSpin *lock = lock_array + i * lock_size; | |
80 | ||
81 | qemu_spin_init(lock); | |
82 | } | |
83 | } |