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