]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blob - arch/arc/include/asm/spinlock.h
Merge tag 'armsoc-cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[mirror_ubuntu-eoan-kernel.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 #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
18 #define arch_spin_unlock_wait(x) \
19 do { while (arch_spin_is_locked(x)) cpu_relax(); } while (0)
20
21 #ifdef CONFIG_ARC_HAS_LLSC
22
23 /*
24 * A normal LLOCK/SCOND based system, w/o need for livelock workaround
25 */
26 #ifndef CONFIG_ARC_STAR_9000923308
27
28 static inline void arch_spin_lock(arch_spinlock_t *lock)
29 {
30 unsigned int val;
31
32 smp_mb();
33
34 __asm__ __volatile__(
35 "1: llock %[val], [%[slock]] \n"
36 " breq %[val], %[LOCKED], 1b \n" /* spin while LOCKED */
37 " scond %[LOCKED], [%[slock]] \n" /* acquire */
38 " bnz 1b \n"
39 " \n"
40 : [val] "=&r" (val)
41 : [slock] "r" (&(lock->slock)),
42 [LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__)
43 : "memory", "cc");
44
45 smp_mb();
46 }
47
48 /* 1 - lock taken successfully */
49 static inline int arch_spin_trylock(arch_spinlock_t *lock)
50 {
51 unsigned int val, got_it = 0;
52
53 smp_mb();
54
55 __asm__ __volatile__(
56 "1: llock %[val], [%[slock]] \n"
57 " breq %[val], %[LOCKED], 4f \n" /* already LOCKED, just bail */
58 " scond %[LOCKED], [%[slock]] \n" /* acquire */
59 " bnz 1b \n"
60 " mov %[got_it], 1 \n"
61 "4: \n"
62 " \n"
63 : [val] "=&r" (val),
64 [got_it] "+&r" (got_it)
65 : [slock] "r" (&(lock->slock)),
66 [LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__)
67 : "memory", "cc");
68
69 smp_mb();
70
71 return got_it;
72 }
73
74 static inline void arch_spin_unlock(arch_spinlock_t *lock)
75 {
76 smp_mb();
77
78 lock->slock = __ARCH_SPIN_LOCK_UNLOCKED__;
79
80 smp_mb();
81 }
82
83 /*
84 * Read-write spinlocks, allowing multiple readers but only one writer.
85 * Unfair locking as Writers could be starved indefinitely by Reader(s)
86 */
87
88 static inline void arch_read_lock(arch_rwlock_t *rw)
89 {
90 unsigned int val;
91
92 smp_mb();
93
94 /*
95 * zero means writer holds the lock exclusively, deny Reader.
96 * Otherwise grant lock to first/subseq reader
97 *
98 * if (rw->counter > 0) {
99 * rw->counter--;
100 * ret = 1;
101 * }
102 */
103
104 __asm__ __volatile__(
105 "1: llock %[val], [%[rwlock]] \n"
106 " brls %[val], %[WR_LOCKED], 1b\n" /* <= 0: spin while write locked */
107 " sub %[val], %[val], 1 \n" /* reader lock */
108 " scond %[val], [%[rwlock]] \n"
109 " bnz 1b \n"
110 " \n"
111 : [val] "=&r" (val)
112 : [rwlock] "r" (&(rw->counter)),
113 [WR_LOCKED] "ir" (0)
114 : "memory", "cc");
115
116 smp_mb();
117 }
118
119 /* 1 - lock taken successfully */
120 static inline int arch_read_trylock(arch_rwlock_t *rw)
121 {
122 unsigned int val, got_it = 0;
123
124 smp_mb();
125
126 __asm__ __volatile__(
127 "1: llock %[val], [%[rwlock]] \n"
128 " brls %[val], %[WR_LOCKED], 4f\n" /* <= 0: already write locked, bail */
129 " sub %[val], %[val], 1 \n" /* counter-- */
130 " scond %[val], [%[rwlock]] \n"
131 " bnz 1b \n" /* retry if collided with someone */
132 " mov %[got_it], 1 \n"
133 " \n"
134 "4: ; --- done --- \n"
135
136 : [val] "=&r" (val),
137 [got_it] "+&r" (got_it)
138 : [rwlock] "r" (&(rw->counter)),
139 [WR_LOCKED] "ir" (0)
140 : "memory", "cc");
141
142 smp_mb();
143
144 return got_it;
145 }
146
147 static inline void arch_write_lock(arch_rwlock_t *rw)
148 {
149 unsigned int val;
150
151 smp_mb();
152
153 /*
154 * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
155 * deny writer. Otherwise if unlocked grant to writer
156 * Hence the claim that Linux rwlocks are unfair to writers.
157 * (can be starved for an indefinite time by readers).
158 *
159 * if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
160 * rw->counter = 0;
161 * ret = 1;
162 * }
163 */
164
165 __asm__ __volatile__(
166 "1: llock %[val], [%[rwlock]] \n"
167 " brne %[val], %[UNLOCKED], 1b \n" /* while !UNLOCKED spin */
168 " mov %[val], %[WR_LOCKED] \n"
169 " scond %[val], [%[rwlock]] \n"
170 " bnz 1b \n"
171 " \n"
172 : [val] "=&r" (val)
173 : [rwlock] "r" (&(rw->counter)),
174 [UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__),
175 [WR_LOCKED] "ir" (0)
176 : "memory", "cc");
177
178 smp_mb();
179 }
180
181 /* 1 - lock taken successfully */
182 static inline int arch_write_trylock(arch_rwlock_t *rw)
183 {
184 unsigned int val, got_it = 0;
185
186 smp_mb();
187
188 __asm__ __volatile__(
189 "1: llock %[val], [%[rwlock]] \n"
190 " brne %[val], %[UNLOCKED], 4f \n" /* !UNLOCKED, bail */
191 " mov %[val], %[WR_LOCKED] \n"
192 " scond %[val], [%[rwlock]] \n"
193 " bnz 1b \n" /* retry if collided with someone */
194 " mov %[got_it], 1 \n"
195 " \n"
196 "4: ; --- done --- \n"
197
198 : [val] "=&r" (val),
199 [got_it] "+&r" (got_it)
200 : [rwlock] "r" (&(rw->counter)),
201 [UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__),
202 [WR_LOCKED] "ir" (0)
203 : "memory", "cc");
204
205 smp_mb();
206
207 return got_it;
208 }
209
210 static inline void arch_read_unlock(arch_rwlock_t *rw)
211 {
212 unsigned int val;
213
214 smp_mb();
215
216 /*
217 * rw->counter++;
218 */
219 __asm__ __volatile__(
220 "1: llock %[val], [%[rwlock]] \n"
221 " add %[val], %[val], 1 \n"
222 " scond %[val], [%[rwlock]] \n"
223 " bnz 1b \n"
224 " \n"
225 : [val] "=&r" (val)
226 : [rwlock] "r" (&(rw->counter))
227 : "memory", "cc");
228
229 smp_mb();
230 }
231
232 static inline void arch_write_unlock(arch_rwlock_t *rw)
233 {
234 smp_mb();
235
236 rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
237
238 smp_mb();
239 }
240
241 #else /* CONFIG_ARC_STAR_9000923308 */
242
243 /*
244 * HS38x4 could get into a LLOCK/SCOND livelock in case of multiple overlapping
245 * coherency transactions in the SCU. The exclusive line state keeps rotating
246 * among contenting cores leading to a never ending cycle. So break the cycle
247 * by deferring the retry of failed exclusive access (SCOND). The actual delay
248 * needed is function of number of contending cores as well as the unrelated
249 * coherency traffic from other cores. To keep the code simple, start off with
250 * small delay of 1 which would suffice most cases and in case of contention
251 * double the delay. Eventually the delay is sufficient such that the coherency
252 * pipeline is drained, thus a subsequent exclusive access would succeed.
253 */
254
255 #define SCOND_FAIL_RETRY_VAR_DEF \
256 unsigned int delay, tmp; \
257
258 #define SCOND_FAIL_RETRY_ASM \
259 " ; --- scond fail delay --- \n" \
260 " mov %[tmp], %[delay] \n" /* tmp = delay */ \
261 "2: brne.d %[tmp], 0, 2b \n" /* while (tmp != 0) */ \
262 " sub %[tmp], %[tmp], 1 \n" /* tmp-- */ \
263 " rol %[delay], %[delay] \n" /* delay *= 2 */ \
264 " b 1b \n" /* start over */ \
265 " \n" \
266 "4: ; --- done --- \n" \
267
268 #define SCOND_FAIL_RETRY_VARS \
269 ,[delay] "=&r" (delay), [tmp] "=&r" (tmp) \
270
271 static inline void arch_spin_lock(arch_spinlock_t *lock)
272 {
273 unsigned int val;
274 SCOND_FAIL_RETRY_VAR_DEF;
275
276 smp_mb();
277
278 __asm__ __volatile__(
279 "0: mov %[delay], 1 \n"
280 "1: llock %[val], [%[slock]] \n"
281 " breq %[val], %[LOCKED], 0b \n" /* spin while LOCKED */
282 " scond %[LOCKED], [%[slock]] \n" /* acquire */
283 " bz 4f \n" /* done */
284 " \n"
285 SCOND_FAIL_RETRY_ASM
286
287 : [val] "=&r" (val)
288 SCOND_FAIL_RETRY_VARS
289 : [slock] "r" (&(lock->slock)),
290 [LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__)
291 : "memory", "cc");
292
293 smp_mb();
294 }
295
296 /* 1 - lock taken successfully */
297 static inline int arch_spin_trylock(arch_spinlock_t *lock)
298 {
299 unsigned int val, got_it = 0;
300 SCOND_FAIL_RETRY_VAR_DEF;
301
302 smp_mb();
303
304 __asm__ __volatile__(
305 "0: mov %[delay], 1 \n"
306 "1: llock %[val], [%[slock]] \n"
307 " breq %[val], %[LOCKED], 4f \n" /* already LOCKED, just bail */
308 " scond %[LOCKED], [%[slock]] \n" /* acquire */
309 " bz.d 4f \n"
310 " mov.z %[got_it], 1 \n" /* got it */
311 " \n"
312 SCOND_FAIL_RETRY_ASM
313
314 : [val] "=&r" (val),
315 [got_it] "+&r" (got_it)
316 SCOND_FAIL_RETRY_VARS
317 : [slock] "r" (&(lock->slock)),
318 [LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__)
319 : "memory", "cc");
320
321 smp_mb();
322
323 return got_it;
324 }
325
326 static inline void arch_spin_unlock(arch_spinlock_t *lock)
327 {
328 smp_mb();
329
330 lock->slock = __ARCH_SPIN_LOCK_UNLOCKED__;
331
332 smp_mb();
333 }
334
335 /*
336 * Read-write spinlocks, allowing multiple readers but only one writer.
337 * Unfair locking as Writers could be starved indefinitely by Reader(s)
338 */
339
340 static inline void arch_read_lock(arch_rwlock_t *rw)
341 {
342 unsigned int val;
343 SCOND_FAIL_RETRY_VAR_DEF;
344
345 smp_mb();
346
347 /*
348 * zero means writer holds the lock exclusively, deny Reader.
349 * Otherwise grant lock to first/subseq reader
350 *
351 * if (rw->counter > 0) {
352 * rw->counter--;
353 * ret = 1;
354 * }
355 */
356
357 __asm__ __volatile__(
358 "0: mov %[delay], 1 \n"
359 "1: llock %[val], [%[rwlock]] \n"
360 " brls %[val], %[WR_LOCKED], 0b\n" /* <= 0: spin while write locked */
361 " sub %[val], %[val], 1 \n" /* reader lock */
362 " scond %[val], [%[rwlock]] \n"
363 " bz 4f \n" /* done */
364 " \n"
365 SCOND_FAIL_RETRY_ASM
366
367 : [val] "=&r" (val)
368 SCOND_FAIL_RETRY_VARS
369 : [rwlock] "r" (&(rw->counter)),
370 [WR_LOCKED] "ir" (0)
371 : "memory", "cc");
372
373 smp_mb();
374 }
375
376 /* 1 - lock taken successfully */
377 static inline int arch_read_trylock(arch_rwlock_t *rw)
378 {
379 unsigned int val, got_it = 0;
380 SCOND_FAIL_RETRY_VAR_DEF;
381
382 smp_mb();
383
384 __asm__ __volatile__(
385 "0: mov %[delay], 1 \n"
386 "1: llock %[val], [%[rwlock]] \n"
387 " brls %[val], %[WR_LOCKED], 4f\n" /* <= 0: already write locked, bail */
388 " sub %[val], %[val], 1 \n" /* counter-- */
389 " scond %[val], [%[rwlock]] \n"
390 " bz.d 4f \n"
391 " mov.z %[got_it], 1 \n" /* got it */
392 " \n"
393 SCOND_FAIL_RETRY_ASM
394
395 : [val] "=&r" (val),
396 [got_it] "+&r" (got_it)
397 SCOND_FAIL_RETRY_VARS
398 : [rwlock] "r" (&(rw->counter)),
399 [WR_LOCKED] "ir" (0)
400 : "memory", "cc");
401
402 smp_mb();
403
404 return got_it;
405 }
406
407 static inline void arch_write_lock(arch_rwlock_t *rw)
408 {
409 unsigned int val;
410 SCOND_FAIL_RETRY_VAR_DEF;
411
412 smp_mb();
413
414 /*
415 * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
416 * deny writer. Otherwise if unlocked grant to writer
417 * Hence the claim that Linux rwlocks are unfair to writers.
418 * (can be starved for an indefinite time by readers).
419 *
420 * if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
421 * rw->counter = 0;
422 * ret = 1;
423 * }
424 */
425
426 __asm__ __volatile__(
427 "0: mov %[delay], 1 \n"
428 "1: llock %[val], [%[rwlock]] \n"
429 " brne %[val], %[UNLOCKED], 0b \n" /* while !UNLOCKED spin */
430 " mov %[val], %[WR_LOCKED] \n"
431 " scond %[val], [%[rwlock]] \n"
432 " bz 4f \n"
433 " \n"
434 SCOND_FAIL_RETRY_ASM
435
436 : [val] "=&r" (val)
437 SCOND_FAIL_RETRY_VARS
438 : [rwlock] "r" (&(rw->counter)),
439 [UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__),
440 [WR_LOCKED] "ir" (0)
441 : "memory", "cc");
442
443 smp_mb();
444 }
445
446 /* 1 - lock taken successfully */
447 static inline int arch_write_trylock(arch_rwlock_t *rw)
448 {
449 unsigned int val, got_it = 0;
450 SCOND_FAIL_RETRY_VAR_DEF;
451
452 smp_mb();
453
454 __asm__ __volatile__(
455 "0: mov %[delay], 1 \n"
456 "1: llock %[val], [%[rwlock]] \n"
457 " brne %[val], %[UNLOCKED], 4f \n" /* !UNLOCKED, bail */
458 " mov %[val], %[WR_LOCKED] \n"
459 " scond %[val], [%[rwlock]] \n"
460 " bz.d 4f \n"
461 " mov.z %[got_it], 1 \n" /* got it */
462 " \n"
463 SCOND_FAIL_RETRY_ASM
464
465 : [val] "=&r" (val),
466 [got_it] "+&r" (got_it)
467 SCOND_FAIL_RETRY_VARS
468 : [rwlock] "r" (&(rw->counter)),
469 [UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__),
470 [WR_LOCKED] "ir" (0)
471 : "memory", "cc");
472
473 smp_mb();
474
475 return got_it;
476 }
477
478 static inline void arch_read_unlock(arch_rwlock_t *rw)
479 {
480 unsigned int val;
481
482 smp_mb();
483
484 /*
485 * rw->counter++;
486 */
487 __asm__ __volatile__(
488 "1: llock %[val], [%[rwlock]] \n"
489 " add %[val], %[val], 1 \n"
490 " scond %[val], [%[rwlock]] \n"
491 " bnz 1b \n"
492 " \n"
493 : [val] "=&r" (val)
494 : [rwlock] "r" (&(rw->counter))
495 : "memory", "cc");
496
497 smp_mb();
498 }
499
500 static inline void arch_write_unlock(arch_rwlock_t *rw)
501 {
502 unsigned int val;
503
504 smp_mb();
505
506 /*
507 * rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
508 */
509 __asm__ __volatile__(
510 "1: llock %[val], [%[rwlock]] \n"
511 " scond %[UNLOCKED], [%[rwlock]]\n"
512 " bnz 1b \n"
513 " \n"
514 : [val] "=&r" (val)
515 : [rwlock] "r" (&(rw->counter)),
516 [UNLOCKED] "r" (__ARCH_RW_LOCK_UNLOCKED__)
517 : "memory", "cc");
518
519 smp_mb();
520 }
521
522 #undef SCOND_FAIL_RETRY_VAR_DEF
523 #undef SCOND_FAIL_RETRY_ASM
524 #undef SCOND_FAIL_RETRY_VARS
525
526 #endif /* CONFIG_ARC_STAR_9000923308 */
527
528 #else /* !CONFIG_ARC_HAS_LLSC */
529
530 static inline void arch_spin_lock(arch_spinlock_t *lock)
531 {
532 unsigned int val = __ARCH_SPIN_LOCK_LOCKED__;
533
534 /*
535 * This smp_mb() is technically superfluous, we only need the one
536 * after the lock for providing the ACQUIRE semantics.
537 * However doing the "right" thing was regressing hackbench
538 * so keeping this, pending further investigation
539 */
540 smp_mb();
541
542 __asm__ __volatile__(
543 "1: ex %0, [%1] \n"
544 " breq %0, %2, 1b \n"
545 : "+&r" (val)
546 : "r"(&(lock->slock)), "ir"(__ARCH_SPIN_LOCK_LOCKED__)
547 : "memory");
548
549 /*
550 * ACQUIRE barrier to ensure load/store after taking the lock
551 * don't "bleed-up" out of the critical section (leak-in is allowed)
552 * http://www.spinics.net/lists/kernel/msg2010409.html
553 *
554 * ARCv2 only has load-load, store-store and all-all barrier
555 * thus need the full all-all barrier
556 */
557 smp_mb();
558 }
559
560 /* 1 - lock taken successfully */
561 static inline int arch_spin_trylock(arch_spinlock_t *lock)
562 {
563 unsigned int val = __ARCH_SPIN_LOCK_LOCKED__;
564
565 smp_mb();
566
567 __asm__ __volatile__(
568 "1: ex %0, [%1] \n"
569 : "+r" (val)
570 : "r"(&(lock->slock))
571 : "memory");
572
573 smp_mb();
574
575 return (val == __ARCH_SPIN_LOCK_UNLOCKED__);
576 }
577
578 static inline void arch_spin_unlock(arch_spinlock_t *lock)
579 {
580 unsigned int val = __ARCH_SPIN_LOCK_UNLOCKED__;
581
582 /*
583 * RELEASE barrier: given the instructions avail on ARCv2, full barrier
584 * is the only option
585 */
586 smp_mb();
587
588 __asm__ __volatile__(
589 " ex %0, [%1] \n"
590 : "+r" (val)
591 : "r"(&(lock->slock))
592 : "memory");
593
594 /*
595 * superfluous, but keeping for now - see pairing version in
596 * arch_spin_lock above
597 */
598 smp_mb();
599 }
600
601 /*
602 * Read-write spinlocks, allowing multiple readers but only one writer.
603 * Unfair locking as Writers could be starved indefinitely by Reader(s)
604 *
605 * The spinlock itself is contained in @counter and access to it is
606 * serialized with @lock_mutex.
607 */
608
609 /* 1 - lock taken successfully */
610 static inline int arch_read_trylock(arch_rwlock_t *rw)
611 {
612 int ret = 0;
613
614 arch_spin_lock(&(rw->lock_mutex));
615
616 /*
617 * zero means writer holds the lock exclusively, deny Reader.
618 * Otherwise grant lock to first/subseq reader
619 */
620 if (rw->counter > 0) {
621 rw->counter--;
622 ret = 1;
623 }
624
625 arch_spin_unlock(&(rw->lock_mutex));
626
627 smp_mb();
628 return ret;
629 }
630
631 /* 1 - lock taken successfully */
632 static inline int arch_write_trylock(arch_rwlock_t *rw)
633 {
634 int ret = 0;
635
636 arch_spin_lock(&(rw->lock_mutex));
637
638 /*
639 * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
640 * deny writer. Otherwise if unlocked grant to writer
641 * Hence the claim that Linux rwlocks are unfair to writers.
642 * (can be starved for an indefinite time by readers).
643 */
644 if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
645 rw->counter = 0;
646 ret = 1;
647 }
648 arch_spin_unlock(&(rw->lock_mutex));
649
650 return ret;
651 }
652
653 static inline void arch_read_lock(arch_rwlock_t *rw)
654 {
655 while (!arch_read_trylock(rw))
656 cpu_relax();
657 }
658
659 static inline void arch_write_lock(arch_rwlock_t *rw)
660 {
661 while (!arch_write_trylock(rw))
662 cpu_relax();
663 }
664
665 static inline void arch_read_unlock(arch_rwlock_t *rw)
666 {
667 arch_spin_lock(&(rw->lock_mutex));
668 rw->counter++;
669 arch_spin_unlock(&(rw->lock_mutex));
670 }
671
672 static inline void arch_write_unlock(arch_rwlock_t *rw)
673 {
674 arch_spin_lock(&(rw->lock_mutex));
675 rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
676 arch_spin_unlock(&(rw->lock_mutex));
677 }
678
679 #endif
680
681 #define arch_read_can_lock(x) ((x)->counter > 0)
682 #define arch_write_can_lock(x) ((x)->counter == __ARCH_RW_LOCK_UNLOCKED__)
683
684 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
685 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
686
687 #define arch_spin_relax(lock) cpu_relax()
688 #define arch_read_relax(lock) cpu_relax()
689 #define arch_write_relax(lock) cpu_relax()
690
691 #endif /* __ASM_SPINLOCK_H */