xref: /linux/arch/arm64/include/asm/spinlock.h (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
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_SPINLOCK_H
17 #define __ASM_SPINLOCK_H
18 
19 #include <asm/lse.h>
20 #include <asm/spinlock_types.h>
21 #include <asm/processor.h>
22 
23 /*
24  * Spinlock implementation.
25  *
26  * The memory barriers are implicit with the load-acquire and store-release
27  * instructions.
28  */
29 static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
30 {
31 	unsigned int tmp;
32 	arch_spinlock_t lockval;
33 
34 	asm volatile(
35 "	sevl\n"
36 "1:	wfe\n"
37 "2:	ldaxr	%w0, %2\n"
38 "	eor	%w1, %w0, %w0, ror #16\n"
39 "	cbnz	%w1, 1b\n"
40 	ARM64_LSE_ATOMIC_INSN(
41 	/* LL/SC */
42 "	stxr	%w1, %w0, %2\n"
43 "	cbnz	%w1, 2b\n", /* Serialise against any concurrent lockers */
44 	/* LSE atomics */
45 "	nop\n"
46 "	nop\n")
47 	: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
48 	:
49 	: "memory");
50 }
51 
52 #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
53 
54 static inline void arch_spin_lock(arch_spinlock_t *lock)
55 {
56 	unsigned int tmp;
57 	arch_spinlock_t lockval, newval;
58 
59 	asm volatile(
60 	/* Atomically increment the next ticket. */
61 	ARM64_LSE_ATOMIC_INSN(
62 	/* LL/SC */
63 "	prfm	pstl1strm, %3\n"
64 "1:	ldaxr	%w0, %3\n"
65 "	add	%w1, %w0, %w5\n"
66 "	stxr	%w2, %w1, %3\n"
67 "	cbnz	%w2, 1b\n",
68 	/* LSE atomics */
69 "	mov	%w2, %w5\n"
70 "	ldadda	%w2, %w0, %3\n"
71 "	nop\n"
72 "	nop\n"
73 "	nop\n"
74 	)
75 
76 	/* Did we get the lock? */
77 "	eor	%w1, %w0, %w0, ror #16\n"
78 "	cbz	%w1, 3f\n"
79 	/*
80 	 * No: spin on the owner. Send a local event to avoid missing an
81 	 * unlock before the exclusive load.
82 	 */
83 "	sevl\n"
84 "2:	wfe\n"
85 "	ldaxrh	%w2, %4\n"
86 "	eor	%w1, %w2, %w0, lsr #16\n"
87 "	cbnz	%w1, 2b\n"
88 	/* We got the lock. Critical section starts here. */
89 "3:"
90 	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
91 	: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
92 	: "memory");
93 }
94 
95 static inline int arch_spin_trylock(arch_spinlock_t *lock)
96 {
97 	unsigned int tmp;
98 	arch_spinlock_t lockval;
99 
100 	asm volatile(ARM64_LSE_ATOMIC_INSN(
101 	/* LL/SC */
102 	"	prfm	pstl1strm, %2\n"
103 	"1:	ldaxr	%w0, %2\n"
104 	"	eor	%w1, %w0, %w0, ror #16\n"
105 	"	cbnz	%w1, 2f\n"
106 	"	add	%w0, %w0, %3\n"
107 	"	stxr	%w1, %w0, %2\n"
108 	"	cbnz	%w1, 1b\n"
109 	"2:",
110 	/* LSE atomics */
111 	"	ldr	%w0, %2\n"
112 	"	eor	%w1, %w0, %w0, ror #16\n"
113 	"	cbnz	%w1, 1f\n"
114 	"	add	%w1, %w0, %3\n"
115 	"	casa	%w0, %w1, %2\n"
116 	"	and	%w1, %w1, #0xffff\n"
117 	"	eor	%w1, %w1, %w0, lsr #16\n"
118 	"1:")
119 	: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
120 	: "I" (1 << TICKET_SHIFT)
121 	: "memory");
122 
123 	return !tmp;
124 }
125 
126 static inline void arch_spin_unlock(arch_spinlock_t *lock)
127 {
128 	unsigned long tmp;
129 
130 	asm volatile(ARM64_LSE_ATOMIC_INSN(
131 	/* LL/SC */
132 	"	ldrh	%w1, %0\n"
133 	"	add	%w1, %w1, #1\n"
134 	"	stlrh	%w1, %0",
135 	/* LSE atomics */
136 	"	mov	%w1, #1\n"
137 	"	nop\n"
138 	"	staddlh	%w1, %0")
139 	: "=Q" (lock->owner), "=&r" (tmp)
140 	:
141 	: "memory");
142 }
143 
144 static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
145 {
146 	return lock.owner == lock.next;
147 }
148 
149 static inline int arch_spin_is_locked(arch_spinlock_t *lock)
150 {
151 	return !arch_spin_value_unlocked(READ_ONCE(*lock));
152 }
153 
154 static inline int arch_spin_is_contended(arch_spinlock_t *lock)
155 {
156 	arch_spinlock_t lockval = READ_ONCE(*lock);
157 	return (lockval.next - lockval.owner) > 1;
158 }
159 #define arch_spin_is_contended	arch_spin_is_contended
160 
161 /*
162  * Write lock implementation.
163  *
164  * Write locks set bit 31. Unlocking, is done by writing 0 since the lock is
165  * exclusively held.
166  *
167  * The memory barriers are implicit with the load-acquire and store-release
168  * instructions.
169  */
170 
171 static inline void arch_write_lock(arch_rwlock_t *rw)
172 {
173 	unsigned int tmp;
174 
175 	asm volatile(ARM64_LSE_ATOMIC_INSN(
176 	/* LL/SC */
177 	"	sevl\n"
178 	"1:	wfe\n"
179 	"2:	ldaxr	%w0, %1\n"
180 	"	cbnz	%w0, 1b\n"
181 	"	stxr	%w0, %w2, %1\n"
182 	"	cbnz	%w0, 2b\n"
183 	"	nop",
184 	/* LSE atomics */
185 	"1:	mov	%w0, wzr\n"
186 	"2:	casa	%w0, %w2, %1\n"
187 	"	cbz	%w0, 3f\n"
188 	"	ldxr	%w0, %1\n"
189 	"	cbz	%w0, 2b\n"
190 	"	wfe\n"
191 	"	b	1b\n"
192 	"3:")
193 	: "=&r" (tmp), "+Q" (rw->lock)
194 	: "r" (0x80000000)
195 	: "memory");
196 }
197 
198 static inline int arch_write_trylock(arch_rwlock_t *rw)
199 {
200 	unsigned int tmp;
201 
202 	asm volatile(ARM64_LSE_ATOMIC_INSN(
203 	/* LL/SC */
204 	"1:	ldaxr	%w0, %1\n"
205 	"	cbnz	%w0, 2f\n"
206 	"	stxr	%w0, %w2, %1\n"
207 	"	cbnz	%w0, 1b\n"
208 	"2:",
209 	/* LSE atomics */
210 	"	mov	%w0, wzr\n"
211 	"	casa	%w0, %w2, %1\n"
212 	"	nop\n"
213 	"	nop")
214 	: "=&r" (tmp), "+Q" (rw->lock)
215 	: "r" (0x80000000)
216 	: "memory");
217 
218 	return !tmp;
219 }
220 
221 static inline void arch_write_unlock(arch_rwlock_t *rw)
222 {
223 	asm volatile(ARM64_LSE_ATOMIC_INSN(
224 	"	stlr	wzr, %0",
225 	"	swpl	wzr, wzr, %0")
226 	: "=Q" (rw->lock) :: "memory");
227 }
228 
229 /* write_can_lock - would write_trylock() succeed? */
230 #define arch_write_can_lock(x)		((x)->lock == 0)
231 
232 /*
233  * Read lock implementation.
234  *
235  * It exclusively loads the lock value, increments it and stores the new value
236  * back if positive and the CPU still exclusively owns the location. If the
237  * value is negative, the lock is already held.
238  *
239  * During unlocking there may be multiple active read locks but no write lock.
240  *
241  * The memory barriers are implicit with the load-acquire and store-release
242  * instructions.
243  *
244  * Note that in UNDEFINED cases, such as unlocking a lock twice, the LL/SC
245  * and LSE implementations may exhibit different behaviour (although this
246  * will have no effect on lockdep).
247  */
248 static inline void arch_read_lock(arch_rwlock_t *rw)
249 {
250 	unsigned int tmp, tmp2;
251 
252 	asm volatile(
253 	"	sevl\n"
254 	ARM64_LSE_ATOMIC_INSN(
255 	/* LL/SC */
256 	"1:	wfe\n"
257 	"2:	ldaxr	%w0, %2\n"
258 	"	add	%w0, %w0, #1\n"
259 	"	tbnz	%w0, #31, 1b\n"
260 	"	stxr	%w1, %w0, %2\n"
261 	"	nop\n"
262 	"	cbnz	%w1, 2b",
263 	/* LSE atomics */
264 	"1:	wfe\n"
265 	"2:	ldxr	%w0, %2\n"
266 	"	adds	%w1, %w0, #1\n"
267 	"	tbnz	%w1, #31, 1b\n"
268 	"	casa	%w0, %w1, %2\n"
269 	"	sbc	%w0, %w1, %w0\n"
270 	"	cbnz	%w0, 2b")
271 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
272 	:
273 	: "cc", "memory");
274 }
275 
276 static inline void arch_read_unlock(arch_rwlock_t *rw)
277 {
278 	unsigned int tmp, tmp2;
279 
280 	asm volatile(ARM64_LSE_ATOMIC_INSN(
281 	/* LL/SC */
282 	"1:	ldxr	%w0, %2\n"
283 	"	sub	%w0, %w0, #1\n"
284 	"	stlxr	%w1, %w0, %2\n"
285 	"	cbnz	%w1, 1b",
286 	/* LSE atomics */
287 	"	movn	%w0, #0\n"
288 	"	nop\n"
289 	"	nop\n"
290 	"	staddl	%w0, %2")
291 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
292 	:
293 	: "memory");
294 }
295 
296 static inline int arch_read_trylock(arch_rwlock_t *rw)
297 {
298 	unsigned int tmp, tmp2;
299 
300 	asm volatile(ARM64_LSE_ATOMIC_INSN(
301 	/* LL/SC */
302 	"	mov	%w1, #1\n"
303 	"1:	ldaxr	%w0, %2\n"
304 	"	add	%w0, %w0, #1\n"
305 	"	tbnz	%w0, #31, 2f\n"
306 	"	stxr	%w1, %w0, %2\n"
307 	"	cbnz	%w1, 1b\n"
308 	"2:",
309 	/* LSE atomics */
310 	"	ldr	%w0, %2\n"
311 	"	adds	%w1, %w0, #1\n"
312 	"	tbnz	%w1, #31, 1f\n"
313 	"	casa	%w0, %w1, %2\n"
314 	"	sbc	%w1, %w1, %w0\n"
315 	"	nop\n"
316 	"1:")
317 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
318 	:
319 	: "cc", "memory");
320 
321 	return !tmp2;
322 }
323 
324 /* read_can_lock - would read_trylock() succeed? */
325 #define arch_read_can_lock(x)		((x)->lock < 0x80000000)
326 
327 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
328 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
329 
330 #define arch_spin_relax(lock)	cpu_relax()
331 #define arch_read_relax(lock)	cpu_relax()
332 #define arch_write_relax(lock)	cpu_relax()
333 
334 #endif /* __ASM_SPINLOCK_H */
335