spinlock.h 5.8 KB

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