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

arm64: Atomic operations

This patch introduces the atomic, mutex and futex operations. Many
atomic operations use the load-acquire and store-release operations
which imply barriers, avoiding the need for explicit DMB.

Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Nicolas Pitre <nico@linaro.org>
Acked-by: Olof Johansson <olof@lixom.net>
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>

+441
+305
arch/arm64/include/asm/atomic.h
··· 1 + /* 2 + * Based on arch/arm/include/asm/atomic.h 3 + * 4 + * Copyright (C) 1996 Russell King. 5 + * Copyright (C) 2002 Deep Blue Solutions Ltd. 6 + * Copyright (C) 2012 ARM Ltd. 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, 13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 + * GNU General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 + */ 20 + #ifndef __ASM_ATOMIC_H 21 + #define __ASM_ATOMIC_H 22 + 23 + #include <linux/compiler.h> 24 + #include <linux/types.h> 25 + 26 + #include <asm/barrier.h> 27 + #include <asm/cmpxchg.h> 28 + 29 + #define ATOMIC_INIT(i) { (i) } 30 + 31 + #ifdef __KERNEL__ 32 + 33 + /* 34 + * On ARM, ordinary assignment (str instruction) doesn't clear the local 35 + * strex/ldrex monitor on some implementations. The reason we can use it for 36 + * atomic_set() is the clrex or dummy strex done on every exception return. 37 + */ 38 + #define atomic_read(v) (*(volatile int *)&(v)->counter) 39 + #define atomic_set(v,i) (((v)->counter) = (i)) 40 + 41 + /* 42 + * AArch64 UP and SMP safe atomic ops. We use load exclusive and 43 + * store exclusive to ensure that these are atomic. We may loop 44 + * to ensure that the update happens. 45 + */ 46 + static inline void atomic_add(int i, atomic_t *v) 47 + { 48 + unsigned long tmp; 49 + int result; 50 + 51 + asm volatile("// atomic_add\n" 52 + "1: ldxr %w0, [%3]\n" 53 + " add %w0, %w0, %w4\n" 54 + " stxr %w1, %w0, [%3]\n" 55 + " cbnz %w1, 1b" 56 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 57 + : "r" (&v->counter), "Ir" (i) 58 + : "cc"); 59 + } 60 + 61 + static inline int atomic_add_return(int i, atomic_t *v) 62 + { 63 + unsigned long tmp; 64 + int result; 65 + 66 + asm volatile("// atomic_add_return\n" 67 + "1: ldaxr %w0, [%3]\n" 68 + " add %w0, %w0, %w4\n" 69 + " stlxr %w1, %w0, [%3]\n" 70 + " cbnz %w1, 1b" 71 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 72 + : "r" (&v->counter), "Ir" (i) 73 + : "cc"); 74 + 75 + return result; 76 + } 77 + 78 + static inline void atomic_sub(int i, atomic_t *v) 79 + { 80 + unsigned long tmp; 81 + int result; 82 + 83 + asm volatile("// atomic_sub\n" 84 + "1: ldxr %w0, [%3]\n" 85 + " sub %w0, %w0, %w4\n" 86 + " stxr %w1, %w0, [%3]\n" 87 + " cbnz %w1, 1b" 88 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 89 + : "r" (&v->counter), "Ir" (i) 90 + : "cc"); 91 + } 92 + 93 + static inline int atomic_sub_return(int i, atomic_t *v) 94 + { 95 + unsigned long tmp; 96 + int result; 97 + 98 + asm volatile("// atomic_sub_return\n" 99 + "1: ldaxr %w0, [%3]\n" 100 + " sub %w0, %w0, %w4\n" 101 + " stlxr %w1, %w0, [%3]\n" 102 + " cbnz %w1, 1b" 103 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 104 + : "r" (&v->counter), "Ir" (i) 105 + : "cc"); 106 + 107 + return result; 108 + } 109 + 110 + static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) 111 + { 112 + unsigned long tmp; 113 + int oldval; 114 + 115 + asm volatile("// atomic_cmpxchg\n" 116 + "1: ldaxr %w1, [%3]\n" 117 + " cmp %w1, %w4\n" 118 + " b.ne 2f\n" 119 + " stlxr %w0, %w5, [%3]\n" 120 + " cbnz %w0, 1b\n" 121 + "2:" 122 + : "=&r" (tmp), "=&r" (oldval), "+o" (ptr->counter) 123 + : "r" (&ptr->counter), "Ir" (old), "r" (new) 124 + : "cc"); 125 + 126 + return oldval; 127 + } 128 + 129 + static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) 130 + { 131 + unsigned long tmp, tmp2; 132 + 133 + asm volatile("// atomic_clear_mask\n" 134 + "1: ldxr %0, [%3]\n" 135 + " bic %0, %0, %4\n" 136 + " stxr %w1, %0, [%3]\n" 137 + " cbnz %w1, 1b" 138 + : "=&r" (tmp), "=&r" (tmp2), "+o" (*addr) 139 + : "r" (addr), "Ir" (mask) 140 + : "cc"); 141 + } 142 + 143 + #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) 144 + 145 + static inline int __atomic_add_unless(atomic_t *v, int a, int u) 146 + { 147 + int c, old; 148 + 149 + c = atomic_read(v); 150 + while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) 151 + c = old; 152 + return c; 153 + } 154 + 155 + #define atomic_inc(v) atomic_add(1, v) 156 + #define atomic_dec(v) atomic_sub(1, v) 157 + 158 + #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) 159 + #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) 160 + #define atomic_inc_return(v) (atomic_add_return(1, v)) 161 + #define atomic_dec_return(v) (atomic_sub_return(1, v)) 162 + #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) 163 + 164 + #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) 165 + 166 + #define smp_mb__before_atomic_dec() smp_mb() 167 + #define smp_mb__after_atomic_dec() smp_mb() 168 + #define smp_mb__before_atomic_inc() smp_mb() 169 + #define smp_mb__after_atomic_inc() smp_mb() 170 + 171 + /* 172 + * 64-bit atomic operations. 173 + */ 174 + #define ATOMIC64_INIT(i) { (i) } 175 + 176 + #define atomic64_read(v) (*(volatile long long *)&(v)->counter) 177 + #define atomic64_set(v,i) (((v)->counter) = (i)) 178 + 179 + static inline void atomic64_add(u64 i, atomic64_t *v) 180 + { 181 + long result; 182 + unsigned long tmp; 183 + 184 + asm volatile("// atomic64_add\n" 185 + "1: ldxr %0, [%3]\n" 186 + " add %0, %0, %4\n" 187 + " stxr %w1, %0, [%3]\n" 188 + " cbnz %w1, 1b" 189 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 190 + : "r" (&v->counter), "Ir" (i) 191 + : "cc"); 192 + } 193 + 194 + static inline long atomic64_add_return(long i, atomic64_t *v) 195 + { 196 + long result; 197 + unsigned long tmp; 198 + 199 + asm volatile("// atomic64_add_return\n" 200 + "1: ldaxr %0, [%3]\n" 201 + " add %0, %0, %4\n" 202 + " stlxr %w1, %0, [%3]\n" 203 + " cbnz %w1, 1b" 204 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 205 + : "r" (&v->counter), "Ir" (i) 206 + : "cc"); 207 + 208 + return result; 209 + } 210 + 211 + static inline void atomic64_sub(u64 i, atomic64_t *v) 212 + { 213 + long result; 214 + unsigned long tmp; 215 + 216 + asm volatile("// atomic64_sub\n" 217 + "1: ldxr %0, [%3]\n" 218 + " sub %0, %0, %4\n" 219 + " stxr %w1, %0, [%3]\n" 220 + " cbnz %w1, 1b" 221 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 222 + : "r" (&v->counter), "Ir" (i) 223 + : "cc"); 224 + } 225 + 226 + static inline long atomic64_sub_return(long i, atomic64_t *v) 227 + { 228 + long result; 229 + unsigned long tmp; 230 + 231 + asm volatile("// atomic64_sub_return\n" 232 + "1: ldaxr %0, [%3]\n" 233 + " sub %0, %0, %4\n" 234 + " stlxr %w1, %0, [%3]\n" 235 + " cbnz %w1, 1b" 236 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 237 + : "r" (&v->counter), "Ir" (i) 238 + : "cc"); 239 + 240 + return result; 241 + } 242 + 243 + static inline long atomic64_cmpxchg(atomic64_t *ptr, long old, long new) 244 + { 245 + long oldval; 246 + unsigned long res; 247 + 248 + asm volatile("// atomic64_cmpxchg\n" 249 + "1: ldaxr %1, [%3]\n" 250 + " cmp %1, %4\n" 251 + " b.ne 2f\n" 252 + " stlxr %w0, %5, [%3]\n" 253 + " cbnz %w0, 1b\n" 254 + "2:" 255 + : "=&r" (res), "=&r" (oldval), "+o" (ptr->counter) 256 + : "r" (&ptr->counter), "Ir" (old), "r" (new) 257 + : "cc"); 258 + 259 + return oldval; 260 + } 261 + 262 + #define atomic64_xchg(v, new) (xchg(&((v)->counter), new)) 263 + 264 + static inline long atomic64_dec_if_positive(atomic64_t *v) 265 + { 266 + long result; 267 + unsigned long tmp; 268 + 269 + asm volatile("// atomic64_dec_if_positive\n" 270 + "1: ldaxr %0, [%3]\n" 271 + " subs %0, %0, #1\n" 272 + " b.mi 2f\n" 273 + " stlxr %w1, %0, [%3]\n" 274 + " cbnz %w1, 1b\n" 275 + "2:" 276 + : "=&r" (result), "=&r" (tmp), "+o" (v->counter) 277 + : "r" (&v->counter) 278 + : "cc"); 279 + 280 + return result; 281 + } 282 + 283 + static inline int atomic64_add_unless(atomic64_t *v, long a, long u) 284 + { 285 + long c, old; 286 + 287 + c = atomic64_read(v); 288 + while (c != u && (old = atomic64_cmpxchg((v), c, c + a)) != c) 289 + c = old; 290 + 291 + return c != u; 292 + } 293 + 294 + #define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0) 295 + #define atomic64_inc(v) atomic64_add(1LL, (v)) 296 + #define atomic64_inc_return(v) atomic64_add_return(1LL, (v)) 297 + #define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0) 298 + #define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0) 299 + #define atomic64_dec(v) atomic64_sub(1LL, (v)) 300 + #define atomic64_dec_return(v) atomic64_sub_return(1LL, (v)) 301 + #define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0) 302 + #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL) 303 + 304 + #endif 305 + #endif
+136
arch/arm64/include/asm/futex.h
··· 1 + /* 2 + * Copyright (C) 2012 ARM Ltd. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + * 13 + * You should have received a copy of the GNU General Public License 14 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 + */ 16 + #ifndef __ASM_FUTEX_H 17 + #define __ASM_FUTEX_H 18 + 19 + #ifdef __KERNEL__ 20 + 21 + #include <linux/futex.h> 22 + #include <linux/uaccess.h> 23 + #include <asm/errno.h> 24 + 25 + #define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \ 26 + asm volatile( \ 27 + "1: ldaxr %w1, %2\n" \ 28 + insn "\n" \ 29 + "2: stlxr %w3, %w0, %2\n" \ 30 + " cbnz %w3, 1b\n" \ 31 + "3:\n" \ 32 + " .pushsection .fixup,\"ax\"\n" \ 33 + "4: mov %w0, %w5\n" \ 34 + " b 3b\n" \ 35 + " .popsection\n" \ 36 + " .pushsection __ex_table,\"a\"\n" \ 37 + " .align 3\n" \ 38 + " .quad 1b, 4b, 2b, 4b\n" \ 39 + " .popsection\n" \ 40 + : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \ 41 + : "r" (oparg), "Ir" (-EFAULT) \ 42 + : "cc") 43 + 44 + static inline int 45 + futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) 46 + { 47 + int op = (encoded_op >> 28) & 7; 48 + int cmp = (encoded_op >> 24) & 15; 49 + int oparg = (encoded_op << 8) >> 20; 50 + int cmparg = (encoded_op << 20) >> 20; 51 + int oldval = 0, ret, tmp; 52 + 53 + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) 54 + oparg = 1 << oparg; 55 + 56 + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) 57 + return -EFAULT; 58 + 59 + pagefault_disable(); /* implies preempt_disable() */ 60 + 61 + switch (op) { 62 + case FUTEX_OP_SET: 63 + __futex_atomic_op("mov %w0, %w4", 64 + ret, oldval, uaddr, tmp, oparg); 65 + break; 66 + case FUTEX_OP_ADD: 67 + __futex_atomic_op("add %w0, %w1, %w4", 68 + ret, oldval, uaddr, tmp, oparg); 69 + break; 70 + case FUTEX_OP_OR: 71 + __futex_atomic_op("orr %w0, %w1, %w4", 72 + ret, oldval, uaddr, tmp, oparg); 73 + break; 74 + case FUTEX_OP_ANDN: 75 + __futex_atomic_op("and %w0, %w1, %w4", 76 + ret, oldval, uaddr, tmp, ~oparg); 77 + break; 78 + case FUTEX_OP_XOR: 79 + __futex_atomic_op("eor %w0, %w1, %w4", 80 + ret, oldval, uaddr, tmp, oparg); 81 + break; 82 + default: 83 + ret = -ENOSYS; 84 + } 85 + 86 + pagefault_enable(); /* subsumes preempt_enable() */ 87 + 88 + if (!ret) { 89 + switch (cmp) { 90 + case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; 91 + case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; 92 + case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break; 93 + case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break; 94 + case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break; 95 + case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break; 96 + default: ret = -ENOSYS; 97 + } 98 + } 99 + return ret; 100 + } 101 + 102 + static inline int 103 + futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 104 + u32 oldval, u32 newval) 105 + { 106 + int ret = 0; 107 + u32 val, tmp; 108 + 109 + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) 110 + return -EFAULT; 111 + 112 + asm volatile("// futex_atomic_cmpxchg_inatomic\n" 113 + "1: ldaxr %w1, %2\n" 114 + " sub %w3, %w1, %w4\n" 115 + " cbnz %w3, 3f\n" 116 + "2: stlxr %w3, %w5, %2\n" 117 + " cbnz %w3, 1b\n" 118 + "3:\n" 119 + " .pushsection .fixup,\"ax\"\n" 120 + "4: mov %w0, %w6\n" 121 + " b 3b\n" 122 + " .popsection\n" 123 + " .pushsection __ex_table,\"a\"\n" 124 + " .align 3\n" 125 + " .quad 1b, 4b, 2b, 4b\n" 126 + " .popsection\n" 127 + : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp) 128 + : "r" (oldval), "r" (newval), "Ir" (-EFAULT) 129 + : "cc", "memory"); 130 + 131 + *uval = val; 132 + return ret; 133 + } 134 + 135 + #endif /* __KERNEL__ */ 136 + #endif /* __ASM_FUTEX_H */