xref: /openbmc/linux/arch/arm64/include/asm/spinlock.h (revision 1491eaf9)
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 	u32 owner;
34 
35 	/*
36 	 * Ensure prior spin_lock operations to other locks have completed
37 	 * on this CPU before we test whether "lock" is locked.
38 	 */
39 	smp_mb();
40 	owner = READ_ONCE(lock->owner) << 16;
41 
42 	asm volatile(
43 "	sevl\n"
44 "1:	wfe\n"
45 "2:	ldaxr	%w0, %2\n"
46 	/* Is the lock free? */
47 "	eor	%w1, %w0, %w0, ror #16\n"
48 "	cbz	%w1, 3f\n"
49 	/* Lock taken -- has there been a subsequent unlock->lock transition? */
50 "	eor	%w1, %w3, %w0, lsl #16\n"
51 "	cbz	%w1, 1b\n"
52 	/*
53 	 * The owner has been updated, so there was an unlock->lock
54 	 * transition that we missed. That means we can rely on the
55 	 * store-release of the unlock operation paired with the
56 	 * load-acquire of the lock operation to publish any of our
57 	 * previous stores to the new lock owner and therefore don't
58 	 * need to bother with the writeback below.
59 	 */
60 "	b	4f\n"
61 "3:\n"
62 	/*
63 	 * Serialise against any concurrent lockers by writing back the
64 	 * unlocked lock value
65 	 */
66 	ARM64_LSE_ATOMIC_INSN(
67 	/* LL/SC */
68 "	stxr	%w1, %w0, %2\n"
69 "	nop\n"
70 "	nop\n",
71 	/* LSE atomics */
72 "	mov	%w1, %w0\n"
73 "	cas	%w0, %w0, %2\n"
74 "	eor	%w1, %w1, %w0\n")
75 	/* Somebody else wrote to the lock, GOTO 10 and reload the value */
76 "	cbnz	%w1, 2b\n"
77 "4:"
78 	: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
79 	: "r" (owner)
80 	: "memory");
81 }
82 
83 #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
84 
85 static inline void arch_spin_lock(arch_spinlock_t *lock)
86 {
87 	unsigned int tmp;
88 	arch_spinlock_t lockval, newval;
89 
90 	asm volatile(
91 	/* Atomically increment the next ticket. */
92 	ARM64_LSE_ATOMIC_INSN(
93 	/* LL/SC */
94 "	prfm	pstl1strm, %3\n"
95 "1:	ldaxr	%w0, %3\n"
96 "	add	%w1, %w0, %w5\n"
97 "	stxr	%w2, %w1, %3\n"
98 "	cbnz	%w2, 1b\n",
99 	/* LSE atomics */
100 "	mov	%w2, %w5\n"
101 "	ldadda	%w2, %w0, %3\n"
102 "	nop\n"
103 "	nop\n"
104 "	nop\n"
105 	)
106 
107 	/* Did we get the lock? */
108 "	eor	%w1, %w0, %w0, ror #16\n"
109 "	cbz	%w1, 3f\n"
110 	/*
111 	 * No: spin on the owner. Send a local event to avoid missing an
112 	 * unlock before the exclusive load.
113 	 */
114 "	sevl\n"
115 "2:	wfe\n"
116 "	ldaxrh	%w2, %4\n"
117 "	eor	%w1, %w2, %w0, lsr #16\n"
118 "	cbnz	%w1, 2b\n"
119 	/* We got the lock. Critical section starts here. */
120 "3:"
121 	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
122 	: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
123 	: "memory");
124 }
125 
126 static inline int arch_spin_trylock(arch_spinlock_t *lock)
127 {
128 	unsigned int tmp;
129 	arch_spinlock_t lockval;
130 
131 	asm volatile(ARM64_LSE_ATOMIC_INSN(
132 	/* LL/SC */
133 	"	prfm	pstl1strm, %2\n"
134 	"1:	ldaxr	%w0, %2\n"
135 	"	eor	%w1, %w0, %w0, ror #16\n"
136 	"	cbnz	%w1, 2f\n"
137 	"	add	%w0, %w0, %3\n"
138 	"	stxr	%w1, %w0, %2\n"
139 	"	cbnz	%w1, 1b\n"
140 	"2:",
141 	/* LSE atomics */
142 	"	ldr	%w0, %2\n"
143 	"	eor	%w1, %w0, %w0, ror #16\n"
144 	"	cbnz	%w1, 1f\n"
145 	"	add	%w1, %w0, %3\n"
146 	"	casa	%w0, %w1, %2\n"
147 	"	and	%w1, %w1, #0xffff\n"
148 	"	eor	%w1, %w1, %w0, lsr #16\n"
149 	"1:")
150 	: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
151 	: "I" (1 << TICKET_SHIFT)
152 	: "memory");
153 
154 	return !tmp;
155 }
156 
157 static inline void arch_spin_unlock(arch_spinlock_t *lock)
158 {
159 	unsigned long tmp;
160 
161 	asm volatile(ARM64_LSE_ATOMIC_INSN(
162 	/* LL/SC */
163 	"	ldrh	%w1, %0\n"
164 	"	add	%w1, %w1, #1\n"
165 	"	stlrh	%w1, %0",
166 	/* LSE atomics */
167 	"	mov	%w1, #1\n"
168 	"	nop\n"
169 	"	staddlh	%w1, %0")
170 	: "=Q" (lock->owner), "=&r" (tmp)
171 	:
172 	: "memory");
173 }
174 
175 static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
176 {
177 	return lock.owner == lock.next;
178 }
179 
180 static inline int arch_spin_is_locked(arch_spinlock_t *lock)
181 {
182 	smp_mb(); /* See arch_spin_unlock_wait */
183 	return !arch_spin_value_unlocked(READ_ONCE(*lock));
184 }
185 
186 static inline int arch_spin_is_contended(arch_spinlock_t *lock)
187 {
188 	arch_spinlock_t lockval = READ_ONCE(*lock);
189 	return (lockval.next - lockval.owner) > 1;
190 }
191 #define arch_spin_is_contended	arch_spin_is_contended
192 
193 /*
194  * Write lock implementation.
195  *
196  * Write locks set bit 31. Unlocking, is done by writing 0 since the lock is
197  * exclusively held.
198  *
199  * The memory barriers are implicit with the load-acquire and store-release
200  * instructions.
201  */
202 
203 static inline void arch_write_lock(arch_rwlock_t *rw)
204 {
205 	unsigned int tmp;
206 
207 	asm volatile(ARM64_LSE_ATOMIC_INSN(
208 	/* LL/SC */
209 	"	sevl\n"
210 	"1:	wfe\n"
211 	"2:	ldaxr	%w0, %1\n"
212 	"	cbnz	%w0, 1b\n"
213 	"	stxr	%w0, %w2, %1\n"
214 	"	cbnz	%w0, 2b\n"
215 	"	nop",
216 	/* LSE atomics */
217 	"1:	mov	%w0, wzr\n"
218 	"2:	casa	%w0, %w2, %1\n"
219 	"	cbz	%w0, 3f\n"
220 	"	ldxr	%w0, %1\n"
221 	"	cbz	%w0, 2b\n"
222 	"	wfe\n"
223 	"	b	1b\n"
224 	"3:")
225 	: "=&r" (tmp), "+Q" (rw->lock)
226 	: "r" (0x80000000)
227 	: "memory");
228 }
229 
230 static inline int arch_write_trylock(arch_rwlock_t *rw)
231 {
232 	unsigned int tmp;
233 
234 	asm volatile(ARM64_LSE_ATOMIC_INSN(
235 	/* LL/SC */
236 	"1:	ldaxr	%w0, %1\n"
237 	"	cbnz	%w0, 2f\n"
238 	"	stxr	%w0, %w2, %1\n"
239 	"	cbnz	%w0, 1b\n"
240 	"2:",
241 	/* LSE atomics */
242 	"	mov	%w0, wzr\n"
243 	"	casa	%w0, %w2, %1\n"
244 	"	nop\n"
245 	"	nop")
246 	: "=&r" (tmp), "+Q" (rw->lock)
247 	: "r" (0x80000000)
248 	: "memory");
249 
250 	return !tmp;
251 }
252 
253 static inline void arch_write_unlock(arch_rwlock_t *rw)
254 {
255 	asm volatile(ARM64_LSE_ATOMIC_INSN(
256 	"	stlr	wzr, %0",
257 	"	swpl	wzr, wzr, %0")
258 	: "=Q" (rw->lock) :: "memory");
259 }
260 
261 /* write_can_lock - would write_trylock() succeed? */
262 #define arch_write_can_lock(x)		((x)->lock == 0)
263 
264 /*
265  * Read lock implementation.
266  *
267  * It exclusively loads the lock value, increments it and stores the new value
268  * back if positive and the CPU still exclusively owns the location. If the
269  * value is negative, the lock is already held.
270  *
271  * During unlocking there may be multiple active read locks but no write lock.
272  *
273  * The memory barriers are implicit with the load-acquire and store-release
274  * instructions.
275  *
276  * Note that in UNDEFINED cases, such as unlocking a lock twice, the LL/SC
277  * and LSE implementations may exhibit different behaviour (although this
278  * will have no effect on lockdep).
279  */
280 static inline void arch_read_lock(arch_rwlock_t *rw)
281 {
282 	unsigned int tmp, tmp2;
283 
284 	asm volatile(
285 	"	sevl\n"
286 	ARM64_LSE_ATOMIC_INSN(
287 	/* LL/SC */
288 	"1:	wfe\n"
289 	"2:	ldaxr	%w0, %2\n"
290 	"	add	%w0, %w0, #1\n"
291 	"	tbnz	%w0, #31, 1b\n"
292 	"	stxr	%w1, %w0, %2\n"
293 	"	nop\n"
294 	"	cbnz	%w1, 2b",
295 	/* LSE atomics */
296 	"1:	wfe\n"
297 	"2:	ldxr	%w0, %2\n"
298 	"	adds	%w1, %w0, #1\n"
299 	"	tbnz	%w1, #31, 1b\n"
300 	"	casa	%w0, %w1, %2\n"
301 	"	sbc	%w0, %w1, %w0\n"
302 	"	cbnz	%w0, 2b")
303 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
304 	:
305 	: "cc", "memory");
306 }
307 
308 static inline void arch_read_unlock(arch_rwlock_t *rw)
309 {
310 	unsigned int tmp, tmp2;
311 
312 	asm volatile(ARM64_LSE_ATOMIC_INSN(
313 	/* LL/SC */
314 	"1:	ldxr	%w0, %2\n"
315 	"	sub	%w0, %w0, #1\n"
316 	"	stlxr	%w1, %w0, %2\n"
317 	"	cbnz	%w1, 1b",
318 	/* LSE atomics */
319 	"	movn	%w0, #0\n"
320 	"	nop\n"
321 	"	nop\n"
322 	"	staddl	%w0, %2")
323 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
324 	:
325 	: "memory");
326 }
327 
328 static inline int arch_read_trylock(arch_rwlock_t *rw)
329 {
330 	unsigned int tmp, tmp2;
331 
332 	asm volatile(ARM64_LSE_ATOMIC_INSN(
333 	/* LL/SC */
334 	"	mov	%w1, #1\n"
335 	"1:	ldaxr	%w0, %2\n"
336 	"	add	%w0, %w0, #1\n"
337 	"	tbnz	%w0, #31, 2f\n"
338 	"	stxr	%w1, %w0, %2\n"
339 	"	cbnz	%w1, 1b\n"
340 	"2:",
341 	/* LSE atomics */
342 	"	ldr	%w0, %2\n"
343 	"	adds	%w1, %w0, #1\n"
344 	"	tbnz	%w1, #31, 1f\n"
345 	"	casa	%w0, %w1, %2\n"
346 	"	sbc	%w1, %w1, %w0\n"
347 	"	nop\n"
348 	"1:")
349 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
350 	:
351 	: "cc", "memory");
352 
353 	return !tmp2;
354 }
355 
356 /* read_can_lock - would read_trylock() succeed? */
357 #define arch_read_can_lock(x)		((x)->lock < 0x80000000)
358 
359 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
360 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
361 
362 #define arch_spin_relax(lock)	cpu_relax()
363 #define arch_read_relax(lock)	cpu_relax()
364 #define arch_write_relax(lock)	cpu_relax()
365 
366 /*
367  * Accesses appearing in program order before a spin_lock() operation
368  * can be reordered with accesses inside the critical section, by virtue
369  * of arch_spin_lock being constructed using acquire semantics.
370  *
371  * In cases where this is problematic (e.g. try_to_wake_up), an
372  * smp_mb__before_spinlock() can restore the required ordering.
373  */
374 #define smp_mb__before_spinlock()	smp_mb()
375 
376 #endif /* __ASM_SPINLOCK_H */
377