aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoqun Feng <boqun.feng@gmail.com>2015-12-15 09:24:17 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2016-02-17 08:11:39 -0500
commit56c08e6d226c860ad097fa6ba109133228c56722 (patch)
treee7888be8d1d81987e5acc648b2c52d9bac5e41ff
parent26760fc19a7e663e4f49d586aca6740fb21d887d (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.h10
-rw-r--r--arch/powerpc/include/asm/cmpxchg.h149
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
184static __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 */
212static __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
185static __always_inline unsigned long 235static __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
278static __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
297static __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
355static __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
371static __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))