aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2011-05-03 01:41:28 -0400
committerRuss Cox <rsc@golang.org>2011-05-03 01:41:28 -0400
commita7ae73d4e0d6f2131d339bae46d7b1a50050e766 (patch)
treec8d0286eac4aeba041dd21bc38cbe0dc1d8261ae
parent6a9e2c727917e10d2b4b6d71bb9d3f47043b6c32 (diff)
downloadgo-a7ae73d4e0d6f2131d339bae46d7b1a50050e766.tar.gz
go-a7ae73d4e0d6f2131d339bae46d7b1a50050e766.zip
[release-branch.r57] runtime, sync/atomic: fix arm cas
««« CL 4436072 / e280d98747be runtime, sync/atomic: fix arm cas Works around bug in kernel implementation on old ARM5 kernels. Bug was fixed on 26 Nov 2007 (between 2.6.23 and 2.6.24) but old kernels persist. Fixes #1750. R=dfc, golang-dev CC=golang-dev https://golang.org/cl/4436072 »»» TBR=adg CC=golang-dev https://golang.org/cl/4452064
-rw-r--r--src/pkg/runtime/linux/arm/sys.s15
-rw-r--r--src/pkg/sync/atomic/asm_linux_arm.s21
2 files changed, 32 insertions, 4 deletions
diff --git a/src/pkg/runtime/linux/arm/sys.s b/src/pkg/runtime/linux/arm/sys.s
index d866b0e220..2b5365bd86 100644
--- a/src/pkg/runtime/linux/arm/sys.s
+++ b/src/pkg/runtime/linux/arm/sys.s
@@ -258,11 +258,22 @@ TEXT cas<>(SB),7,$0
TEXT runtime·cas(SB),7,$0
MOVW valptr+0(FP), R2
MOVW old+4(FP), R0
+casagain:
MOVW new+8(FP), R1
BL cas<>(SB)
- MOVW $0, R0
- MOVW.CS $1, R0
+ BCC cascheck
+ MOVW $1, R0
RET
+cascheck:
+ // Kernel lies; double-check.
+ MOVW valptr+0(FP), R2
+ MOVW old+4(FP), R0
+ MOVW 0(R2), R3
+ CMP R0, R3
+ BEQ casagain
+ MOVW $0, R0
+ RET
+
TEXT runtime·casp(SB),7,$0
B runtime·cas(SB)
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index 5e7aea292e..72f8d746bb 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -13,6 +13,12 @@
// LR = return address
// The function returns with CS true if the swap happened.
// http://lxr.linux.no/linux+v2.6.37.2/arch/arm/kernel/entry-armv.S#L850
+// On older kernels (before 2.6.24) the function can incorrectly
+// report a conflict, so we have to double-check the compare ourselves
+// and retry if necessary.
+//
+// http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b49c0f24cf6744a3f4fd09289fe7cade349dead5
+//
TEXT cas<>(SB),7,$0
MOVW $0xffff0fc0, PC
@@ -23,12 +29,23 @@ TEXT ·CompareAndSwapInt32(SB),7,$0
TEXT ·CompareAndSwapUint32(SB),7,$0
MOVW valptr+0(FP), R2
MOVW old+4(FP), R0
+casagain:
MOVW new+8(FP), R1
BL cas<>(SB)
- MOVW $0, R0
- MOVW.CS $1, R0
+ BCC cascheck
+ MOVW $1, R0
+casret:
MOVW R0, ret+12(FP)
RET
+cascheck:
+ // Kernel lies; double-check.
+ MOVW valptr+0(FP), R2
+ MOVW old+4(FP), R0
+ MOVW 0(R2), R3
+ CMP R0, R3
+ BEQ casagain
+ MOVW $0, R0
+ B casret
TEXT ·CompareAndSwapUintptr(SB),7,$0
B ·CompareAndSwapUint32(SB)