Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Joshua Kinard and committed by
Paul Burton
4936084c a0a5ac3c

+32 -147
+32 -147
arch/mips/include/asm/atomic.h
··· 22 22 #include <asm/cmpxchg.h> 23 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 36 #define ATOMIC_INIT(i) { (i) } 26 37 27 38 /* ··· 55 44 #define ATOMIC_OP(op, c_op, asm_op) \ 56 45 static __inline__ void atomic_##op(int i, atomic_t * v) \ 57 46 { \ 58 - if (kernel_uses_llsc && R10000_LLSC_WAR) { \ 47 + if (kernel_uses_llsc) { \ 59 48 int temp; \ 60 49 \ 61 50 __asm__ __volatile__( \ 62 - " .set arch=r4000 \n" \ 51 + " .set "MIPS_ISA_LEVEL" \n" \ 63 52 "1: ll %0, %1 # atomic_" #op " \n" \ 64 53 " " #asm_op " %0, %2 \n" \ 65 54 " sc %0, %1 \n" \ 66 - " beqzl %0, 1b \n" \ 55 + "\t" __scbeqz " %0, 1b \n" \ 67 56 " .set mips0 \n" \ 68 57 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ 69 58 : "Ir" (i)); \ 70 - } else if (kernel_uses_llsc) { \ 71 - int temp; \ 72 - \ 73 - do { \ 74 - __asm__ __volatile__( \ 75 - " .set "MIPS_ISA_LEVEL" \n" \ 76 - " ll %0, %1 # atomic_" #op "\n" \ 77 - " " #asm_op " %0, %2 \n" \ 78 - " sc %0, %1 \n" \ 79 - " .set mips0 \n" \ 80 - : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ 81 - : "Ir" (i)); \ 82 - } while (unlikely(!temp)); \ 83 59 } else { \ 84 60 unsigned long flags; \ 85 61 \ ··· 81 83 { \ 82 84 int result; \ 83 85 \ 84 - if (kernel_uses_llsc && R10000_LLSC_WAR) { \ 86 + if (kernel_uses_llsc) { \ 85 87 int temp; \ 86 88 \ 87 89 __asm__ __volatile__( \ 88 - " .set arch=r4000 \n" \ 90 + " .set "MIPS_ISA_LEVEL" \n" \ 89 91 "1: ll %1, %2 # atomic_" #op "_return \n" \ 90 92 " " #asm_op " %0, %1, %3 \n" \ 91 93 " sc %0, %2 \n" \ 92 - " beqzl %0, 1b \n" \ 94 + "\t" __scbeqz " %0, 1b \n" \ 93 95 " " #asm_op " %0, %1, %3 \n" \ 94 96 " .set mips0 \n" \ 95 97 : "=&r" (result), "=&r" (temp), \ 96 98 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 97 99 : "Ir" (i)); \ 98 - } else if (kernel_uses_llsc) { \ 99 - int temp; \ 100 - \ 101 - do { \ 102 - __asm__ __volatile__( \ 103 - " .set "MIPS_ISA_LEVEL" \n" \ 104 - " ll %1, %2 # atomic_" #op "_return \n" \ 105 - " " #asm_op " %0, %1, %3 \n" \ 106 - " sc %0, %2 \n" \ 107 - " .set mips0 \n" \ 108 - : "=&r" (result), "=&r" (temp), \ 109 - "+" GCC_OFF_SMALL_ASM() (v->counter) \ 110 - : "Ir" (i)); \ 111 - } while (unlikely(!result)); \ 112 - \ 113 - result = temp; result c_op i; \ 114 100 } else { \ 115 101 unsigned long flags; \ 116 102 \ ··· 113 131 { \ 114 132 int result; \ 115 133 \ 116 - if (kernel_uses_llsc && R10000_LLSC_WAR) { \ 134 + if (kernel_uses_llsc) { \ 117 135 int temp; \ 118 136 \ 119 137 __asm__ __volatile__( \ 120 - " .set arch=r4000 \n" \ 138 + " .set "MIPS_ISA_LEVEL" \n" \ 121 139 "1: ll %1, %2 # atomic_fetch_" #op " \n" \ 122 140 " " #asm_op " %0, %1, %3 \n" \ 123 141 " sc %0, %2 \n" \ 124 - " beqzl %0, 1b \n" \ 142 + "\t" __scbeqz " %0, 1b \n" \ 125 143 " move %0, %1 \n" \ 126 144 " .set mips0 \n" \ 127 145 : "=&r" (result), "=&r" (temp), \ 128 146 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 129 147 : "Ir" (i)); \ 130 - } else if (kernel_uses_llsc) { \ 131 - int temp; \ 132 - \ 133 - do { \ 134 - __asm__ __volatile__( \ 135 - " .set "MIPS_ISA_LEVEL" \n" \ 136 - " ll %1, %2 # atomic_fetch_" #op " \n" \ 137 - " " #asm_op " %0, %1, %3 \n" \ 138 - " sc %0, %2 \n" \ 139 - " .set mips0 \n" \ 140 - : "=&r" (result), "=&r" (temp), \ 141 - "+" GCC_OFF_SMALL_ASM() (v->counter) \ 142 - : "Ir" (i)); \ 143 - } while (unlikely(!result)); \ 144 - \ 145 - result = temp; \ 146 148 } else { \ 147 149 unsigned long flags; \ 148 150 \ ··· 184 218 185 219 smp_mb__before_llsc(); 186 220 187 - if (kernel_uses_llsc && R10000_LLSC_WAR) { 188 - int temp; 189 - 190 - __asm__ __volatile__( 191 - " .set arch=r4000 \n" 192 - "1: ll %1, %2 # atomic_sub_if_positive\n" 193 - " subu %0, %1, %3 \n" 194 - " move %1, %0 \n" 195 - " bltz %0, 1f \n" 196 - " sc %1, %2 \n" 197 - " beqzl %1, 1b \n" 198 - "1: \n" 199 - " .set mips0 \n" 200 - : "=&r" (result), "=&r" (temp), 201 - "+" GCC_OFF_SMALL_ASM() (v->counter) 202 - : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) 203 - : "memory"); 204 - } else if (kernel_uses_llsc) { 221 + if (kernel_uses_llsc) { 205 222 int temp; 206 223 207 224 __asm__ __volatile__( ··· 194 245 " move %1, %0 \n" 195 246 " bltz %0, 1f \n" 196 247 " sc %1, %2 \n" 197 - " beqz %1, 1b \n" 248 + "\t" __scbeqz " %1, 1b \n" 198 249 "1: \n" 199 250 " .set mips0 \n" 200 251 : "=&r" (result), "=&r" (temp), ··· 331 382 #define ATOMIC64_OP(op, c_op, asm_op) \ 332 383 static __inline__ void atomic64_##op(long i, atomic64_t * v) \ 333 384 { \ 334 - if (kernel_uses_llsc && R10000_LLSC_WAR) { \ 385 + if (kernel_uses_llsc) { \ 335 386 long temp; \ 336 387 \ 337 388 __asm__ __volatile__( \ 338 - " .set arch=r4000 \n" \ 389 + " .set "MIPS_ISA_LEVEL" \n" \ 339 390 "1: lld %0, %1 # atomic64_" #op " \n" \ 340 391 " " #asm_op " %0, %2 \n" \ 341 392 " scd %0, %1 \n" \ 342 - " beqzl %0, 1b \n" \ 393 + "\t" __scbeqz " %0, 1b \n" \ 343 394 " .set mips0 \n" \ 344 395 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ 345 396 : "Ir" (i)); \ 346 - } else if (kernel_uses_llsc) { \ 347 - long temp; \ 348 - \ 349 - do { \ 350 - __asm__ __volatile__( \ 351 - " .set "MIPS_ISA_LEVEL" \n" \ 352 - " lld %0, %1 # atomic64_" #op "\n" \ 353 - " " #asm_op " %0, %2 \n" \ 354 - " scd %0, %1 \n" \ 355 - " .set mips0 \n" \ 356 - : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ 357 - : "Ir" (i)); \ 358 - } while (unlikely(!temp)); \ 359 397 } else { \ 360 398 unsigned long flags; \ 361 399 \ ··· 357 421 { \ 358 422 long result; \ 359 423 \ 360 - if (kernel_uses_llsc && R10000_LLSC_WAR) { \ 424 + if (kernel_uses_llsc) { \ 361 425 long temp; \ 362 426 \ 363 427 __asm__ __volatile__( \ 364 - " .set arch=r4000 \n" \ 428 + " .set "MIPS_ISA_LEVEL" \n" \ 365 429 "1: lld %1, %2 # atomic64_" #op "_return\n" \ 366 430 " " #asm_op " %0, %1, %3 \n" \ 367 431 " scd %0, %2 \n" \ 368 - " beqzl %0, 1b \n" \ 432 + "\t" __scbeqz " %0, 1b \n" \ 369 433 " " #asm_op " %0, %1, %3 \n" \ 370 434 " .set mips0 \n" \ 371 435 : "=&r" (result), "=&r" (temp), \ 372 436 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 373 437 : "Ir" (i)); \ 374 - } else if (kernel_uses_llsc) { \ 375 - long temp; \ 376 - \ 377 - do { \ 378 - __asm__ __volatile__( \ 379 - " .set "MIPS_ISA_LEVEL" \n" \ 380 - " lld %1, %2 # atomic64_" #op "_return\n" \ 381 - " " #asm_op " %0, %1, %3 \n" \ 382 - " scd %0, %2 \n" \ 383 - " .set mips0 \n" \ 384 - : "=&r" (result), "=&r" (temp), \ 385 - "=" GCC_OFF_SMALL_ASM() (v->counter) \ 386 - : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) \ 387 - : "memory"); \ 388 - } while (unlikely(!result)); \ 389 - \ 390 - result = temp; result c_op i; \ 391 438 } else { \ 392 439 unsigned long flags; \ 393 440 \ ··· 393 474 long temp; \ 394 475 \ 395 476 __asm__ __volatile__( \ 396 - " .set arch=r4000 \n" \ 477 + " .set "MIPS_ISA_LEVEL" \n" \ 397 478 "1: lld %1, %2 # atomic64_fetch_" #op "\n" \ 398 479 " " #asm_op " %0, %1, %3 \n" \ 399 480 " scd %0, %2 \n" \ 400 - " beqzl %0, 1b \n" \ 481 + "\t" __scbeqz " %0, 1b \n" \ 401 482 " move %0, %1 \n" \ 402 483 " .set mips0 \n" \ 403 484 : "=&r" (result), "=&r" (temp), \ 404 485 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 405 486 : "Ir" (i)); \ 406 - } else if (kernel_uses_llsc) { \ 407 - long temp; \ 408 - \ 409 - do { \ 410 - __asm__ __volatile__( \ 411 - " .set "MIPS_ISA_LEVEL" \n" \ 412 - " lld %1, %2 # atomic64_fetch_" #op "\n" \ 413 - " " #asm_op " %0, %1, %3 \n" \ 414 - " scd %0, %2 \n" \ 415 - " .set mips0 \n" \ 416 - : "=&r" (result), "=&r" (temp), \ 417 - "=" GCC_OFF_SMALL_ASM() (v->counter) \ 418 - : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) \ 419 - : "memory"); \ 420 - } while (unlikely(!result)); \ 421 - \ 422 - result = temp; \ 423 487 } else { \ 424 488 unsigned long flags; \ 425 489 \ ··· 461 559 462 560 smp_mb__before_llsc(); 463 561 464 - if (kernel_uses_llsc && R10000_LLSC_WAR) { 465 - long temp; 466 - 467 - __asm__ __volatile__( 468 - " .set arch=r4000 \n" 469 - "1: lld %1, %2 # atomic64_sub_if_positive\n" 470 - " dsubu %0, %1, %3 \n" 471 - " move %1, %0 \n" 472 - " bltz %0, 1f \n" 473 - " scd %1, %2 \n" 474 - " beqzl %1, 1b \n" 475 - "1: \n" 476 - " .set mips0 \n" 477 - : "=&r" (result), "=&r" (temp), 478 - "=" GCC_OFF_SMALL_ASM() (v->counter) 479 - : "Ir" (i), GCC_OFF_SMALL_ASM() (v->counter) 480 - : "memory"); 481 - } else if (kernel_uses_llsc) { 562 + if (kernel_uses_llsc) { 482 563 long temp; 483 564 484 565 __asm__ __volatile__( ··· 471 586 " move %1, %0 \n" 472 587 " bltz %0, 1f \n" 473 588 " scd %1, %2 \n" 474 - " beqz %1, 1b \n" 589 + "\t" __scbeqz " %1, 1b \n" 475 590 "1: \n" 476 591 " .set mips0 \n" 477 592 : "=&r" (result), "=&r" (temp),