diff options
author | Joshua Kinard <kumba@gentoo.org> | 2017-11-18 23:00:51 -0500 |
---|---|---|
committer | Paul Burton <paul.burton@mips.com> | 2018-07-12 14:25:25 -0400 |
commit | 4936084c2ee227524c242d790a9fbad7650320c7 (patch) | |
tree | 153449026d2682524d9e43d03aa05e3caf2ff3ac | |
parent | a0a5ac3ce8fe6bf26694f49f9ba42ed859487424 (diff) |
MIPS: Cleanup R10000_LLSC_WAR logic in atomic.h
This patch reduces down the conditionals in MIPS atomic code that deal
with a silicon bug in early R10000 cpus that required a workaround of
a branch-likely instruction following a store-conditional in order to
to guarantee the whole ll/sc sequence is atomic. As the only real
difference is a branch-likely instruction (beqzl) over a standard
branch (beqz), the conditional is reduced down to a single preprocessor
check at the top to pick the required instruction.
This requires writing the uses in assembler, thus we discard the
non-R10000 case that uses a mixture of a C do...while loop with
embedded assembler that was added back in commit 7837314d141c ("MIPS:
Get rid of branches to .subsections."). A note found in the git log
for commit 5999eca25c1f ("[MIPS] Improve branch prediction in ll/sc
atomic operations.") is also addressed.
The macro definition for the branch instruction and the code comment
derives from a patch sent in earlier by Paul Burton for various cmpxchg
cleanups.
[paul.burton@mips.com:
- Minor whitespace fix for checkpatch.]
Signed-off-by: Joshua Kinard <kumba@gentoo.org>
Signed-off-by: Paul Burton <paul.burton@mips.com>
Patchwork: https://patchwork.linux-mips.org/patch/17736/
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: James Hogan <james.hogan@mips.com>
Cc: "Maciej W. Rozycki" <macro@mips.com>
Cc: linux-mips@linux-mips.org
-rw-r--r-- | arch/mips/include/asm/atomic.h | 179 |
1 files changed, 32 insertions, 147 deletions
diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h index 0babf2775d8e..3ccea238be2d 100644 --- a/arch/mips/include/asm/atomic.h +++ b/arch/mips/include/asm/atomic.h | |||
@@ -22,6 +22,17 @@ | |||
22 | #include <asm/cmpxchg.h> | 22 | #include <asm/cmpxchg.h> |
23 | #include <asm/war.h> | 23 | #include <asm/war.h> |
24 | 24 | ||
25 | /* | ||
26 | * Using a branch-likely instruction to check the result of an sc instruction | ||
27 | * works around a bug present in R10000 CPUs prior to revision 3.0 that could | ||
28 | * cause ll-sc sequences to execute non-atomically. | ||
29 | */ | ||
30 | #if R10000_LLSC_WAR | ||
31 | # define __scbeqz "beqzl" | ||
32 | #else | ||
33 | # define __scbeqz "beqz" | ||
34 | #endif | ||
35 | |||
25 | #define ATOMIC_INIT(i) { (i) } | 36 | #define ATOMIC_INIT(i) { (i) } |
26 | 37 | ||
27 | /* | 38 | /* |
@@ -44,31 +55,18 @@ | |||
44 | #define ATOMIC_OP(op, c_op, asm_op) \ | 55 | #define ATOMIC_OP(op, c_op, asm_op) \ |
45 | static __inline__ void atomic_##op(int i, atomic_t * v) \ | 56 | static __inline__ void atomic_##op(int i, atomic_t * v) \ |
46 | { \ | 57 | { \ |
47 | if (kernel_uses_llsc && R10000_LLSC_WAR) { \ | 58 | if (kernel_uses_llsc) { \ |
48 | int temp; \ | 59 | int temp; \ |
49 | \ | 60 | \ |
50 | __asm__ __volatile__( \ | 61 | __asm__ __volatile__( \ |
51 | " .set arch=r4000 \n" \ | 62 | " .set "MIPS_ISA_LEVEL" \n" \ |
52 | "1: ll %0, %1 # atomic_" #op " \n" \ | 63 | "1: ll %0, %1 # atomic_" #op " \n" \ |
53 | " " #asm_op " %0, %2 \n" \ | 64 | " " #asm_op " %0, %2 \n" \ |
54 | " sc %0, %1 \n" \ | 65 | " sc %0, %1 \n" \ |
55 | " beqzl %0, 1b \n" \ | 66 | "\t" __scbeqz " %0, 1b \n" \ |
56 | " .set mips0 \n" \ | 67 | " .set mips0 \n" \ |
57 | : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ | 68 | : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ |
58 | : "Ir" (i)); \ | 69 | : "Ir" (i)); \ |
59 | } else if (kernel_uses_llsc) { \ | ||
60 | int temp; \ | ||
61 | \ | ||
62 | do { \ | ||
63 | __asm__ __volatile__( \ | ||
64 | " .set "MIPS_ISA_LEVEL" \n" \ | ||
65 | " ll %0, %1 # atomic_" #op "\n" \ | ||
66 | " " #asm_op " %0, %2 \n" \ | ||
67 | " sc %0, %1 \n" \ | ||
68 | " .set mips0 \n" \ | ||
69 | : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ | ||
70 | : "Ir" (i)); \ | ||
71 | } while (unlikely(!temp)); \ | ||
72 | } else { \ | 70 | } else { \ |
73 | unsigned long flags; \ | 71 | unsigned long flags; \ |
74 | \ | 72 | \ |
@@ -83,36 +81,20 @@ static __inline__ int atomic_##op##_return_relaxed(int i, atomic_t * v) \ | |||
83 | { \ | 81 | { \ |
84 | int result; \ | 82 | int result; \ |
85 | \ | 83 | \ |
86 | if (kernel_uses_llsc && R10000_LLSC_WAR) { \ | 84 | if (kernel_uses_llsc) { \ |
87 | int temp; \ | 85 | int temp; \ |
88 | \ | 86 | \ |
89 | __asm__ __volatile__( \ | 87 | __asm__ __volatile__( \ |
90 | " .set arch=r4000 \n" \ | 88 | " .set "MIPS_ISA_LEVEL" \n" \ |
91 | "1: ll %1, %2 # atomic_" #op "_return \n" \ | 89 | "1: ll %1, %2 # atomic_" #op "_return \n" \ |
92 | " " #asm_op " %0, %1, %3 \n" \ | 90 | " " #asm_op " %0, %1, %3 \n" \ |
93 | " sc %0, %2 \n" \ | 91 | " sc %0, %2 \n" \ |
94 | " beqzl %0, 1b \n" \ | 92 | "\t" __scbeqz " %0, 1b \n" \ |
95 | " " #asm_op " %0, %1, %3 \n" \ | 93 | " " #asm_op " %0, %1, %3 \n" \ |
96 | " .set mips0 \n" \ | 94 | " .set mips0 \n" \ |
97 | : "=&r" (result), "=&r" (temp), \ | 95 | : "=&r" (result), "=&r" (temp), \ |
98 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ | 96 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ |
99 | : "Ir" (i)); \ | 97 | : "Ir" (i)); \ |
100 | } else if (kernel_uses_llsc) { \ | ||
101 | int temp; \ | ||
102 | \ | ||
103 | do { \ | ||
104 | __asm__ __volatile__( \ | ||
105 | " .set "MIPS_ISA_LEVEL" \n" \ | ||
106 | " ll %1, %2 # atomic_" #op "_return \n" \ | ||
107 | " " #asm_op " %0, %1, %3 \n" \ | ||
108 | " sc %0, %2 \n" \ | ||
109 | " .set mips0 \n" \ | ||
110 | : "=&r" (result), "=&r" (temp), \ | ||
111 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ | ||
112 | : "Ir" (i)); \ | ||
113 | } while (unlikely(!result)); \ | ||
114 | \ | ||
115 | result = temp; result c_op i; \ | ||
116 | } else { \ | 98 | } else { \ |
117 | unsigned long flags; \ | 99 | unsigned long flags; \ |
118 | \ | 100 | \ |
@@ -131,36 +113,20 @@ static __inline__ int atomic_fetch_##op##_relaxed(int i, atomic_t * v) \ | |||
131 | { \ | 113 | { \ |
132 | int result; \ | 114 | int result; \ |
133 | \ | 115 | \ |
134 | if (kernel_uses_llsc && R10000_LLSC_WAR) { \ | 116 | if (kernel_uses_llsc) { \ |
135 | int temp; \ | 117 | int temp; \ |
136 | \ | 118 | \ |
137 | __asm__ __volatile__( \ | 119 | __asm__ __volatile__( \ |
138 | " .set arch=r4000 \n" \ | 120 | " .set "MIPS_ISA_LEVEL" \n" \ |
139 | "1: ll %1, %2 # atomic_fetch_" #op " \n" \ | 121 | "1: ll %1, %2 # atomic_fetch_" #op " \n" \ |
140 | " " #asm_op " %0, %1, %3 \n" \ | 122 | " " #asm_op " %0, %1, %3 \n" \ |
141 | " sc %0, %2 \n" \ | 123 | " sc %0, %2 \n" \ |
142 | " beqzl %0, 1b \n" \ | 124 | "\t" __scbeqz " %0, 1b \n" \ |
143 | " move %0, %1 \n" \ | 125 | " move %0, %1 \n" \ |
144 | " .set mips0 \n" \ | 126 | " .set mips0 \n" \ |
145 | : "=&r" (result), "=&r" (temp), \ | 127 | : "=&r" (result), "=&r" (temp), \ |
146 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ | 128 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ |
147 | : "Ir" (i)); \ | 129 | : "Ir" (i)); \ |
148 | } else if (kernel_uses_llsc) { \ | ||
149 | int temp; \ | ||
150 | \ | ||
151 | do { \ | ||
152 | __asm__ __volatile__( \ | ||
153 | " .set "MIPS_ISA_LEVEL" \n" \ | ||
154 | " ll %1, %2 # atomic_fetch_" #op " \n" \ | ||
155 | " " #asm_op " %0, %1, %3 \n" \ | ||
156 | " sc %0, %2 \n" \ | ||
157 | " .set mips0 \n" \ | ||
158 | : "=&r" (result), "=&r" (temp), \ | ||
159 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ | ||
160 | : "Ir" (i)); \ | ||
161 | } while (unlikely(!result)); \ | ||
162 | \ | ||
163 | result = temp; \ | ||
164 | } else { \ | 130 | } else { \ |
165 | unsigned long flags; \ | 131 | unsigned long flags; \ |
166 | \ | 132 | \ |
@@ -218,24 +184,7 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v) | |||
218 | 184 | ||
219 | smp_mb__before_llsc(); | 185 | smp_mb__before_llsc(); |
220 | 186 | ||
221 | if (kernel_uses_llsc && R10000_LLSC_WAR) { | 187 | if (kernel_uses_llsc) { |
222 | int temp; | ||
223 | |||
224 | __asm__ __volatile__( | ||
225 | " .set arch=r4000 \n" | ||
226 | "1: ll %1, %2 # atomic_sub_if_positive\n" | ||
227 | " subu %0, %1, %3 \n" | ||
228 | " move %1, %0 \n" | ||
229 | " bltz %0, 1f \n" | ||
230 | " sc %1, %2 \n" | ||
231 | " beqzl %1, 1b \n" | ||
232 | "1: \n" | ||
233 | " .set mips0 \n" | ||
234 | : "=&r" (result), "=&r" (temp), | ||
235 | "+" GCC_OFF_SMALL_ASM() (v->counter) | ||
236 | : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) | ||
237 | : "memory"); | ||
238 | } else if (kernel_uses_llsc) { | ||
239 | int temp; | 188 | int temp; |
240 | 189 | ||
241 | __asm__ __volatile__( | 190 | __asm__ __volatile__( |
@@ -245,7 +194,7 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v) | |||
245 | " move %1, %0 \n" | 194 | " move %1, %0 \n" |
246 | " bltz %0, 1f \n" | 195 | " bltz %0, 1f \n" |
247 | " sc %1, %2 \n" | 196 | " sc %1, %2 \n" |
248 | " beqz %1, 1b \n" | 197 | "\t" __scbeqz " %1, 1b \n" |
249 | "1: \n" | 198 | "1: \n" |
250 | " .set mips0 \n" | 199 | " .set mips0 \n" |
251 | : "=&r" (result), "=&r" (temp), | 200 | : "=&r" (result), "=&r" (temp), |
@@ -382,31 +331,18 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u) | |||
382 | #define ATOMIC64_OP(op, c_op, asm_op) \ | 331 | #define ATOMIC64_OP(op, c_op, asm_op) \ |
383 | static __inline__ void atomic64_##op(long i, atomic64_t * v) \ | 332 | static __inline__ void atomic64_##op(long i, atomic64_t * v) \ |
384 | { \ | 333 | { \ |
385 | if (kernel_uses_llsc && R10000_LLSC_WAR) { \ | 334 | if (kernel_uses_llsc) { \ |
386 | long temp; \ | 335 | long temp; \ |
387 | \ | 336 | \ |
388 | __asm__ __volatile__( \ | 337 | __asm__ __volatile__( \ |
389 | " .set arch=r4000 \n" \ | 338 | " .set "MIPS_ISA_LEVEL" \n" \ |
390 | "1: lld %0, %1 # atomic64_" #op " \n" \ | 339 | "1: lld %0, %1 # atomic64_" #op " \n" \ |
391 | " " #asm_op " %0, %2 \n" \ | 340 | " " #asm_op " %0, %2 \n" \ |
392 | " scd %0, %1 \n" \ | 341 | " scd %0, %1 \n" \ |
393 | " beqzl %0, 1b \n" \ | 342 | "\t" __scbeqz " %0, 1b \n" \ |
394 | " .set mips0 \n" \ | 343 | " .set mips0 \n" \ |
395 | : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ | 344 | : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ |
396 | : "Ir" (i)); \ | 345 | : "Ir" (i)); \ |
397 | } else if (kernel_uses_llsc) { \ | ||
398 | long temp; \ | ||
399 | \ | ||
400 | do { \ | ||
401 | __asm__ __volatile__( \ | ||
402 | " .set "MIPS_ISA_LEVEL" \n" \ | ||
403 | " lld %0, %1 # atomic64_" #op "\n" \ | ||
404 | " " #asm_op " %0, %2 \n" \ | ||
405 | " scd %0, %1 \n" \ | ||
406 | " .set mips0 \n" \ | ||
407 | : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ | ||
408 | : "Ir" (i)); \ | ||
409 | } while (unlikely(!temp)); \ | ||
410 | } else { \ | 346 | } else { \ |
411 | unsigned long flags; \ | 347 | unsigned long flags; \ |
412 | \ | 348 | \ |
@@ -421,37 +357,20 @@ static __inline__ long atomic64_##op##_return_relaxed(long i, atomic64_t * v) \ | |||
421 | { \ | 357 | { \ |
422 | long result; \ | 358 | long result; \ |
423 | \ | 359 | \ |
424 | if (kernel_uses_llsc && R10000_LLSC_WAR) { \ | 360 | if (kernel_uses_llsc) { \ |
425 | long temp; \ | 361 | long temp; \ |
426 | \ | 362 | \ |
427 | __asm__ __volatile__( \ | 363 | __asm__ __volatile__( \ |
428 | " .set arch=r4000 \n" \ | 364 | " .set "MIPS_ISA_LEVEL" \n" \ |
429 | "1: lld %1, %2 # atomic64_" #op "_return\n" \ | 365 | "1: lld %1, %2 # atomic64_" #op "_return\n" \ |
430 | " " #asm_op " %0, %1, %3 \n" \ | 366 | " " #asm_op " %0, %1, %3 \n" \ |
431 | " scd %0, %2 \n" \ | 367 | " scd %0, %2 \n" \ |
432 | " beqzl %0, 1b \n" \ | 368 | "\t" __scbeqz " %0, 1b \n" \ |
433 | " " #asm_op " %0, %1, %3 \n" \ | 369 | " " #asm_op " %0, %1, %3 \n" \ |
434 | " .set mips0 \n" \ | 370 | " .set mips0 \n" \ |
435 | : "=&r" (result), "=&r" (temp), \ | 371 | : "=&r" (result), "=&r" (temp), \ |
436 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ | 372 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ |
437 | : "Ir" (i)); \ | 373 | : "Ir" (i)); \ |
438 | } else if (kernel_uses_llsc) { \ | ||
439 | long temp; \ | ||
440 | \ | ||
441 | do { \ | ||
442 | __asm__ __volatile__( \ | ||
443 | " .set "MIPS_ISA_LEVEL" \n" \ | ||
444 | " lld %1, %2 # atomic64_" #op "_return\n" \ | ||
445 | " " #asm_op " %0, %1, %3 \n" \ | ||
446 | " scd %0, %2 \n" \ | ||
447 | " .set mips0 \n" \ | ||
448 | : "=&r" (result), "=&r" (temp), \ | ||
449 | "=" GCC_OFF_SMALL_ASM() (v->counter) \ | ||
450 | : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) \ | ||
451 | : "memory"); \ | ||
452 | } while (unlikely(!result)); \ | ||
453 | \ | ||
454 | result = temp; result c_op i; \ | ||
455 | } else { \ | 374 | } else { \ |
456 | unsigned long flags; \ | 375 | unsigned long flags; \ |
457 | \ | 376 | \ |
@@ -474,33 +393,16 @@ static __inline__ long atomic64_fetch_##op##_relaxed(long i, atomic64_t * v) \ | |||
474 | long temp; \ | 393 | long temp; \ |
475 | \ | 394 | \ |
476 | __asm__ __volatile__( \ | 395 | __asm__ __volatile__( \ |
477 | " .set arch=r4000 \n" \ | 396 | " .set "MIPS_ISA_LEVEL" \n" \ |
478 | "1: lld %1, %2 # atomic64_fetch_" #op "\n" \ | 397 | "1: lld %1, %2 # atomic64_fetch_" #op "\n" \ |
479 | " " #asm_op " %0, %1, %3 \n" \ | 398 | " " #asm_op " %0, %1, %3 \n" \ |
480 | " scd %0, %2 \n" \ | 399 | " scd %0, %2 \n" \ |
481 | " beqzl %0, 1b \n" \ | 400 | "\t" __scbeqz " %0, 1b \n" \ |
482 | " move %0, %1 \n" \ | 401 | " move %0, %1 \n" \ |
483 | " .set mips0 \n" \ | 402 | " .set mips0 \n" \ |
484 | : "=&r" (result), "=&r" (temp), \ | 403 | : "=&r" (result), "=&r" (temp), \ |
485 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ | 404 | "+" GCC_OFF_SMALL_ASM() (v->counter) \ |
486 | : "Ir" (i)); \ | 405 | : "Ir" (i)); \ |
487 | } else if (kernel_uses_llsc) { \ | ||
488 | long temp; \ | ||
489 | \ | ||
490 | do { \ | ||
491 | __asm__ __volatile__( \ | ||
492 | " .set "MIPS_ISA_LEVEL" \n" \ | ||
493 | " lld %1, %2 # atomic64_fetch_" #op "\n" \ | ||
494 | " " #asm_op " %0, %1, %3 \n" \ | ||
495 | " scd %0, %2 \n" \ | ||
496 | " .set mips0 \n" \ | ||
497 | : "=&r" (result), "=&r" (temp), \ | ||
498 | "=" GCC_OFF_SMALL_ASM() (v->counter) \ | ||
499 | : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) \ | ||
500 | : "memory"); \ | ||
501 | } while (unlikely(!result)); \ | ||
502 | \ | ||
503 | result = temp; \ | ||
504 | } else { \ | 406 | } else { \ |
505 | unsigned long flags; \ | 407 | unsigned long flags; \ |
506 | \ | 408 | \ |
@@ -559,24 +461,7 @@ static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v) | |||
559 | 461 | ||
560 | smp_mb__before_llsc(); | 462 | smp_mb__before_llsc(); |
561 | 463 | ||
562 | if (kernel_uses_llsc && R10000_LLSC_WAR) { | 464 | if (kernel_uses_llsc) { |
563 | long temp; | ||
564 | |||
565 | __asm__ __volatile__( | ||
566 | " .set arch=r4000 \n" | ||
567 | "1: lld %1, %2 # atomic64_sub_if_positive\n" | ||
568 | " dsubu %0, %1, %3 \n" | ||
569 | " move %1, %0 \n" | ||
570 | " bltz %0, 1f \n" | ||
571 | " scd %1, %2 \n" | ||
572 | " beqzl %1, 1b \n" | ||
573 | "1: \n" | ||
574 | " .set mips0 \n" | ||
575 | : "=&r" (result), "=&r" (temp), | ||
576 | "=" GCC_OFF_SMALL_ASM() (v->counter) | ||
577 | : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) | ||
578 | : "memory"); | ||
579 | } else if (kernel_uses_llsc) { | ||
580 | long temp; | 465 | long temp; |
581 | 466 | ||
582 | __asm__ __volatile__( | 467 | __asm__ __volatile__( |
@@ -586,7 +471,7 @@ static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v) | |||
586 | " move %1, %0 \n" | 471 | " move %1, %0 \n" |
587 | " bltz %0, 1f \n" | 472 | " bltz %0, 1f \n" |
588 | " scd %1, %2 \n" | 473 | " scd %1, %2 \n" |
589 | " beqz %1, 1b \n" | 474 | "\t" __scbeqz " %1, 1b \n" |
590 | "1: \n" | 475 | "1: \n" |
591 | " .set mips0 \n" | 476 | " .set mips0 \n" |
592 | : "=&r" (result), "=&r" (temp), | 477 | : "=&r" (result), "=&r" (temp), |