atomic_ll_sc.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /* SPDX-License-Identifier: GPL-2.0-only */
  2. /*
  3. * Based on arch/arm/include/asm/atomic.h
  4. *
  5. * Copyright (C) 1996 Russell King.
  6. * Copyright (C) 2002 Deep Blue Solutions Ltd.
  7. * Copyright (C) 2012 ARM Ltd.
  8. */
  9. #ifndef __ASM_ATOMIC_LL_SC_H
  10. #define __ASM_ATOMIC_LL_SC_H
  11. #include <linux/stringify.h>
  12. #ifndef CONFIG_CC_HAS_K_CONSTRAINT
  13. #define K
  14. #endif
  15. /*
  16. * AArch64 UP and SMP safe atomic ops. We use load exclusive and
  17. * store exclusive to ensure that these are atomic. We may loop
  18. * to ensure that the update happens.
  19. */
  20. #define ATOMIC_OP(op, asm_op, constraint) \
  21. static __always_inline void \
  22. __ll_sc_atomic_##op(int i, atomic_t *v) \
  23. { \
  24. unsigned long tmp; \
  25. int result; \
  26. \
  27. asm volatile("// atomic_" #op "\n" \
  28. " prfm pstl1strm, %2\n" \
  29. "1: ldxr %w0, %2\n" \
  30. " " #asm_op " %w0, %w0, %w3\n" \
  31. " stxr %w1, %w0, %2\n" \
  32. " cbnz %w1, 1b\n" \
  33. : "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \
  34. : __stringify(constraint) "r" (i)); \
  35. }
  36. #define ATOMIC_OP_RETURN(name, mb, acq, rel, cl, op, asm_op, constraint)\
  37. static __always_inline int \
  38. __ll_sc_atomic_##op##_return##name(int i, atomic_t *v) \
  39. { \
  40. unsigned long tmp; \
  41. int result; \
  42. \
  43. asm volatile("// atomic_" #op "_return" #name "\n" \
  44. " prfm pstl1strm, %2\n" \
  45. "1: ld" #acq "xr %w0, %2\n" \
  46. " " #asm_op " %w0, %w0, %w3\n" \
  47. " st" #rel "xr %w1, %w0, %2\n" \
  48. " cbnz %w1, 1b\n" \
  49. " " #mb \
  50. : "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \
  51. : __stringify(constraint) "r" (i) \
  52. : cl); \
  53. \
  54. return result; \
  55. }
  56. #define ATOMIC_FETCH_OP(name, mb, acq, rel, cl, op, asm_op, constraint) \
  57. static __always_inline int \
  58. __ll_sc_atomic_fetch_##op##name(int i, atomic_t *v) \
  59. { \
  60. unsigned long tmp; \
  61. int val, result; \
  62. \
  63. asm volatile("// atomic_fetch_" #op #name "\n" \
  64. " prfm pstl1strm, %3\n" \
  65. "1: ld" #acq "xr %w0, %3\n" \
  66. " " #asm_op " %w1, %w0, %w4\n" \
  67. " st" #rel "xr %w2, %w1, %3\n" \
  68. " cbnz %w2, 1b\n" \
  69. " " #mb \
  70. : "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter) \
  71. : __stringify(constraint) "r" (i) \
  72. : cl); \
  73. \
  74. return result; \
  75. }
  76. #define ATOMIC_OPS(...) \
  77. ATOMIC_OP(__VA_ARGS__) \
  78. ATOMIC_OP_RETURN( , dmb ish, , l, "memory", __VA_ARGS__)\
  79. ATOMIC_OP_RETURN(_relaxed, , , , , __VA_ARGS__)\
  80. ATOMIC_OP_RETURN(_acquire, , a, , "memory", __VA_ARGS__)\
  81. ATOMIC_OP_RETURN(_release, , , l, "memory", __VA_ARGS__)\
  82. ATOMIC_FETCH_OP ( , dmb ish, , l, "memory", __VA_ARGS__)\
  83. ATOMIC_FETCH_OP (_relaxed, , , , , __VA_ARGS__)\
  84. ATOMIC_FETCH_OP (_acquire, , a, , "memory", __VA_ARGS__)\
  85. ATOMIC_FETCH_OP (_release, , , l, "memory", __VA_ARGS__)
  86. ATOMIC_OPS(add, add, I)
  87. ATOMIC_OPS(sub, sub, J)
  88. #undef ATOMIC_OPS
  89. #define ATOMIC_OPS(...) \
  90. ATOMIC_OP(__VA_ARGS__) \
  91. ATOMIC_FETCH_OP ( , dmb ish, , l, "memory", __VA_ARGS__)\
  92. ATOMIC_FETCH_OP (_relaxed, , , , , __VA_ARGS__)\
  93. ATOMIC_FETCH_OP (_acquire, , a, , "memory", __VA_ARGS__)\
  94. ATOMIC_FETCH_OP (_release, , , l, "memory", __VA_ARGS__)
  95. ATOMIC_OPS(and, and, K)
  96. ATOMIC_OPS(or, orr, K)
  97. ATOMIC_OPS(xor, eor, K)
  98. /*
  99. * GAS converts the mysterious and undocumented BIC (immediate) alias to
  100. * an AND (immediate) instruction with the immediate inverted. We don't
  101. * have a constraint for this, so fall back to register.
  102. */
  103. ATOMIC_OPS(andnot, bic, )
  104. #undef ATOMIC_OPS
  105. #undef ATOMIC_FETCH_OP
  106. #undef ATOMIC_OP_RETURN
  107. #undef ATOMIC_OP
  108. #define ATOMIC64_OP(op, asm_op, constraint) \
  109. static __always_inline void \
  110. __ll_sc_atomic64_##op(s64 i, atomic64_t *v) \
  111. { \
  112. s64 result; \
  113. unsigned long tmp; \
  114. \
  115. asm volatile("// atomic64_" #op "\n" \
  116. " prfm pstl1strm, %2\n" \
  117. "1: ldxr %0, %2\n" \
  118. " " #asm_op " %0, %0, %3\n" \
  119. " stxr %w1, %0, %2\n" \
  120. " cbnz %w1, 1b" \
  121. : "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \
  122. : __stringify(constraint) "r" (i)); \
  123. }
  124. #define ATOMIC64_OP_RETURN(name, mb, acq, rel, cl, op, asm_op, constraint)\
  125. static __always_inline long \
  126. __ll_sc_atomic64_##op##_return##name(s64 i, atomic64_t *v) \
  127. { \
  128. s64 result; \
  129. unsigned long tmp; \
  130. \
  131. asm volatile("// atomic64_" #op "_return" #name "\n" \
  132. " prfm pstl1strm, %2\n" \
  133. "1: ld" #acq "xr %0, %2\n" \
  134. " " #asm_op " %0, %0, %3\n" \
  135. " st" #rel "xr %w1, %0, %2\n" \
  136. " cbnz %w1, 1b\n" \
  137. " " #mb \
  138. : "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \
  139. : __stringify(constraint) "r" (i) \
  140. : cl); \
  141. \
  142. return result; \
  143. }
  144. #define ATOMIC64_FETCH_OP(name, mb, acq, rel, cl, op, asm_op, constraint)\
  145. static __always_inline long \
  146. __ll_sc_atomic64_fetch_##op##name(s64 i, atomic64_t *v) \
  147. { \
  148. s64 result, val; \
  149. unsigned long tmp; \
  150. \
  151. asm volatile("// atomic64_fetch_" #op #name "\n" \
  152. " prfm pstl1strm, %3\n" \
  153. "1: ld" #acq "xr %0, %3\n" \
  154. " " #asm_op " %1, %0, %4\n" \
  155. " st" #rel "xr %w2, %1, %3\n" \
  156. " cbnz %w2, 1b\n" \
  157. " " #mb \
  158. : "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter) \
  159. : __stringify(constraint) "r" (i) \
  160. : cl); \
  161. \
  162. return result; \
  163. }
  164. #define ATOMIC64_OPS(...) \
  165. ATOMIC64_OP(__VA_ARGS__) \
  166. ATOMIC64_OP_RETURN(, dmb ish, , l, "memory", __VA_ARGS__) \
  167. ATOMIC64_OP_RETURN(_relaxed,, , , , __VA_ARGS__) \
  168. ATOMIC64_OP_RETURN(_acquire,, a, , "memory", __VA_ARGS__) \
  169. ATOMIC64_OP_RETURN(_release,, , l, "memory", __VA_ARGS__) \
  170. ATOMIC64_FETCH_OP (, dmb ish, , l, "memory", __VA_ARGS__) \
  171. ATOMIC64_FETCH_OP (_relaxed,, , , , __VA_ARGS__) \
  172. ATOMIC64_FETCH_OP (_acquire,, a, , "memory", __VA_ARGS__) \
  173. ATOMIC64_FETCH_OP (_release,, , l, "memory", __VA_ARGS__)
  174. ATOMIC64_OPS(add, add, I)
  175. ATOMIC64_OPS(sub, sub, J)
  176. #undef ATOMIC64_OPS
  177. #define ATOMIC64_OPS(...) \
  178. ATOMIC64_OP(__VA_ARGS__) \
  179. ATOMIC64_FETCH_OP (, dmb ish, , l, "memory", __VA_ARGS__) \
  180. ATOMIC64_FETCH_OP (_relaxed,, , , , __VA_ARGS__) \
  181. ATOMIC64_FETCH_OP (_acquire,, a, , "memory", __VA_ARGS__) \
  182. ATOMIC64_FETCH_OP (_release,, , l, "memory", __VA_ARGS__)
  183. ATOMIC64_OPS(and, and, L)
  184. ATOMIC64_OPS(or, orr, L)
  185. ATOMIC64_OPS(xor, eor, L)
  186. /*
  187. * GAS converts the mysterious and undocumented BIC (immediate) alias to
  188. * an AND (immediate) instruction with the immediate inverted. We don't
  189. * have a constraint for this, so fall back to register.
  190. */
  191. ATOMIC64_OPS(andnot, bic, )
  192. #undef ATOMIC64_OPS
  193. #undef ATOMIC64_FETCH_OP
  194. #undef ATOMIC64_OP_RETURN
  195. #undef ATOMIC64_OP
  196. static __always_inline s64
  197. __ll_sc_atomic64_dec_if_positive(atomic64_t *v)
  198. {
  199. s64 result;
  200. unsigned long tmp;
  201. asm volatile("// atomic64_dec_if_positive\n"
  202. " prfm pstl1strm, %2\n"
  203. "1: ldxr %0, %2\n"
  204. " subs %0, %0, #1\n"
  205. " b.lt 2f\n"
  206. " stlxr %w1, %0, %2\n"
  207. " cbnz %w1, 1b\n"
  208. " dmb ish\n"
  209. "2:"
  210. : "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
  211. :
  212. : "cc", "memory");
  213. return result;
  214. }
  215. #define __CMPXCHG_CASE(w, sfx, name, sz, mb, acq, rel, cl, constraint) \
  216. static __always_inline u##sz \
  217. __ll_sc__cmpxchg_case_##name##sz(volatile void *ptr, \
  218. unsigned long old, \
  219. u##sz new) \
  220. { \
  221. unsigned long tmp; \
  222. u##sz oldval; \
  223. \
  224. /* \
  225. * Sub-word sizes require explicit casting so that the compare \
  226. * part of the cmpxchg doesn't end up interpreting non-zero \
  227. * upper bits of the register containing "old". \
  228. */ \
  229. if (sz < 32) \
  230. old = (u##sz)old; \
  231. \
  232. asm volatile( \
  233. " prfm pstl1strm, %[v]\n" \
  234. "1: ld" #acq "xr" #sfx "\t%" #w "[oldval], %[v]\n" \
  235. " eor %" #w "[tmp], %" #w "[oldval], %" #w "[old]\n" \
  236. " cbnz %" #w "[tmp], 2f\n" \
  237. " st" #rel "xr" #sfx "\t%w[tmp], %" #w "[new], %[v]\n" \
  238. " cbnz %w[tmp], 1b\n" \
  239. " " #mb "\n" \
  240. "2:" \
  241. : [tmp] "=&r" (tmp), [oldval] "=&r" (oldval), \
  242. [v] "+Q" (*(u##sz *)ptr) \
  243. : [old] __stringify(constraint) "r" (old), [new] "r" (new) \
  244. : cl); \
  245. \
  246. return oldval; \
  247. }
  248. /*
  249. * Earlier versions of GCC (no later than 8.1.0) appear to incorrectly
  250. * handle the 'K' constraint for the value 4294967295 - thus we use no
  251. * constraint for 32 bit operations.
  252. */
  253. __CMPXCHG_CASE(w, b, , 8, , , , , K)
  254. __CMPXCHG_CASE(w, h, , 16, , , , , K)
  255. __CMPXCHG_CASE(w, , , 32, , , , , K)
  256. __CMPXCHG_CASE( , , , 64, , , , , L)
  257. __CMPXCHG_CASE(w, b, acq_, 8, , a, , "memory", K)
  258. __CMPXCHG_CASE(w, h, acq_, 16, , a, , "memory", K)
  259. __CMPXCHG_CASE(w, , acq_, 32, , a, , "memory", K)
  260. __CMPXCHG_CASE( , , acq_, 64, , a, , "memory", L)
  261. __CMPXCHG_CASE(w, b, rel_, 8, , , l, "memory", K)
  262. __CMPXCHG_CASE(w, h, rel_, 16, , , l, "memory", K)
  263. __CMPXCHG_CASE(w, , rel_, 32, , , l, "memory", K)
  264. __CMPXCHG_CASE( , , rel_, 64, , , l, "memory", L)
  265. __CMPXCHG_CASE(w, b, mb_, 8, dmb ish, , l, "memory", K)
  266. __CMPXCHG_CASE(w, h, mb_, 16, dmb ish, , l, "memory", K)
  267. __CMPXCHG_CASE(w, , mb_, 32, dmb ish, , l, "memory", K)
  268. __CMPXCHG_CASE( , , mb_, 64, dmb ish, , l, "memory", L)
  269. #undef __CMPXCHG_CASE
  270. union __u128_halves {
  271. u128 full;
  272. struct {
  273. u64 low, high;
  274. };
  275. };
  276. #define __CMPXCHG128(name, mb, rel, cl...) \
  277. static __always_inline u128 \
  278. __ll_sc__cmpxchg128##name(volatile u128 *ptr, u128 old, u128 new) \
  279. { \
  280. union __u128_halves r, o = { .full = (old) }, \
  281. n = { .full = (new) }; \
  282. unsigned int tmp; \
  283. \
  284. asm volatile("// __cmpxchg128" #name "\n" \
  285. " prfm pstl1strm, %[v]\n" \
  286. "1: ldxp %[rl], %[rh], %[v]\n" \
  287. " cmp %[rl], %[ol]\n" \
  288. " ccmp %[rh], %[oh], 0, eq\n" \
  289. " b.ne 2f\n" \
  290. " st" #rel "xp %w[tmp], %[nl], %[nh], %[v]\n" \
  291. " cbnz %w[tmp], 1b\n" \
  292. " " #mb "\n" \
  293. "2:" \
  294. : [v] "+Q" (*(u128 *)ptr), \
  295. [rl] "=&r" (r.low), [rh] "=&r" (r.high), \
  296. [tmp] "=&r" (tmp) \
  297. : [ol] "r" (o.low), [oh] "r" (o.high), \
  298. [nl] "r" (n.low), [nh] "r" (n.high) \
  299. : "cc", ##cl); \
  300. \
  301. return r.full; \
  302. }
  303. __CMPXCHG128( , , )
  304. __CMPXCHG128(_mb, dmb ish, l, "memory")
  305. #undef __CMPXCHG128
  306. #undef K
  307. #endif /* __ASM_ATOMIC_LL_SC_H */