diff options
author | Boqun Feng <boqun.feng@gmail.com> | 2015-12-15 09:24:17 -0500 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2016-02-17 08:11:39 -0500 |
commit | 56c08e6d226c860ad097fa6ba109133228c56722 (patch) | |
tree | e7888be8d1d81987e5acc648b2c52d9bac5e41ff | |
parent | 26760fc19a7e663e4f49d586aca6740fb21d887d (diff) |
powerpc: atomic: Implement acquire/release/relaxed variants for cmpxchg
Implement cmpxchg{,64}_relaxed and atomic{,64}_cmpxchg_relaxed, based on
which _release variants can be built.
To avoid superfluous barriers in _acquire variants, we implement these
operations with assembly code rather use __atomic_op_acquire() to build
them automatically.
For the same reason, we keep the assembly implementation of fully
ordered cmpxchg operations.
However, we don't do the similar for _release, because that will require
putting barriers in the middle of ll/sc loops, which is probably a bad
idea.
Note cmpxchg{,64}_relaxed and atomic{,64}_cmpxchg_relaxed are not
compiler barriers.
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r-- | arch/powerpc/include/asm/atomic.h | 10 | ||||
-rw-r--r-- | arch/powerpc/include/asm/cmpxchg.h | 149 |
2 files changed, 158 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h index a19fcdc318ee..ae0751ef8788 100644 --- a/arch/powerpc/include/asm/atomic.h +++ b/arch/powerpc/include/asm/atomic.h | |||
@@ -176,6 +176,11 @@ static __inline__ int atomic_dec_return_relaxed(atomic_t *v) | |||
176 | #define atomic_dec_return_relaxed atomic_dec_return_relaxed | 176 | #define atomic_dec_return_relaxed atomic_dec_return_relaxed |
177 | 177 | ||
178 | #define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) | 178 | #define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) |
179 | #define atomic_cmpxchg_relaxed(v, o, n) \ | ||
180 | cmpxchg_relaxed(&((v)->counter), (o), (n)) | ||
181 | #define atomic_cmpxchg_acquire(v, o, n) \ | ||
182 | cmpxchg_acquire(&((v)->counter), (o), (n)) | ||
183 | |||
179 | #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) | 184 | #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) |
180 | #define atomic_xchg_relaxed(v, new) xchg_relaxed(&((v)->counter), (new)) | 185 | #define atomic_xchg_relaxed(v, new) xchg_relaxed(&((v)->counter), (new)) |
181 | 186 | ||
@@ -444,6 +449,11 @@ static __inline__ long atomic64_dec_if_positive(atomic64_t *v) | |||
444 | } | 449 | } |
445 | 450 | ||
446 | #define atomic64_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) | 451 | #define atomic64_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) |
452 | #define atomic64_cmpxchg_relaxed(v, o, n) \ | ||
453 | cmpxchg_relaxed(&((v)->counter), (o), (n)) | ||
454 | #define atomic64_cmpxchg_acquire(v, o, n) \ | ||
455 | cmpxchg_acquire(&((v)->counter), (o), (n)) | ||
456 | |||
447 | #define atomic64_xchg(v, new) (xchg(&((v)->counter), new)) | 457 | #define atomic64_xchg(v, new) (xchg(&((v)->counter), new)) |
448 | #define atomic64_xchg_relaxed(v, new) xchg_relaxed(&((v)->counter), (new)) | 458 | #define atomic64_xchg_relaxed(v, new) xchg_relaxed(&((v)->counter), (new)) |
449 | 459 | ||
diff --git a/arch/powerpc/include/asm/cmpxchg.h b/arch/powerpc/include/asm/cmpxchg.h index 17c7e14b37ca..cae4fa85250c 100644 --- a/arch/powerpc/include/asm/cmpxchg.h +++ b/arch/powerpc/include/asm/cmpxchg.h | |||
@@ -181,6 +181,56 @@ __cmpxchg_u32_local(volatile unsigned int *p, unsigned long old, | |||
181 | return prev; | 181 | return prev; |
182 | } | 182 | } |
183 | 183 | ||
184 | static __always_inline unsigned long | ||
185 | __cmpxchg_u32_relaxed(u32 *p, unsigned long old, unsigned long new) | ||
186 | { | ||
187 | unsigned long prev; | ||
188 | |||
189 | __asm__ __volatile__ ( | ||
190 | "1: lwarx %0,0,%2 # __cmpxchg_u32_relaxed\n" | ||
191 | " cmpw 0,%0,%3\n" | ||
192 | " bne- 2f\n" | ||
193 | PPC405_ERR77(0, %2) | ||
194 | " stwcx. %4,0,%2\n" | ||
195 | " bne- 1b\n" | ||
196 | "2:" | ||
197 | : "=&r" (prev), "+m" (*p) | ||
198 | : "r" (p), "r" (old), "r" (new) | ||
199 | : "cc"); | ||
200 | |||
201 | return prev; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * cmpxchg family don't have order guarantee if cmp part fails, therefore we | ||
206 | * can avoid superfluous barriers if we use assembly code to implement | ||
207 | * cmpxchg() and cmpxchg_acquire(), however we don't do the similar for | ||
208 | * cmpxchg_release() because that will result in putting a barrier in the | ||
209 | * middle of a ll/sc loop, which is probably a bad idea. For example, this | ||
210 | * might cause the conditional store more likely to fail. | ||
211 | */ | ||
212 | static __always_inline unsigned long | ||
213 | __cmpxchg_u32_acquire(u32 *p, unsigned long old, unsigned long new) | ||
214 | { | ||
215 | unsigned long prev; | ||
216 | |||
217 | __asm__ __volatile__ ( | ||
218 | "1: lwarx %0,0,%2 # __cmpxchg_u32_acquire\n" | ||
219 | " cmpw 0,%0,%3\n" | ||
220 | " bne- 2f\n" | ||
221 | PPC405_ERR77(0, %2) | ||
222 | " stwcx. %4,0,%2\n" | ||
223 | " bne- 1b\n" | ||
224 | PPC_ACQUIRE_BARRIER | ||
225 | "\n" | ||
226 | "2:" | ||
227 | : "=&r" (prev), "+m" (*p) | ||
228 | : "r" (p), "r" (old), "r" (new) | ||
229 | : "cc", "memory"); | ||
230 | |||
231 | return prev; | ||
232 | } | ||
233 | |||
184 | #ifdef CONFIG_PPC64 | 234 | #ifdef CONFIG_PPC64 |
185 | static __always_inline unsigned long | 235 | static __always_inline unsigned long |
186 | __cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new) | 236 | __cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new) |
@@ -224,6 +274,46 @@ __cmpxchg_u64_local(volatile unsigned long *p, unsigned long old, | |||
224 | 274 | ||
225 | return prev; | 275 | return prev; |
226 | } | 276 | } |
277 | |||
278 | static __always_inline unsigned long | ||
279 | __cmpxchg_u64_relaxed(u64 *p, unsigned long old, unsigned long new) | ||
280 | { | ||
281 | unsigned long prev; | ||
282 | |||
283 | __asm__ __volatile__ ( | ||
284 | "1: ldarx %0,0,%2 # __cmpxchg_u64_relaxed\n" | ||
285 | " cmpd 0,%0,%3\n" | ||
286 | " bne- 2f\n" | ||
287 | " stdcx. %4,0,%2\n" | ||
288 | " bne- 1b\n" | ||
289 | "2:" | ||
290 | : "=&r" (prev), "+m" (*p) | ||
291 | : "r" (p), "r" (old), "r" (new) | ||
292 | : "cc"); | ||
293 | |||
294 | return prev; | ||
295 | } | ||
296 | |||
297 | static __always_inline unsigned long | ||
298 | __cmpxchg_u64_acquire(u64 *p, unsigned long old, unsigned long new) | ||
299 | { | ||
300 | unsigned long prev; | ||
301 | |||
302 | __asm__ __volatile__ ( | ||
303 | "1: ldarx %0,0,%2 # __cmpxchg_u64_acquire\n" | ||
304 | " cmpd 0,%0,%3\n" | ||
305 | " bne- 2f\n" | ||
306 | " stdcx. %4,0,%2\n" | ||
307 | " bne- 1b\n" | ||
308 | PPC_ACQUIRE_BARRIER | ||
309 | "\n" | ||
310 | "2:" | ||
311 | : "=&r" (prev), "+m" (*p) | ||
312 | : "r" (p), "r" (old), "r" (new) | ||
313 | : "cc", "memory"); | ||
314 | |||
315 | return prev; | ||
316 | } | ||
227 | #endif | 317 | #endif |
228 | 318 | ||
229 | /* This function doesn't exist, so you'll get a linker error | 319 | /* This function doesn't exist, so you'll get a linker error |
@@ -262,6 +352,37 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, | |||
262 | return old; | 352 | return old; |
263 | } | 353 | } |
264 | 354 | ||
355 | static __always_inline unsigned long | ||
356 | __cmpxchg_relaxed(void *ptr, unsigned long old, unsigned long new, | ||
357 | unsigned int size) | ||
358 | { | ||
359 | switch (size) { | ||
360 | case 4: | ||
361 | return __cmpxchg_u32_relaxed(ptr, old, new); | ||
362 | #ifdef CONFIG_PPC64 | ||
363 | case 8: | ||
364 | return __cmpxchg_u64_relaxed(ptr, old, new); | ||
365 | #endif | ||
366 | } | ||
367 | __cmpxchg_called_with_bad_pointer(); | ||
368 | return old; | ||
369 | } | ||
370 | |||
371 | static __always_inline unsigned long | ||
372 | __cmpxchg_acquire(void *ptr, unsigned long old, unsigned long new, | ||
373 | unsigned int size) | ||
374 | { | ||
375 | switch (size) { | ||
376 | case 4: | ||
377 | return __cmpxchg_u32_acquire(ptr, old, new); | ||
378 | #ifdef CONFIG_PPC64 | ||
379 | case 8: | ||
380 | return __cmpxchg_u64_acquire(ptr, old, new); | ||
381 | #endif | ||
382 | } | ||
383 | __cmpxchg_called_with_bad_pointer(); | ||
384 | return old; | ||
385 | } | ||
265 | #define cmpxchg(ptr, o, n) \ | 386 | #define cmpxchg(ptr, o, n) \ |
266 | ({ \ | 387 | ({ \ |
267 | __typeof__(*(ptr)) _o_ = (o); \ | 388 | __typeof__(*(ptr)) _o_ = (o); \ |
@@ -279,6 +400,23 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, | |||
279 | (unsigned long)_n_, sizeof(*(ptr))); \ | 400 | (unsigned long)_n_, sizeof(*(ptr))); \ |
280 | }) | 401 | }) |
281 | 402 | ||
403 | #define cmpxchg_relaxed(ptr, o, n) \ | ||
404 | ({ \ | ||
405 | __typeof__(*(ptr)) _o_ = (o); \ | ||
406 | __typeof__(*(ptr)) _n_ = (n); \ | ||
407 | (__typeof__(*(ptr))) __cmpxchg_relaxed((ptr), \ | ||
408 | (unsigned long)_o_, (unsigned long)_n_, \ | ||
409 | sizeof(*(ptr))); \ | ||
410 | }) | ||
411 | |||
412 | #define cmpxchg_acquire(ptr, o, n) \ | ||
413 | ({ \ | ||
414 | __typeof__(*(ptr)) _o_ = (o); \ | ||
415 | __typeof__(*(ptr)) _n_ = (n); \ | ||
416 | (__typeof__(*(ptr))) __cmpxchg_acquire((ptr), \ | ||
417 | (unsigned long)_o_, (unsigned long)_n_, \ | ||
418 | sizeof(*(ptr))); \ | ||
419 | }) | ||
282 | #ifdef CONFIG_PPC64 | 420 | #ifdef CONFIG_PPC64 |
283 | #define cmpxchg64(ptr, o, n) \ | 421 | #define cmpxchg64(ptr, o, n) \ |
284 | ({ \ | 422 | ({ \ |
@@ -290,7 +428,16 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, | |||
290 | BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ | 428 | BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ |
291 | cmpxchg_local((ptr), (o), (n)); \ | 429 | cmpxchg_local((ptr), (o), (n)); \ |
292 | }) | 430 | }) |
293 | #define cmpxchg64_relaxed cmpxchg64_local | 431 | #define cmpxchg64_relaxed(ptr, o, n) \ |
432 | ({ \ | ||
433 | BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ | ||
434 | cmpxchg_relaxed((ptr), (o), (n)); \ | ||
435 | }) | ||
436 | #define cmpxchg64_acquire(ptr, o, n) \ | ||
437 | ({ \ | ||
438 | BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ | ||
439 | cmpxchg_acquire((ptr), (o), (n)); \ | ||
440 | }) | ||
294 | #else | 441 | #else |
295 | #include <asm-generic/cmpxchg-local.h> | 442 | #include <asm-generic/cmpxchg-local.h> |
296 | #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) | 443 | #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) |