ARM: 7099/1: futex: preserve oldval in SMP __futex_atomic_op

The SMP implementation of __futex_atomic_op clobbers oldval with the
status flag from the exclusive store. This causes it to always read as
zero when performing the FUTEX_OP_CMP_* operation.

This patch updates the ARM __futex_atomic_op implementations to take a
tmp argument, allowing us to store the strex status flag without
overwriting the register containing oldval.

Cc: stable@kernel.org
Reported-by: Minho Ban <mhban@samsung.com>
Reviewed-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Will Deacon 2011-09-23 14:34:12 +01:00 committed by Russell King
parent d8e89b47e0
commit df77abcafc
1 changed files with 17 additions and 17 deletions

View File

@ -25,17 +25,17 @@
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
smp_mb(); \ smp_mb(); \
__asm__ __volatile__( \ __asm__ __volatile__( \
"1: ldrex %1, [%2]\n" \ "1: ldrex %1, [%3]\n" \
" " insn "\n" \ " " insn "\n" \
"2: strex %1, %0, [%2]\n" \ "2: strex %2, %0, [%3]\n" \
" teq %1, #0\n" \ " teq %2, #0\n" \
" bne 1b\n" \ " bne 1b\n" \
" mov %0, #0\n" \ " mov %0, #0\n" \
__futex_atomic_ex_table("%4") \ __futex_atomic_ex_table("%5") \
: "=&r" (ret), "=&r" (oldval) \ : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
: "cc", "memory") : "cc", "memory")
@ -73,14 +73,14 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
#include <linux/preempt.h> #include <linux/preempt.h>
#include <asm/domain.h> #include <asm/domain.h>
#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
__asm__ __volatile__( \ __asm__ __volatile__( \
"1: " T(ldr) " %1, [%2]\n" \ "1: " T(ldr) " %1, [%3]\n" \
" " insn "\n" \ " " insn "\n" \
"2: " T(str) " %0, [%2]\n" \ "2: " T(str) " %0, [%3]\n" \
" mov %0, #0\n" \ " mov %0, #0\n" \
__futex_atomic_ex_table("%4") \ __futex_atomic_ex_table("%5") \
: "=&r" (ret), "=&r" (oldval) \ : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
: "cc", "memory") : "cc", "memory")
@ -117,7 +117,7 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
int cmp = (encoded_op >> 24) & 15; int cmp = (encoded_op >> 24) & 15;
int oparg = (encoded_op << 8) >> 20; int oparg = (encoded_op << 8) >> 20;
int cmparg = (encoded_op << 20) >> 20; int cmparg = (encoded_op << 20) >> 20;
int oldval = 0, ret; int oldval = 0, ret, tmp;
if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
oparg = 1 << oparg; oparg = 1 << oparg;
@ -129,19 +129,19 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
switch (op) { switch (op) {
case FUTEX_OP_SET: case FUTEX_OP_SET:
__futex_atomic_op("mov %0, %3", ret, oldval, uaddr, oparg); __futex_atomic_op("mov %0, %4", ret, oldval, tmp, uaddr, oparg);
break; break;
case FUTEX_OP_ADD: case FUTEX_OP_ADD:
__futex_atomic_op("add %0, %1, %3", ret, oldval, uaddr, oparg); __futex_atomic_op("add %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
break; break;
case FUTEX_OP_OR: case FUTEX_OP_OR:
__futex_atomic_op("orr %0, %1, %3", ret, oldval, uaddr, oparg); __futex_atomic_op("orr %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
break; break;
case FUTEX_OP_ANDN: case FUTEX_OP_ANDN:
__futex_atomic_op("and %0, %1, %3", ret, oldval, uaddr, ~oparg); __futex_atomic_op("and %0, %1, %4", ret, oldval, tmp, uaddr, ~oparg);
break; break;
case FUTEX_OP_XOR: case FUTEX_OP_XOR:
__futex_atomic_op("eor %0, %1, %3", ret, oldval, uaddr, oparg); __futex_atomic_op("eor %0, %1, %4", ret, oldval, tmp, uaddr, oparg);
break; break;
default: default:
ret = -ENOSYS; ret = -ENOSYS;