Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
1a3b1d89 BG |
2 | #ifndef _ASM_X86_ATOMIC64_32_H |
3 | #define _ASM_X86_ATOMIC64_32_H | |
4 | ||
5 | #include <linux/compiler.h> | |
6 | #include <linux/types.h> | |
1a3b1d89 BG |
7 | //#include <asm/cmpxchg.h> |
8 | ||
9 | /* An 64bit atomic type */ | |
10 | ||
11 | typedef struct { | |
79c53a83 | 12 | s64 __aligned(8) counter; |
1a3b1d89 BG |
13 | } atomic64_t; |
14 | ||
15 | #define ATOMIC64_INIT(val) { (val) } | |
16 | ||
e73c4e34 UB |
17 | /* |
18 | * Read an atomic64_t non-atomically. | |
19 | * | |
20 | * This is intended to be used in cases where a subsequent atomic operation | |
21 | * will handle the torn value, and can be used to prime the first iteration | |
22 | * of unconditional try_cmpxchg() loops, e.g.: | |
23 | * | |
24 | * s64 val = arch_atomic64_read_nonatomic(v); | |
25 | * do { } while (!arch_atomic64_try_cmpxchg(v, &val, val OP i); | |
26 | * | |
27 | * This is NOT safe to use where the value is not always checked by a | |
28 | * subsequent atomic operation, such as in conditional try_cmpxchg() loops | |
29 | * that can break before the atomic operation, e.g.: | |
30 | * | |
31 | * s64 val = arch_atomic64_read_nonatomic(v); | |
32 | * do { | |
33 | * if (condition(val)) | |
34 | * break; | |
35 | * } while (!arch_atomic64_try_cmpxchg(v, &val, val OP i); | |
36 | */ | |
37 | static __always_inline s64 arch_atomic64_read_nonatomic(const atomic64_t *v) | |
38 | { | |
39 | /* See comment in arch_atomic_read(). */ | |
40 | return __READ_ONCE(v->counter); | |
41 | } | |
42 | ||
819165fb JB |
43 | #define __ATOMIC64_DECL(sym) void atomic64_##sym(atomic64_t *, ...) |
44 | #ifndef ATOMIC64_EXPORT | |
45 | #define ATOMIC64_DECL_ONE __ATOMIC64_DECL | |
46 | #else | |
47 | #define ATOMIC64_DECL_ONE(sym) __ATOMIC64_DECL(sym); \ | |
48 | ATOMIC64_EXPORT(atomic64_##sym) | |
49 | #endif | |
50 | ||
909639aa | 51 | #ifdef CONFIG_X86_CX8 |
224788b6 JP |
52 | #define __alternative_atomic64(f, g, out, in, clobbers...) \ |
53 | asm volatile("call %c[func]" \ | |
8b64db97 | 54 | : ALT_OUTPUT_SP(out) \ |
224788b6 JP |
55 | : [func] "i" (atomic64_##g##_cx8) \ |
56 | COMMA(in) \ | |
57 | : clobbers) | |
819165fb JB |
58 | |
59 | #define ATOMIC64_DECL(sym) ATOMIC64_DECL_ONE(sym##_cx8) | |
a7e926ab | 60 | #else |
224788b6 JP |
61 | #define __alternative_atomic64(f, g, out, in, clobbers...) \ |
62 | alternative_call(atomic64_##f##_386, atomic64_##g##_cx8, \ | |
63 | X86_FEATURE_CX8, ASM_OUTPUT(out), \ | |
64 | ASM_INPUT(in), clobbers) | |
819165fb JB |
65 | |
66 | #define ATOMIC64_DECL(sym) ATOMIC64_DECL_ONE(sym##_cx8); \ | |
67 | ATOMIC64_DECL_ONE(sym##_386) | |
68 | ||
69 | ATOMIC64_DECL_ONE(add_386); | |
70 | ATOMIC64_DECL_ONE(sub_386); | |
71 | ATOMIC64_DECL_ONE(inc_386); | |
72 | ATOMIC64_DECL_ONE(dec_386); | |
a7e926ab LB |
73 | #endif |
74 | ||
224788b6 JP |
75 | #define alternative_atomic64(f, out, in, clobbers...) \ |
76 | __alternative_atomic64(f, f, ASM_OUTPUT(out), ASM_INPUT(in), clobbers) | |
819165fb JB |
77 | |
78 | ATOMIC64_DECL(read); | |
79 | ATOMIC64_DECL(set); | |
80 | ATOMIC64_DECL(xchg); | |
81 | ATOMIC64_DECL(add_return); | |
82 | ATOMIC64_DECL(sub_return); | |
83 | ATOMIC64_DECL(inc_return); | |
84 | ATOMIC64_DECL(dec_return); | |
85 | ATOMIC64_DECL(dec_if_positive); | |
86 | ATOMIC64_DECL(inc_not_zero); | |
87 | ATOMIC64_DECL(add_unless); | |
88 | ||
89 | #undef ATOMIC64_DECL | |
90 | #undef ATOMIC64_DECL_ONE | |
91 | #undef __ATOMIC64_DECL | |
92 | #undef ATOMIC64_EXPORT | |
a7e926ab | 93 | |
276b8930 | 94 | static __always_inline s64 arch_atomic64_cmpxchg(atomic64_t *v, s64 old, s64 new) |
a7e926ab | 95 | { |
276b8930 | 96 | return arch_cmpxchg64(&v->counter, old, new); |
a7e926ab | 97 | } |
37f8173d | 98 | #define arch_atomic64_cmpxchg arch_atomic64_cmpxchg |
1a3b1d89 | 99 | |
276b8930 UB |
100 | static __always_inline bool arch_atomic64_try_cmpxchg(atomic64_t *v, s64 *old, s64 new) |
101 | { | |
102 | return arch_try_cmpxchg64(&v->counter, old, new); | |
103 | } | |
104 | #define arch_atomic64_try_cmpxchg arch_atomic64_try_cmpxchg | |
105 | ||
7aab7aa4 | 106 | static __always_inline s64 arch_atomic64_xchg(atomic64_t *v, s64 n) |
a7e926ab | 107 | { |
79c53a83 | 108 | s64 o; |
a7e926ab LB |
109 | unsigned high = (unsigned)(n >> 32); |
110 | unsigned low = (unsigned)n; | |
224788b6 JP |
111 | alternative_atomic64(xchg, |
112 | "=&A" (o), | |
113 | ASM_INPUT("S" (v), "b" (low), "c" (high)), | |
114 | "memory"); | |
a7e926ab LB |
115 | return o; |
116 | } | |
37f8173d | 117 | #define arch_atomic64_xchg arch_atomic64_xchg |
1a3b1d89 | 118 | |
7aab7aa4 | 119 | static __always_inline void arch_atomic64_set(atomic64_t *v, s64 i) |
a7e926ab LB |
120 | { |
121 | unsigned high = (unsigned)(i >> 32); | |
122 | unsigned low = (unsigned)i; | |
224788b6 JP |
123 | alternative_atomic64(set, |
124 | /* no output */, | |
125 | ASM_INPUT("S" (v), "b" (low), "c" (high)), | |
126 | "eax", "edx", "memory"); | |
a7e926ab | 127 | } |
1a3b1d89 | 128 | |
7aab7aa4 | 129 | static __always_inline s64 arch_atomic64_read(const atomic64_t *v) |
1a3b1d89 | 130 | { |
79c53a83 | 131 | s64 r; |
224788b6 | 132 | alternative_atomic64(read, "=&A" (r), "c" (v), "memory"); |
a7e926ab | 133 | return r; |
447a5647 | 134 | } |
1a3b1d89 | 135 | |
7aab7aa4 | 136 | static __always_inline s64 arch_atomic64_add_return(s64 i, atomic64_t *v) |
a7e926ab | 137 | { |
819165fb | 138 | alternative_atomic64(add_return, |
224788b6 JP |
139 | ASM_OUTPUT("+A" (i), "+c" (v)), |
140 | /* no input */, | |
141 | "memory"); | |
a7e926ab LB |
142 | return i; |
143 | } | |
37f8173d | 144 | #define arch_atomic64_add_return arch_atomic64_add_return |
1a3b1d89 | 145 | |
7aab7aa4 | 146 | static __always_inline s64 arch_atomic64_sub_return(s64 i, atomic64_t *v) |
a7e926ab | 147 | { |
819165fb | 148 | alternative_atomic64(sub_return, |
224788b6 JP |
149 | ASM_OUTPUT("+A" (i), "+c" (v)), |
150 | /* no input */, | |
151 | "memory"); | |
a7e926ab LB |
152 | return i; |
153 | } | |
37f8173d | 154 | #define arch_atomic64_sub_return arch_atomic64_sub_return |
a7e926ab | 155 | |
7aab7aa4 | 156 | static __always_inline s64 arch_atomic64_inc_return(atomic64_t *v) |
a7e926ab | 157 | { |
79c53a83 | 158 | s64 a; |
224788b6 JP |
159 | alternative_atomic64(inc_return, |
160 | "=&A" (a), | |
161 | "S" (v), | |
162 | "memory", "ecx"); | |
a7e926ab LB |
163 | return a; |
164 | } | |
9837559d | 165 | #define arch_atomic64_inc_return arch_atomic64_inc_return |
a7e926ab | 166 | |
7aab7aa4 | 167 | static __always_inline s64 arch_atomic64_dec_return(atomic64_t *v) |
a7e926ab | 168 | { |
79c53a83 | 169 | s64 a; |
224788b6 JP |
170 | alternative_atomic64(dec_return, |
171 | "=&A" (a), | |
172 | "S" (v), | |
173 | "memory", "ecx"); | |
a7e926ab LB |
174 | return a; |
175 | } | |
9837559d | 176 | #define arch_atomic64_dec_return arch_atomic64_dec_return |
1a3b1d89 | 177 | |
dce2a224 | 178 | static __always_inline void arch_atomic64_add(s64 i, atomic64_t *v) |
a7e926ab | 179 | { |
819165fb | 180 | __alternative_atomic64(add, add_return, |
224788b6 JP |
181 | ASM_OUTPUT("+A" (i), "+c" (v)), |
182 | /* no input */, | |
183 | "memory"); | |
a7e926ab | 184 | } |
1a3b1d89 | 185 | |
dce2a224 | 186 | static __always_inline void arch_atomic64_sub(s64 i, atomic64_t *v) |
a7e926ab | 187 | { |
819165fb | 188 | __alternative_atomic64(sub, sub_return, |
224788b6 JP |
189 | ASM_OUTPUT("+A" (i), "+c" (v)), |
190 | /* no input */, | |
191 | "memory"); | |
a7e926ab | 192 | } |
1a3b1d89 | 193 | |
7aab7aa4 | 194 | static __always_inline void arch_atomic64_inc(atomic64_t *v) |
a7e926ab | 195 | { |
224788b6 JP |
196 | __alternative_atomic64(inc, inc_return, |
197 | /* no output */, | |
198 | "S" (v), | |
199 | "memory", "eax", "ecx", "edx"); | |
a7e926ab | 200 | } |
4331f4d5 | 201 | #define arch_atomic64_inc arch_atomic64_inc |
1a3b1d89 | 202 | |
7aab7aa4 | 203 | static __always_inline void arch_atomic64_dec(atomic64_t *v) |
a7e926ab | 204 | { |
224788b6 JP |
205 | __alternative_atomic64(dec, dec_return, |
206 | /* no output */, | |
207 | "S" (v), | |
208 | "memory", "eax", "ecx", "edx"); | |
a7e926ab | 209 | } |
4331f4d5 | 210 | #define arch_atomic64_dec arch_atomic64_dec |
1a3b1d89 | 211 | |
7aab7aa4 | 212 | static __always_inline int arch_atomic64_add_unless(atomic64_t *v, s64 a, s64 u) |
a7e926ab LB |
213 | { |
214 | unsigned low = (unsigned)u; | |
215 | unsigned high = (unsigned)(u >> 32); | |
819165fb | 216 | alternative_atomic64(add_unless, |
224788b6 JP |
217 | ASM_OUTPUT("+A" (a), "+c" (low), "+D" (high)), |
218 | "S" (v), | |
219 | "memory"); | |
a7e926ab LB |
220 | return (int)a; |
221 | } | |
37f8173d | 222 | #define arch_atomic64_add_unless arch_atomic64_add_unless |
a7e926ab | 223 | |
7aab7aa4 | 224 | static __always_inline int arch_atomic64_inc_not_zero(atomic64_t *v) |
a7e926ab LB |
225 | { |
226 | int r; | |
224788b6 JP |
227 | alternative_atomic64(inc_not_zero, |
228 | "=&a" (r), | |
229 | "S" (v), | |
230 | "ecx", "edx", "memory"); | |
a7e926ab LB |
231 | return r; |
232 | } | |
4331f4d5 | 233 | #define arch_atomic64_inc_not_zero arch_atomic64_inc_not_zero |
a7e926ab | 234 | |
7aab7aa4 | 235 | static __always_inline s64 arch_atomic64_dec_if_positive(atomic64_t *v) |
a7e926ab | 236 | { |
79c53a83 | 237 | s64 r; |
224788b6 JP |
238 | alternative_atomic64(dec_if_positive, |
239 | "=&A" (r), | |
240 | "S" (v), | |
241 | "ecx", "memory"); | |
a7e926ab LB |
242 | return r; |
243 | } | |
4331f4d5 | 244 | #define arch_atomic64_dec_if_positive arch_atomic64_dec_if_positive |
a7e926ab | 245 | |
819165fb JB |
246 | #undef alternative_atomic64 |
247 | #undef __alternative_atomic64 | |
1a3b1d89 | 248 | |
7aab7aa4 | 249 | static __always_inline void arch_atomic64_and(s64 i, atomic64_t *v) |
ba1c9f83 | 250 | { |
95ece481 | 251 | s64 val = arch_atomic64_read_nonatomic(v); |
ba1c9f83 | 252 | |
95ece481 | 253 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val & i)); |
7fc1845d PZ |
254 | } |
255 | ||
7aab7aa4 | 256 | static __always_inline s64 arch_atomic64_fetch_and(s64 i, atomic64_t *v) |
ba1c9f83 | 257 | { |
95ece481 | 258 | s64 val = arch_atomic64_read_nonatomic(v); |
ba1c9f83 | 259 | |
95ece481 | 260 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val & i)); |
ba1c9f83 | 261 | |
95ece481 | 262 | return val; |
a8bcccab | 263 | } |
37f8173d | 264 | #define arch_atomic64_fetch_and arch_atomic64_fetch_and |
a8bcccab | 265 | |
7aab7aa4 | 266 | static __always_inline void arch_atomic64_or(s64 i, atomic64_t *v) |
ba1c9f83 | 267 | { |
95ece481 | 268 | s64 val = arch_atomic64_read_nonatomic(v); |
a8bcccab | 269 | |
95ece481 | 270 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val | i)); |
ba1c9f83 DV |
271 | } |
272 | ||
7aab7aa4 | 273 | static __always_inline s64 arch_atomic64_fetch_or(s64 i, atomic64_t *v) |
ba1c9f83 | 274 | { |
95ece481 | 275 | s64 val = arch_atomic64_read_nonatomic(v); |
ba1c9f83 | 276 | |
95ece481 | 277 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val | i)); |
ba1c9f83 | 278 | |
95ece481 | 279 | return val; |
ba1c9f83 | 280 | } |
37f8173d | 281 | #define arch_atomic64_fetch_or arch_atomic64_fetch_or |
a8bcccab | 282 | |
7aab7aa4 | 283 | static __always_inline void arch_atomic64_xor(s64 i, atomic64_t *v) |
ba1c9f83 | 284 | { |
95ece481 | 285 | s64 val = arch_atomic64_read_nonatomic(v); |
ba1c9f83 | 286 | |
95ece481 | 287 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val ^ i)); |
ba1c9f83 | 288 | } |
a8bcccab | 289 | |
7aab7aa4 | 290 | static __always_inline s64 arch_atomic64_fetch_xor(s64 i, atomic64_t *v) |
ba1c9f83 | 291 | { |
95ece481 | 292 | s64 val = arch_atomic64_read_nonatomic(v); |
ba1c9f83 | 293 | |
95ece481 | 294 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val ^ i)); |
ba1c9f83 | 295 | |
95ece481 | 296 | return val; |
ba1c9f83 | 297 | } |
37f8173d | 298 | #define arch_atomic64_fetch_xor arch_atomic64_fetch_xor |
7fc1845d | 299 | |
7aab7aa4 | 300 | static __always_inline s64 arch_atomic64_fetch_add(s64 i, atomic64_t *v) |
ba1c9f83 | 301 | { |
95ece481 | 302 | s64 val = arch_atomic64_read_nonatomic(v); |
ba1c9f83 | 303 | |
95ece481 | 304 | do { } while (!arch_atomic64_try_cmpxchg(v, &val, val + i)); |
ba1c9f83 | 305 | |
95ece481 | 306 | return val; |
ba1c9f83 | 307 | } |
37f8173d | 308 | #define arch_atomic64_fetch_add arch_atomic64_fetch_add |
ba1c9f83 | 309 | |
8bf705d1 | 310 | #define arch_atomic64_fetch_sub(i, v) arch_atomic64_fetch_add(-(i), (v)) |
7fc1845d | 311 | |
1a3b1d89 | 312 | #endif /* _ASM_X86_ATOMIC64_32_H */ |