spinlock.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #ifndef __ASM_SPINLOCK_H
  2. #define __ASM_SPINLOCK_H
  3. #if __LINUX_ARM_ARCH__ < 6
  4. #error SMP not supported on pre-ARMv6 CPUs
  5. #endif
  6. #include <asm/processor.h>
  7. /*
  8. * sev and wfe are ARMv6K extensions. Uniprocessor ARMv6 may not have the K
  9. * extensions, so when running on UP, we have to patch these instructions away.
  10. */
  11. #define ALT_SMP(smp, up) \
  12. "9998: " smp "\n" \
  13. " .pushsection \".alt.smp.init\", \"a\"\n" \
  14. " .long 9998b\n" \
  15. " " up "\n" \
  16. " .popsection\n"
  17. #ifdef CONFIG_THUMB2_KERNEL
  18. #define SEV ALT_SMP("sev.w", "nop.w")
  19. /*
  20. * For Thumb-2, special care is needed to ensure that the conditional WFE
  21. * instruction really does assemble to exactly 4 bytes (as required by
  22. * the SMP_ON_UP fixup code). By itself "wfene" might cause the
  23. * assembler to insert a extra (16-bit) IT instruction, depending on the
  24. * presence or absence of neighbouring conditional instructions.
  25. *
  26. * To avoid this unpredictableness, an approprite IT is inserted explicitly:
  27. * the assembler won't change IT instructions which are explicitly present
  28. * in the input.
  29. */
  30. #define WFE(cond) ALT_SMP( \
  31. "it " cond "\n\t" \
  32. "wfe" cond ".n", \
  33. \
  34. "nop.w" \
  35. )
  36. #else
  37. #define SEV ALT_SMP("sev", "nop")
  38. #define WFE(cond) ALT_SMP("wfe" cond, "nop")
  39. #endif
  40. static inline void dsb_sev(void)
  41. {
  42. #if __LINUX_ARM_ARCH__ >= 7
  43. __asm__ __volatile__ (
  44. "dsb\n"
  45. SEV
  46. );
  47. #else
  48. __asm__ __volatile__ (
  49. "mcr p15, 0, %0, c7, c10, 4\n"
  50. SEV
  51. : : "r" (0)
  52. );
  53. #endif
  54. }
  55. /*
  56. * ARMv6 ticket-based spin-locking.
  57. *
  58. * A memory barrier is required after we get a lock, and before we
  59. * release it, because V6 CPUs are assumed to have weakly ordered
  60. * memory.
  61. */
  62. #define arch_spin_unlock_wait(lock) \
  63. do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
  64. #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
  65. static inline void arch_spin_lock(arch_spinlock_t *lock)
  66. {
  67. unsigned long tmp;
  68. u32 newval;
  69. arch_spinlock_t lockval;
  70. __asm__ __volatile__(
  71. "1: ldrex %0, [%3]\n"
  72. " add %1, %0, %4\n"
  73. " strex %2, %1, [%3]\n"
  74. " teq %2, #0\n"
  75. " bne 1b"
  76. : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
  77. : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
  78. : "cc");
  79. while (lockval.tickets.next != lockval.tickets.owner) {
  80. wfe();
  81. lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
  82. }
  83. smp_mb();
  84. }
  85. static inline int arch_spin_trylock(arch_spinlock_t *lock)
  86. {
  87. unsigned long tmp;
  88. u32 slock;
  89. __asm__ __volatile__(
  90. " ldrex %0, [%2]\n"
  91. " subs %1, %0, %0, ror #16\n"
  92. " addeq %0, %0, %3\n"
  93. " strexeq %1, %0, [%2]"
  94. : "=&r" (slock), "=&r" (tmp)
  95. : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
  96. : "cc");
  97. if (tmp == 0) {
  98. smp_mb();
  99. return 1;
  100. } else {
  101. return 0;
  102. }
  103. }
  104. static inline void arch_spin_unlock(arch_spinlock_t *lock)
  105. {
  106. unsigned long tmp;
  107. u32 slock;
  108. smp_mb();
  109. __asm__ __volatile__(
  110. " mov %1, #1\n"
  111. "1: ldrex %0, [%2]\n"
  112. " uadd16 %0, %0, %1\n"
  113. " strex %1, %0, [%2]\n"
  114. " teq %1, #0\n"
  115. " bne 1b"
  116. : "=&r" (slock), "=&r" (tmp)
  117. : "r" (&lock->slock)
  118. : "cc");
  119. dsb_sev();
  120. }
  121. static inline int arch_spin_is_locked(arch_spinlock_t *lock)
  122. {
  123. struct __raw_tickets tickets = ACCESS_ONCE(lock->tickets);
  124. return tickets.owner != tickets.next;
  125. }
  126. static inline int arch_spin_is_contended(arch_spinlock_t *lock)
  127. {
  128. struct __raw_tickets tickets = ACCESS_ONCE(lock->tickets);
  129. return (tickets.next - tickets.owner) > 1;
  130. }
  131. #define arch_spin_is_contended arch_spin_is_contended
  132. /*
  133. * RWLOCKS
  134. *
  135. *
  136. * Write locks are easy - we just set bit 31. When unlocking, we can
  137. * just write zero since the lock is exclusively held.
  138. */
  139. static inline void arch_write_lock(arch_rwlock_t *rw)
  140. {
  141. unsigned long tmp;
  142. __asm__ __volatile__(
  143. "1: ldrex %0, [%1]\n"
  144. " teq %0, #0\n"
  145. WFE("ne")
  146. " strexeq %0, %2, [%1]\n"
  147. " teq %0, #0\n"
  148. " bne 1b"
  149. : "=&r" (tmp)
  150. : "r" (&rw->lock), "r" (0x80000000)
  151. : "cc");
  152. smp_mb();
  153. }
  154. static inline int arch_write_trylock(arch_rwlock_t *rw)
  155. {
  156. unsigned long tmp;
  157. __asm__ __volatile__(
  158. " ldrex %0, [%1]\n"
  159. " teq %0, #0\n"
  160. " strexeq %0, %2, [%1]"
  161. : "=&r" (tmp)
  162. : "r" (&rw->lock), "r" (0x80000000)
  163. : "cc");
  164. if (tmp == 0) {
  165. smp_mb();
  166. return 1;
  167. } else {
  168. return 0;
  169. }
  170. }
  171. static inline void arch_write_unlock(arch_rwlock_t *rw)
  172. {
  173. smp_mb();
  174. __asm__ __volatile__(
  175. "str %1, [%0]\n"
  176. :
  177. : "r" (&rw->lock), "r" (0)
  178. : "cc");
  179. dsb_sev();
  180. }
  181. /* write_can_lock - would write_trylock() succeed? */
  182. #define arch_write_can_lock(x) ((x)->lock == 0)
  183. /*
  184. * Read locks are a bit more hairy:
  185. * - Exclusively load the lock value.
  186. * - Increment it.
  187. * - Store new lock value if positive, and we still own this location.
  188. * If the value is negative, we've already failed.
  189. * - If we failed to store the value, we want a negative result.
  190. * - If we failed, try again.
  191. * Unlocking is similarly hairy. We may have multiple read locks
  192. * currently active. However, we know we won't have any write
  193. * locks.
  194. */
  195. static inline void arch_read_lock(arch_rwlock_t *rw)
  196. {
  197. unsigned long tmp, tmp2;
  198. __asm__ __volatile__(
  199. "1: ldrex %0, [%2]\n"
  200. " adds %0, %0, #1\n"
  201. " strexpl %1, %0, [%2]\n"
  202. WFE("mi")
  203. " rsbpls %0, %1, #0\n"
  204. " bmi 1b"
  205. : "=&r" (tmp), "=&r" (tmp2)
  206. : "r" (&rw->lock)
  207. : "cc");
  208. smp_mb();
  209. }
  210. static inline void arch_read_unlock(arch_rwlock_t *rw)
  211. {
  212. unsigned long tmp, tmp2;
  213. smp_mb();
  214. __asm__ __volatile__(
  215. "1: ldrex %0, [%2]\n"
  216. " sub %0, %0, #1\n"
  217. " strex %1, %0, [%2]\n"
  218. " teq %1, #0\n"
  219. " bne 1b"
  220. : "=&r" (tmp), "=&r" (tmp2)
  221. : "r" (&rw->lock)
  222. : "cc");
  223. if (tmp == 0)
  224. dsb_sev();
  225. }
  226. static inline int arch_read_trylock(arch_rwlock_t *rw)
  227. {
  228. unsigned long tmp, tmp2 = 1;
  229. __asm__ __volatile__(
  230. " ldrex %0, [%2]\n"
  231. " adds %0, %0, #1\n"
  232. " strexpl %1, %0, [%2]\n"
  233. : "=&r" (tmp), "+r" (tmp2)
  234. : "r" (&rw->lock)
  235. : "cc");
  236. smp_mb();
  237. return tmp2 == 0;
  238. }
  239. /* read_can_lock - would read_trylock() succeed? */
  240. #define arch_read_can_lock(x) ((x)->lock < 0x80000000)
  241. #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
  242. #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
  243. #define arch_spin_relax(lock) cpu_relax()
  244. #define arch_read_relax(lock) cpu_relax()
  245. #define arch_write_relax(lock) cpu_relax()
  246. #endif /* __ASM_SPINLOCK_H */