diff options
| author | Will Deacon <will.deacon@arm.com> | 2011-09-23 09:34:12 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-09-26 07:36:47 -0400 |
| commit | df77abcafc8dc881b6c9347548651777088e4b27 (patch) | |
| tree | 1311e860f1c86371852175c582d111ce565d411d | |
| parent | d8e89b47e00ee80e920761145144640aac4cf71a (diff) | |
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>
| -rw-r--r-- | arch/arm/include/asm/futex.h | 34 |
1 files changed, 17 insertions, 17 deletions
diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 8c73900da9ed..253cc86318bf 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h | |||
| @@ -25,17 +25,17 @@ | |||
| 25 | 25 | ||
| 26 | #ifdef CONFIG_SMP | 26 | #ifdef CONFIG_SMP |
| 27 | 27 | ||
| 28 | #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ | 28 | #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ |
| 29 | smp_mb(); \ | 29 | smp_mb(); \ |
| 30 | __asm__ __volatile__( \ | 30 | __asm__ __volatile__( \ |
| 31 | "1: ldrex %1, [%2]\n" \ | 31 | "1: ldrex %1, [%3]\n" \ |
| 32 | " " insn "\n" \ | 32 | " " insn "\n" \ |
| 33 | "2: strex %1, %0, [%2]\n" \ | 33 | "2: strex %2, %0, [%3]\n" \ |
| 34 | " teq %1, #0\n" \ | 34 | " teq %2, #0\n" \ |
| 35 | " bne 1b\n" \ | 35 | " bne 1b\n" \ |
| 36 | " mov %0, #0\n" \ | 36 | " mov %0, #0\n" \ |
| 37 | __futex_atomic_ex_table("%4") \ | 37 | __futex_atomic_ex_table("%5") \ |
| 38 | : "=&r" (ret), "=&r" (oldval) \ | 38 | : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ |
| 39 | : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ | 39 | : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ |
| 40 | : "cc", "memory") | 40 | : "cc", "memory") |
| 41 | 41 | ||
| @@ -73,14 +73,14 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | |||
| 73 | #include <linux/preempt.h> | 73 | #include <linux/preempt.h> |
| 74 | #include <asm/domain.h> | 74 | #include <asm/domain.h> |
| 75 | 75 | ||
| 76 | #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ | 76 | #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ |
| 77 | __asm__ __volatile__( \ | 77 | __asm__ __volatile__( \ |
| 78 | "1: " T(ldr) " %1, [%2]\n" \ | 78 | "1: " T(ldr) " %1, [%3]\n" \ |
| 79 | " " insn "\n" \ | 79 | " " insn "\n" \ |
| 80 | "2: " T(str) " %0, [%2]\n" \ | 80 | "2: " T(str) " %0, [%3]\n" \ |
| 81 | " mov %0, #0\n" \ | 81 | " mov %0, #0\n" \ |
| 82 | __futex_atomic_ex_table("%4") \ | 82 | __futex_atomic_ex_table("%5") \ |
| 83 | : "=&r" (ret), "=&r" (oldval) \ | 83 | : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ |
| 84 | : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ | 84 | : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ |
| 85 | : "cc", "memory") | 85 | : "cc", "memory") |
| 86 | 86 | ||
| @@ -117,7 +117,7 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) | |||
| 117 | int cmp = (encoded_op >> 24) & 15; | 117 | int cmp = (encoded_op >> 24) & 15; |
| 118 | int oparg = (encoded_op << 8) >> 20; | 118 | int oparg = (encoded_op << 8) >> 20; |
| 119 | int cmparg = (encoded_op << 20) >> 20; | 119 | int cmparg = (encoded_op << 20) >> 20; |
| 120 | int oldval = 0, ret; | 120 | int oldval = 0, ret, tmp; |
| 121 | 121 | ||
| 122 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) | 122 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) |
| 123 | oparg = 1 << oparg; | 123 | oparg = 1 << oparg; |
| @@ -129,19 +129,19 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) | |||
| 129 | 129 | ||
| 130 | switch (op) { | 130 | switch (op) { |
| 131 | case FUTEX_OP_SET: | 131 | case FUTEX_OP_SET: |
| 132 | __futex_atomic_op("mov %0, %3", ret, oldval, uaddr, oparg); | 132 | __futex_atomic_op("mov %0, %4", ret, oldval, tmp, uaddr, oparg); |
| 133 | break; | 133 | break; |
| 134 | case FUTEX_OP_ADD: | 134 | case FUTEX_OP_ADD: |
| 135 | __futex_atomic_op("add %0, %1, %3", ret, oldval, uaddr, oparg); | 135 | __futex_atomic_op("add %0, %1, %4", ret, oldval, tmp, uaddr, oparg); |
| 136 | break; | 136 | break; |
| 137 | case FUTEX_OP_OR: | 137 | case FUTEX_OP_OR: |
| 138 | __futex_atomic_op("orr %0, %1, %3", ret, oldval, uaddr, oparg); | 138 | __futex_atomic_op("orr %0, %1, %4", ret, oldval, tmp, uaddr, oparg); |
| 139 | break; | 139 | break; |
| 140 | case FUTEX_OP_ANDN: | 140 | case FUTEX_OP_ANDN: |
| 141 | __futex_atomic_op("and %0, %1, %3", ret, oldval, uaddr, ~oparg); | 141 | __futex_atomic_op("and %0, %1, %4", ret, oldval, tmp, uaddr, ~oparg); |
| 142 | break; | 142 | break; |
| 143 | case FUTEX_OP_XOR: | 143 | case FUTEX_OP_XOR: |
| 144 | __futex_atomic_op("eor %0, %1, %3", ret, oldval, uaddr, oparg); | 144 | __futex_atomic_op("eor %0, %1, %4", ret, oldval, tmp, uaddr, oparg); |
| 145 | break; | 145 | break; |
| 146 | default: | 146 | default: |
| 147 | ret = -ENOSYS; | 147 | ret = -ENOSYS; |
