diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2012-03-05 06:49:29 -0500 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2012-09-17 08:42:02 -0400 |
commit | 6170a97460dbda12686bef0dec65ce2d59867f9e (patch) | |
tree | 8e4fde78c58d30e2fc3af4be6b0fff0371c5f79f /arch/arm64/include/asm | |
parent | fb9bd7d6df81ddf1e7ab6648ac89ddbe0625b26b (diff) |
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>
Diffstat (limited to 'arch/arm64/include/asm')
-rw-r--r-- | arch/arm64/include/asm/atomic.h | 305 | ||||
-rw-r--r-- | arch/arm64/include/asm/futex.h | 136 |
2 files changed, 441 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/atomic.h b/arch/arm64/include/asm/atomic.h new file mode 100644 index 000000000000..407717ba060e --- /dev/null +++ b/arch/arm64/include/asm/atomic.h | |||
@@ -0,0 +1,305 @@ | |||
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 | ||
diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h new file mode 100644 index 000000000000..3468ae8439fa --- /dev/null +++ b/arch/arm64/include/asm/futex.h | |||
@@ -0,0 +1,136 @@ | |||
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 */ | ||