summaryrefslogtreecommitdiff
path: root/arch/arm/kernel/traps.c
diff options
context:
space:
mode:
authorNicolas Pitre <nico@cam.org>2007-11-20 19:20:29 +0300
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-11-26 22:43:58 +0300
commitb49c0f24cf6744a3f4fd09289fe7cade349dead5 (patch)
tree23d244eae940f2e36a9bc1a2e77b8f1cc53e071a /arch/arm/kernel/traps.c
parentaeb747afb3fb1f42d9c82615a103882f7f97f291 (diff)
downloadlinux-b49c0f24cf6744a3f4fd09289fe7cade349dead5.tar.xz
[ARM] 4659/1: remove possibilities for spurious false negative with __kuser_cmpxchg
The ARM __kuser_cmpxchg routine is meant to implement an atomic cmpxchg in user space. It however can produce spurious false negative if a processor exception occurs in the middle of the operation. Normally this is not a problem since cmpxchg is typically called in a loop until it succeeds to implement an atomic increment for example. Some use cases which don't involve a loop require that the operation be 100% reliable though. This patch changes the implementation so to reattempt the operation after an exception has occurred in the critical section rather than abort it. Here's a simple program to test the fix (don't use CONFIG_NO_HZ in your kernel as this depends on a sufficiently high interrupt rate): #include <stdio.h> typedef int (__kernel_cmpxchg_t)(int oldval, int newval, int *ptr); #define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) int main() { int i, x = 0; for (i = 0; i < 100000000; i++) { int v = x; if (__kernel_cmpxchg(v, v+1, &x)) printf("failed at %d: %d vs %d\n", i, v, x); } printf("done with %d vs %d\n", i, x); return 0; } Signed-off-by: Nicolas Pitre <nico@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/traps.c')
-rw-r--r--arch/arm/kernel/traps.c3
1 files changed, 1 insertions, 2 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 5a52dd7f97fe..c34db4e868fa 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -509,7 +509,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
* existence. Don't ever use this from user code.
*/
case 0xfff0:
- {
+ for (;;) {
extern void do_DataAbort(unsigned long addr, unsigned int fsr,
struct pt_regs *regs);
unsigned long val;
@@ -545,7 +545,6 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
up_read(&mm->mmap_sem);
/* simulate a write access fault */
do_DataAbort(addr, 15 + (1 << 11), regs);
- return -1;
}
#endif