ARCv2: spinlock: remove the extra smp_mb before lock, after unlock
[linux-2.6-block.git] / arch / arc / include / asm / spinlock.h
1 /*
2  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
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
9 #ifndef __ASM_SPINLOCK_H
10 #define __ASM_SPINLOCK_H
11
12 #include <asm/spinlock_types.h>
13 #include <asm/processor.h>
14 #include <asm/barrier.h>
15
16 #define arch_spin_is_locked(x)  ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__)
17
18 #ifdef CONFIG_ARC_HAS_LLSC
19
20 static inline void arch_spin_lock(arch_spinlock_t *lock)
21 {
22         unsigned int val;
23
24         __asm__ __volatile__(
25         "1:     llock   %[val], [%[slock]]      \n"
26         "       breq    %[val], %[LOCKED], 1b   \n"     /* spin while LOCKED */
27         "       scond   %[LOCKED], [%[slock]]   \n"     /* acquire */
28         "       bnz     1b                      \n"
29         "                                       \n"
30         : [val]         "=&r"   (val)
31         : [slock]       "r"     (&(lock->slock)),
32           [LOCKED]      "r"     (__ARCH_SPIN_LOCK_LOCKED__)
33         : "memory", "cc");
34
35         /*
36          * ACQUIRE barrier to ensure load/store after taking the lock
37          * don't "bleed-up" out of the critical section (leak-in is allowed)
38          * http://www.spinics.net/lists/kernel/msg2010409.html
39          *
40          * ARCv2 only has load-load, store-store and all-all barrier
41          * thus need the full all-all barrier
42          */
43         smp_mb();
44 }
45
46 /* 1 - lock taken successfully */
47 static inline int arch_spin_trylock(arch_spinlock_t *lock)
48 {
49         unsigned int val, got_it = 0;
50
51         __asm__ __volatile__(
52         "1:     llock   %[val], [%[slock]]      \n"
53         "       breq    %[val], %[LOCKED], 4f   \n"     /* already LOCKED, just bail */
54         "       scond   %[LOCKED], [%[slock]]   \n"     /* acquire */
55         "       bnz     1b                      \n"
56         "       mov     %[got_it], 1            \n"
57         "4:                                     \n"
58         "                                       \n"
59         : [val]         "=&r"   (val),
60           [got_it]      "+&r"   (got_it)
61         : [slock]       "r"     (&(lock->slock)),
62           [LOCKED]      "r"     (__ARCH_SPIN_LOCK_LOCKED__)
63         : "memory", "cc");
64
65         smp_mb();
66
67         return got_it;
68 }
69
70 static inline void arch_spin_unlock(arch_spinlock_t *lock)
71 {
72         smp_mb();
73
74         WRITE_ONCE(lock->slock, __ARCH_SPIN_LOCK_UNLOCKED__);
75 }
76
77 /*
78  * Read-write spinlocks, allowing multiple readers but only one writer.
79  * Unfair locking as Writers could be starved indefinitely by Reader(s)
80  */
81
82 static inline void arch_read_lock(arch_rwlock_t *rw)
83 {
84         unsigned int val;
85
86         /*
87          * zero means writer holds the lock exclusively, deny Reader.
88          * Otherwise grant lock to first/subseq reader
89          *
90          *      if (rw->counter > 0) {
91          *              rw->counter--;
92          *              ret = 1;
93          *      }
94          */
95
96         __asm__ __volatile__(
97         "1:     llock   %[val], [%[rwlock]]     \n"
98         "       brls    %[val], %[WR_LOCKED], 1b\n"     /* <= 0: spin while write locked */
99         "       sub     %[val], %[val], 1       \n"     /* reader lock */
100         "       scond   %[val], [%[rwlock]]     \n"
101         "       bnz     1b                      \n"
102         "                                       \n"
103         : [val]         "=&r"   (val)
104         : [rwlock]      "r"     (&(rw->counter)),
105           [WR_LOCKED]   "ir"    (0)
106         : "memory", "cc");
107
108         smp_mb();
109 }
110
111 /* 1 - lock taken successfully */
112 static inline int arch_read_trylock(arch_rwlock_t *rw)
113 {
114         unsigned int val, got_it = 0;
115
116         __asm__ __volatile__(
117         "1:     llock   %[val], [%[rwlock]]     \n"
118         "       brls    %[val], %[WR_LOCKED], 4f\n"     /* <= 0: already write locked, bail */
119         "       sub     %[val], %[val], 1       \n"     /* counter-- */
120         "       scond   %[val], [%[rwlock]]     \n"
121         "       bnz     1b                      \n"     /* retry if collided with someone */
122         "       mov     %[got_it], 1            \n"
123         "                                       \n"
124         "4: ; --- done ---                      \n"
125
126         : [val]         "=&r"   (val),
127           [got_it]      "+&r"   (got_it)
128         : [rwlock]      "r"     (&(rw->counter)),
129           [WR_LOCKED]   "ir"    (0)
130         : "memory", "cc");
131
132         smp_mb();
133
134         return got_it;
135 }
136
137 static inline void arch_write_lock(arch_rwlock_t *rw)
138 {
139         unsigned int val;
140
141         /*
142          * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
143          * deny writer. Otherwise if unlocked grant to writer
144          * Hence the claim that Linux rwlocks are unfair to writers.
145          * (can be starved for an indefinite time by readers).
146          *
147          *      if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
148          *              rw->counter = 0;
149          *              ret = 1;
150          *      }
151          */
152
153         __asm__ __volatile__(
154         "1:     llock   %[val], [%[rwlock]]     \n"
155         "       brne    %[val], %[UNLOCKED], 1b \n"     /* while !UNLOCKED spin */
156         "       mov     %[val], %[WR_LOCKED]    \n"
157         "       scond   %[val], [%[rwlock]]     \n"
158         "       bnz     1b                      \n"
159         "                                       \n"
160         : [val]         "=&r"   (val)
161         : [rwlock]      "r"     (&(rw->counter)),
162           [UNLOCKED]    "ir"    (__ARCH_RW_LOCK_UNLOCKED__),
163           [WR_LOCKED]   "ir"    (0)
164         : "memory", "cc");
165
166         smp_mb();
167 }
168
169 /* 1 - lock taken successfully */
170 static inline int arch_write_trylock(arch_rwlock_t *rw)
171 {
172         unsigned int val, got_it = 0;
173
174         __asm__ __volatile__(
175         "1:     llock   %[val], [%[rwlock]]     \n"
176         "       brne    %[val], %[UNLOCKED], 4f \n"     /* !UNLOCKED, bail */
177         "       mov     %[val], %[WR_LOCKED]    \n"
178         "       scond   %[val], [%[rwlock]]     \n"
179         "       bnz     1b                      \n"     /* retry if collided with someone */
180         "       mov     %[got_it], 1            \n"
181         "                                       \n"
182         "4: ; --- done ---                      \n"
183
184         : [val]         "=&r"   (val),
185           [got_it]      "+&r"   (got_it)
186         : [rwlock]      "r"     (&(rw->counter)),
187           [UNLOCKED]    "ir"    (__ARCH_RW_LOCK_UNLOCKED__),
188           [WR_LOCKED]   "ir"    (0)
189         : "memory", "cc");
190
191         smp_mb();
192
193         return got_it;
194 }
195
196 static inline void arch_read_unlock(arch_rwlock_t *rw)
197 {
198         unsigned int val;
199
200         smp_mb();
201
202         /*
203          * rw->counter++;
204          */
205         __asm__ __volatile__(
206         "1:     llock   %[val], [%[rwlock]]     \n"
207         "       add     %[val], %[val], 1       \n"
208         "       scond   %[val], [%[rwlock]]     \n"
209         "       bnz     1b                      \n"
210         "                                       \n"
211         : [val]         "=&r"   (val)
212         : [rwlock]      "r"     (&(rw->counter))
213         : "memory", "cc");
214 }
215
216 static inline void arch_write_unlock(arch_rwlock_t *rw)
217 {
218         smp_mb();
219
220         WRITE_ONCE(rw->counter, __ARCH_RW_LOCK_UNLOCKED__);
221 }
222
223 #else   /* !CONFIG_ARC_HAS_LLSC */
224
225 static inline void arch_spin_lock(arch_spinlock_t *lock)
226 {
227         unsigned int val = __ARCH_SPIN_LOCK_LOCKED__;
228
229         /*
230          * Per lkmm, smp_mb() is only required after _lock (and before_unlock)
231          * for ACQ and REL semantics respectively. However EX based spinlocks
232          * need the extra smp_mb to workaround a hardware quirk.
233          */
234         smp_mb();
235
236         __asm__ __volatile__(
237         "1:     ex  %0, [%1]            \n"
238 #ifdef CONFIG_EZNPS_MTM_EXT
239         "       .word %3                \n"
240 #endif
241         "       breq  %0, %2, 1b        \n"
242         : "+&r" (val)
243         : "r"(&(lock->slock)), "ir"(__ARCH_SPIN_LOCK_LOCKED__)
244 #ifdef CONFIG_EZNPS_MTM_EXT
245         , "i"(CTOP_INST_SCHD_RW)
246 #endif
247         : "memory");
248
249         smp_mb();
250 }
251
252 /* 1 - lock taken successfully */
253 static inline int arch_spin_trylock(arch_spinlock_t *lock)
254 {
255         unsigned int val = __ARCH_SPIN_LOCK_LOCKED__;
256
257         smp_mb();
258
259         __asm__ __volatile__(
260         "1:     ex  %0, [%1]            \n"
261         : "+r" (val)
262         : "r"(&(lock->slock))
263         : "memory");
264
265         smp_mb();
266
267         return (val == __ARCH_SPIN_LOCK_UNLOCKED__);
268 }
269
270 static inline void arch_spin_unlock(arch_spinlock_t *lock)
271 {
272         unsigned int val = __ARCH_SPIN_LOCK_UNLOCKED__;
273
274         /*
275          * RELEASE barrier: given the instructions avail on ARCv2, full barrier
276          * is the only option
277          */
278         smp_mb();
279
280         /*
281          * EX is not really required here, a simple STore of 0 suffices.
282          * However this causes tasklist livelocks in SystemC based SMP virtual
283          * platforms where the systemc core scheduler uses EX as a cue for
284          * moving to next core. Do a git log of this file for details
285          */
286         __asm__ __volatile__(
287         "       ex  %0, [%1]            \n"
288         : "+r" (val)
289         : "r"(&(lock->slock))
290         : "memory");
291
292         /*
293          * see pairing version/comment in arch_spin_lock above
294          */
295         smp_mb();
296 }
297
298 /*
299  * Read-write spinlocks, allowing multiple readers but only one writer.
300  * Unfair locking as Writers could be starved indefinitely by Reader(s)
301  *
302  * The spinlock itself is contained in @counter and access to it is
303  * serialized with @lock_mutex.
304  */
305
306 /* 1 - lock taken successfully */
307 static inline int arch_read_trylock(arch_rwlock_t *rw)
308 {
309         int ret = 0;
310         unsigned long flags;
311
312         local_irq_save(flags);
313         arch_spin_lock(&(rw->lock_mutex));
314
315         /*
316          * zero means writer holds the lock exclusively, deny Reader.
317          * Otherwise grant lock to first/subseq reader
318          */
319         if (rw->counter > 0) {
320                 rw->counter--;
321                 ret = 1;
322         }
323
324         arch_spin_unlock(&(rw->lock_mutex));
325         local_irq_restore(flags);
326
327         return ret;
328 }
329
330 /* 1 - lock taken successfully */
331 static inline int arch_write_trylock(arch_rwlock_t *rw)
332 {
333         int ret = 0;
334         unsigned long flags;
335
336         local_irq_save(flags);
337         arch_spin_lock(&(rw->lock_mutex));
338
339         /*
340          * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
341          * deny writer. Otherwise if unlocked grant to writer
342          * Hence the claim that Linux rwlocks are unfair to writers.
343          * (can be starved for an indefinite time by readers).
344          */
345         if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
346                 rw->counter = 0;
347                 ret = 1;
348         }
349         arch_spin_unlock(&(rw->lock_mutex));
350         local_irq_restore(flags);
351
352         return ret;
353 }
354
355 static inline void arch_read_lock(arch_rwlock_t *rw)
356 {
357         while (!arch_read_trylock(rw))
358                 cpu_relax();
359 }
360
361 static inline void arch_write_lock(arch_rwlock_t *rw)
362 {
363         while (!arch_write_trylock(rw))
364                 cpu_relax();
365 }
366
367 static inline void arch_read_unlock(arch_rwlock_t *rw)
368 {
369         unsigned long flags;
370
371         local_irq_save(flags);
372         arch_spin_lock(&(rw->lock_mutex));
373         rw->counter++;
374         arch_spin_unlock(&(rw->lock_mutex));
375         local_irq_restore(flags);
376 }
377
378 static inline void arch_write_unlock(arch_rwlock_t *rw)
379 {
380         unsigned long flags;
381
382         local_irq_save(flags);
383         arch_spin_lock(&(rw->lock_mutex));
384         rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
385         arch_spin_unlock(&(rw->lock_mutex));
386         local_irq_restore(flags);
387 }
388
389 #endif
390
391 #endif /* __ASM_SPINLOCK_H */