spinlock.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 contended, res;
  88. u32 slock;
  89. do {
  90. __asm__ __volatile__(
  91. " ldrex %0, [%3]\n"
  92. " mov %2, #0\n"
  93. " subs %1, %0, %0, ror #16\n"
  94. " addeq %0, %0, %4\n"
  95. " strexeq %2, %0, [%3]"
  96. : "=&r" (slock), "=&r" (contended), "=r" (res)
  97. : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
  98. : "cc");
  99. } while (res);
  100. if (!contended) {
  101. smp_mb();
  102. return 1;
  103. } else {
  104. return 0;
  105. }
  106. }
  107. static inline void arch_spin_unlock(arch_spinlock_t *lock)
  108. {
  109. smp_mb();
  110. lock->tickets.owner++;
  111. dsb_sev();
  112. }
  113. static inline int arch_spin_is_locked(arch_spinlock_t *lock)
  114. {
  115. struct __raw_tickets tickets = ACCESS_ONCE(lock->tickets);
  116. return tickets.owner != tickets.next;
  117. }
  118. static inline int arch_spin_is_contended(arch_spinlock_t *lock)
  119. {
  120. struct __raw_tickets tickets = ACCESS_ONCE(lock->tickets);
  121. return (tickets.next - tickets.owner) > 1;
  122. }
  123. #define arch_spin_is_contended arch_spin_is_contended
  124. /*
  125. * RWLOCKS
  126. *
  127. *
  128. * Write locks are easy - we just set bit 31. When unlocking, we can
  129. * just write zero since the lock is exclusively held.
  130. */
  131. static inline void arch_write_lock(arch_rwlock_t *rw)
  132. {
  133. unsigned long tmp;
  134. __asm__ __volatile__(
  135. "1: ldrex %0, [%1]\n"
  136. " teq %0, #0\n"
  137. WFE("ne")
  138. " strexeq %0, %2, [%1]\n"
  139. " teq %0, #0\n"
  140. " bne 1b"
  141. : "=&r" (tmp)
  142. : "r" (&rw->lock), "r" (0x80000000)
  143. : "cc");
  144. smp_mb();
  145. }
  146. static inline int arch_write_trylock(arch_rwlock_t *rw)
  147. {
  148. unsigned long tmp;
  149. __asm__ __volatile__(
  150. " ldrex %0, [%1]\n"
  151. " teq %0, #0\n"
  152. " strexeq %0, %2, [%1]"
  153. : "=&r" (tmp)
  154. : "r" (&rw->lock), "r" (0x80000000)
  155. : "cc");
  156. if (tmp == 0) {
  157. smp_mb();
  158. return 1;
  159. } else {
  160. return 0;
  161. }
  162. }
  163. static inline void arch_write_unlock(arch_rwlock_t *rw)
  164. {
  165. smp_mb();
  166. __asm__ __volatile__(
  167. "str %1, [%0]\n"
  168. :
  169. : "r" (&rw->lock), "r" (0)
  170. : "cc");
  171. dsb_sev();
  172. }
  173. /* write_can_lock - would write_trylock() succeed? */
  174. #define arch_write_can_lock(x) ((x)->lock == 0)
  175. /*
  176. * Read locks are a bit more hairy:
  177. * - Exclusively load the lock value.
  178. * - Increment it.
  179. * - Store new lock value if positive, and we still own this location.
  180. * If the value is negative, we've already failed.
  181. * - If we failed to store the value, we want a negative result.
  182. * - If we failed, try again.
  183. * Unlocking is similarly hairy. We may have multiple read locks
  184. * currently active. However, we know we won't have any write
  185. * locks.
  186. */
  187. static inline void arch_read_lock(arch_rwlock_t *rw)
  188. {
  189. unsigned long tmp, tmp2;
  190. __asm__ __volatile__(
  191. "1: ldrex %0, [%2]\n"
  192. " adds %0, %0, #1\n"
  193. " strexpl %1, %0, [%2]\n"
  194. WFE("mi")
  195. " rsbpls %0, %1, #0\n"
  196. " bmi 1b"
  197. : "=&r" (tmp), "=&r" (tmp2)
  198. : "r" (&rw->lock)
  199. : "cc");
  200. smp_mb();
  201. }
  202. static inline void arch_read_unlock(arch_rwlock_t *rw)
  203. {
  204. unsigned long tmp, tmp2;
  205. smp_mb();
  206. __asm__ __volatile__(
  207. "1: ldrex %0, [%2]\n"
  208. " sub %0, %0, #1\n"
  209. " strex %1, %0, [%2]\n"
  210. " teq %1, #0\n"
  211. " bne 1b"
  212. : "=&r" (tmp), "=&r" (tmp2)
  213. : "r" (&rw->lock)
  214. : "cc");
  215. if (tmp == 0)
  216. dsb_sev();
  217. }
  218. static inline int arch_read_trylock(arch_rwlock_t *rw)
  219. {
  220. unsigned long tmp, tmp2 = 1;
  221. __asm__ __volatile__(
  222. " ldrex %0, [%2]\n"
  223. " adds %0, %0, #1\n"
  224. " strexpl %1, %0, [%2]\n"
  225. : "=&r" (tmp), "+r" (tmp2)
  226. : "r" (&rw->lock)
  227. : "cc");
  228. smp_mb();
  229. return tmp2 == 0;
  230. }
  231. /* read_can_lock - would read_trylock() succeed? */
  232. #define arch_read_can_lock(x) ((x)->lock < 0x80000000)
  233. #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
  234. #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
  235. #define arch_spin_relax(lock) cpu_relax()
  236. #define arch_read_relax(lock) cpu_relax()
  237. #define arch_write_relax(lock) cpu_relax()
  238. #endif /* __ASM_SPINLOCK_H */