cmpxchg.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * This file is subject to the terms and conditions of the GNU General Public
  3. * License. See the file "COPYING" in the main directory of this archive
  4. * for more details.
  5. *
  6. * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
  7. */
  8. #ifndef __ASM_CMPXCHG_H
  9. #define __ASM_CMPXCHG_H
  10. #include <linux/bug.h>
  11. #include <linux/irqflags.h>
  12. #include <asm/compiler.h>
  13. #include <asm/war.h>
  14. /*
  15. * Using a branch-likely instruction to check the result of an sc instruction
  16. * works around a bug present in R10000 CPUs prior to revision 3.0 that could
  17. * cause ll-sc sequences to execute non-atomically.
  18. */
  19. #if R10000_LLSC_WAR
  20. # define __scbeqz "beqzl"
  21. #else
  22. # define __scbeqz "beqz"
  23. #endif
  24. /*
  25. * These functions doesn't exist, so if they are called you'll either:
  26. *
  27. * - Get an error at compile-time due to __compiletime_error, if supported by
  28. * your compiler.
  29. *
  30. * or:
  31. *
  32. * - Get an error at link-time due to the call to the missing function.
  33. */
  34. extern unsigned long __cmpxchg_called_with_bad_pointer(void)
  35. __compiletime_error("Bad argument size for cmpxchg");
  36. extern unsigned long __xchg_called_with_bad_pointer(void)
  37. __compiletime_error("Bad argument size for xchg");
  38. #define __xchg_asm(ld, st, m, val) \
  39. ({ \
  40. __typeof(*(m)) __ret; \
  41. \
  42. if (kernel_uses_llsc) { \
  43. __asm__ __volatile__( \
  44. " .set push \n" \
  45. " .set noat \n" \
  46. " .set " MIPS_ISA_ARCH_LEVEL " \n" \
  47. "1: " ld " %0, %2 # __xchg_asm \n" \
  48. " .set mips0 \n" \
  49. " move $1, %z3 \n" \
  50. " .set " MIPS_ISA_ARCH_LEVEL " \n" \
  51. " " st " $1, %1 \n" \
  52. "\t" __scbeqz " $1, 1b \n" \
  53. " .set pop \n" \
  54. : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
  55. : GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \
  56. : "memory"); \
  57. } else { \
  58. unsigned long __flags; \
  59. \
  60. raw_local_irq_save(__flags); \
  61. __ret = *m; \
  62. *m = val; \
  63. raw_local_irq_restore(__flags); \
  64. } \
  65. \
  66. __ret; \
  67. })
  68. extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
  69. unsigned int size);
  70. static __always_inline
  71. unsigned long __xchg(volatile void *ptr, unsigned long x, int size)
  72. {
  73. switch (size) {
  74. case 1:
  75. case 2:
  76. return __xchg_small(ptr, x, size);
  77. case 4:
  78. return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x);
  79. case 8:
  80. if (!IS_ENABLED(CONFIG_64BIT))
  81. return __xchg_called_with_bad_pointer();
  82. return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x);
  83. default:
  84. return __xchg_called_with_bad_pointer();
  85. }
  86. }
  87. #define xchg(ptr, x) \
  88. ({ \
  89. __typeof__(*(ptr)) __res; \
  90. \
  91. smp_mb__before_llsc(); \
  92. \
  93. __res = (__typeof__(*(ptr))) \
  94. __xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
  95. \
  96. smp_llsc_mb(); \
  97. \
  98. __res; \
  99. })
  100. #define __cmpxchg_asm(ld, st, m, old, new) \
  101. ({ \
  102. __typeof(*(m)) __ret; \
  103. \
  104. if (kernel_uses_llsc) { \
  105. __asm__ __volatile__( \
  106. " .set push \n" \
  107. " .set noat \n" \
  108. " .set "MIPS_ISA_ARCH_LEVEL" \n" \
  109. "1: " ld " %0, %2 # __cmpxchg_asm \n" \
  110. " bne %0, %z3, 2f \n" \
  111. " .set mips0 \n" \
  112. " move $1, %z4 \n" \
  113. " .set "MIPS_ISA_ARCH_LEVEL" \n" \
  114. " " st " $1, %1 \n" \
  115. "\t" __scbeqz " $1, 1b \n" \
  116. " .set pop \n" \
  117. "2: \n" \
  118. : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
  119. : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
  120. : "memory"); \
  121. } else { \
  122. unsigned long __flags; \
  123. \
  124. raw_local_irq_save(__flags); \
  125. __ret = *m; \
  126. if (__ret == old) \
  127. *m = new; \
  128. raw_local_irq_restore(__flags); \
  129. } \
  130. \
  131. __ret; \
  132. })
  133. extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
  134. unsigned long new, unsigned int size);
  135. static __always_inline
  136. unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
  137. unsigned long new, unsigned int size)
  138. {
  139. switch (size) {
  140. case 1:
  141. case 2:
  142. return __cmpxchg_small(ptr, old, new, size);
  143. case 4:
  144. return __cmpxchg_asm("ll", "sc", (volatile u32 *)ptr,
  145. (u32)old, new);
  146. case 8:
  147. /* lld/scd are only available for MIPS64 */
  148. if (!IS_ENABLED(CONFIG_64BIT))
  149. return __cmpxchg_called_with_bad_pointer();
  150. return __cmpxchg_asm("lld", "scd", (volatile u64 *)ptr,
  151. (u64)old, new);
  152. default:
  153. return __cmpxchg_called_with_bad_pointer();
  154. }
  155. }
  156. #define cmpxchg_local(ptr, old, new) \
  157. ((__typeof__(*(ptr))) \
  158. __cmpxchg((ptr), \
  159. (unsigned long)(__typeof__(*(ptr)))(old), \
  160. (unsigned long)(__typeof__(*(ptr)))(new), \
  161. sizeof(*(ptr))))
  162. #define cmpxchg(ptr, old, new) \
  163. ({ \
  164. __typeof__(*(ptr)) __res; \
  165. \
  166. smp_mb__before_llsc(); \
  167. __res = cmpxchg_local((ptr), (old), (new)); \
  168. smp_llsc_mb(); \
  169. \
  170. __res; \
  171. })
  172. #ifdef CONFIG_64BIT
  173. #define cmpxchg64_local(ptr, o, n) \
  174. ({ \
  175. BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
  176. cmpxchg_local((ptr), (o), (n)); \
  177. })
  178. #define cmpxchg64(ptr, o, n) \
  179. ({ \
  180. BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
  181. cmpxchg((ptr), (o), (n)); \
  182. })
  183. #else
  184. #include <asm-generic/cmpxchg-local.h>
  185. #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
  186. #ifndef CONFIG_SMP
  187. #define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
  188. #endif
  189. #endif
  190. #undef __scbeqz
  191. #endif /* __ASM_CMPXCHG_H */