atomic.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*
  2. * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. */
  8. #ifndef _ASM_ARC_ATOMIC_H
  9. #define _ASM_ARC_ATOMIC_H
  10. #ifndef __ASSEMBLY__
  11. #include <linux/types.h>
  12. #include <linux/compiler.h>
  13. #include <asm/cmpxchg.h>
  14. #include <asm/barrier.h>
  15. #include <asm/smp.h>
  16. #define ATOMIC_INIT(i) { (i) }
  17. #ifndef CONFIG_ARC_PLAT_EZNPS
  18. #define atomic_read(v) READ_ONCE((v)->counter)
  19. #ifdef CONFIG_ARC_HAS_LLSC
  20. #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
  21. #define ATOMIC_OP(op, c_op, asm_op) \
  22. static inline void atomic_##op(int i, atomic_t *v) \
  23. { \
  24. unsigned int val; \
  25. \
  26. __asm__ __volatile__( \
  27. "1: llock %[val], [%[ctr]] \n" \
  28. " " #asm_op " %[val], %[val], %[i] \n" \
  29. " scond %[val], [%[ctr]] \n" \
  30. " bnz 1b \n" \
  31. : [val] "=&r" (val) /* Early clobber to prevent reg reuse */ \
  32. : [ctr] "r" (&v->counter), /* Not "m": llock only supports reg direct addr mode */ \
  33. [i] "ir" (i) \
  34. : "cc"); \
  35. } \
  36. #define ATOMIC_OP_RETURN(op, c_op, asm_op) \
  37. static inline int atomic_##op##_return(int i, atomic_t *v) \
  38. { \
  39. unsigned int val; \
  40. \
  41. /* \
  42. * Explicit full memory barrier needed before/after as \
  43. * LLOCK/SCOND thmeselves don't provide any such semantics \
  44. */ \
  45. smp_mb(); \
  46. \
  47. __asm__ __volatile__( \
  48. "1: llock %[val], [%[ctr]] \n" \
  49. " " #asm_op " %[val], %[val], %[i] \n" \
  50. " scond %[val], [%[ctr]] \n" \
  51. " bnz 1b \n" \
  52. : [val] "=&r" (val) \
  53. : [ctr] "r" (&v->counter), \
  54. [i] "ir" (i) \
  55. : "cc"); \
  56. \
  57. smp_mb(); \
  58. \
  59. return val; \
  60. }
  61. #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
  62. static inline int atomic_fetch_##op(int i, atomic_t *v) \
  63. { \
  64. unsigned int val, orig; \
  65. \
  66. /* \
  67. * Explicit full memory barrier needed before/after as \
  68. * LLOCK/SCOND thmeselves don't provide any such semantics \
  69. */ \
  70. smp_mb(); \
  71. \
  72. __asm__ __volatile__( \
  73. "1: llock %[orig], [%[ctr]] \n" \
  74. " " #asm_op " %[val], %[orig], %[i] \n" \
  75. " scond %[val], [%[ctr]] \n" \
  76. " \n" \
  77. : [val] "=&r" (val), \
  78. [orig] "=&r" (orig) \
  79. : [ctr] "r" (&v->counter), \
  80. [i] "ir" (i) \
  81. : "cc"); \
  82. \
  83. smp_mb(); \
  84. \
  85. return orig; \
  86. }
  87. #else /* !CONFIG_ARC_HAS_LLSC */
  88. #ifndef CONFIG_SMP
  89. /* violating atomic_xxx API locking protocol in UP for optimization sake */
  90. #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
  91. #else
  92. static inline void atomic_set(atomic_t *v, int i)
  93. {
  94. /*
  95. * Independent of hardware support, all of the atomic_xxx() APIs need
  96. * to follow the same locking rules to make sure that a "hardware"
  97. * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
  98. * sequence
  99. *
  100. * Thus atomic_set() despite being 1 insn (and seemingly atomic)
  101. * requires the locking.
  102. */
  103. unsigned long flags;
  104. atomic_ops_lock(flags);
  105. WRITE_ONCE(v->counter, i);
  106. atomic_ops_unlock(flags);
  107. }
  108. #define atomic_set_release(v, i) atomic_set((v), (i))
  109. #endif
  110. /*
  111. * Non hardware assisted Atomic-R-M-W
  112. * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
  113. */
  114. #define ATOMIC_OP(op, c_op, asm_op) \
  115. static inline void atomic_##op(int i, atomic_t *v) \
  116. { \
  117. unsigned long flags; \
  118. \
  119. atomic_ops_lock(flags); \
  120. v->counter c_op i; \
  121. atomic_ops_unlock(flags); \
  122. }
  123. #define ATOMIC_OP_RETURN(op, c_op, asm_op) \
  124. static inline int atomic_##op##_return(int i, atomic_t *v) \
  125. { \
  126. unsigned long flags; \
  127. unsigned long temp; \
  128. \
  129. /* \
  130. * spin lock/unlock provides the needed smp_mb() before/after \
  131. */ \
  132. atomic_ops_lock(flags); \
  133. temp = v->counter; \
  134. temp c_op i; \
  135. v->counter = temp; \
  136. atomic_ops_unlock(flags); \
  137. \
  138. return temp; \
  139. }
  140. #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
  141. static inline int atomic_fetch_##op(int i, atomic_t *v) \
  142. { \
  143. unsigned long flags; \
  144. unsigned long orig; \
  145. \
  146. /* \
  147. * spin lock/unlock provides the needed smp_mb() before/after \
  148. */ \
  149. atomic_ops_lock(flags); \
  150. orig = v->counter; \
  151. v->counter c_op i; \
  152. atomic_ops_unlock(flags); \
  153. \
  154. return orig; \
  155. }
  156. #endif /* !CONFIG_ARC_HAS_LLSC */
  157. #define ATOMIC_OPS(op, c_op, asm_op) \
  158. ATOMIC_OP(op, c_op, asm_op) \
  159. ATOMIC_OP_RETURN(op, c_op, asm_op) \
  160. ATOMIC_FETCH_OP(op, c_op, asm_op)
  161. ATOMIC_OPS(add, +=, add)
  162. ATOMIC_OPS(sub, -=, sub)
  163. #define atomic_andnot atomic_andnot
  164. #undef ATOMIC_OPS
  165. #define ATOMIC_OPS(op, c_op, asm_op) \
  166. ATOMIC_OP(op, c_op, asm_op) \
  167. ATOMIC_FETCH_OP(op, c_op, asm_op)
  168. ATOMIC_OPS(and, &=, and)
  169. ATOMIC_OPS(andnot, &= ~, bic)
  170. ATOMIC_OPS(or, |=, or)
  171. ATOMIC_OPS(xor, ^=, xor)
  172. #else /* CONFIG_ARC_PLAT_EZNPS */
  173. static inline int atomic_read(const atomic_t *v)
  174. {
  175. int temp;
  176. __asm__ __volatile__(
  177. " ld.di %0, [%1]"
  178. : "=r"(temp)
  179. : "r"(&v->counter)
  180. : "memory");
  181. return temp;
  182. }
  183. static inline void atomic_set(atomic_t *v, int i)
  184. {
  185. __asm__ __volatile__(
  186. " st.di %0,[%1]"
  187. :
  188. : "r"(i), "r"(&v->counter)
  189. : "memory");
  190. }
  191. #define ATOMIC_OP(op, c_op, asm_op) \
  192. static inline void atomic_##op(int i, atomic_t *v) \
  193. { \
  194. __asm__ __volatile__( \
  195. " mov r2, %0\n" \
  196. " mov r3, %1\n" \
  197. " .word %2\n" \
  198. : \
  199. : "r"(i), "r"(&v->counter), "i"(asm_op) \
  200. : "r2", "r3", "memory"); \
  201. } \
  202. #define ATOMIC_OP_RETURN(op, c_op, asm_op) \
  203. static inline int atomic_##op##_return(int i, atomic_t *v) \
  204. { \
  205. unsigned int temp = i; \
  206. \
  207. /* Explicit full memory barrier needed before/after */ \
  208. smp_mb(); \
  209. \
  210. __asm__ __volatile__( \
  211. " mov r2, %0\n" \
  212. " mov r3, %1\n" \
  213. " .word %2\n" \
  214. " mov %0, r2" \
  215. : "+r"(temp) \
  216. : "r"(&v->counter), "i"(asm_op) \
  217. : "r2", "r3", "memory"); \
  218. \
  219. smp_mb(); \
  220. \
  221. temp c_op i; \
  222. \
  223. return temp; \
  224. }
  225. #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
  226. static inline int atomic_fetch_##op(int i, atomic_t *v) \
  227. { \
  228. unsigned int temp = i; \
  229. \
  230. /* Explicit full memory barrier needed before/after */ \
  231. smp_mb(); \
  232. \
  233. __asm__ __volatile__( \
  234. " mov r2, %0\n" \
  235. " mov r3, %1\n" \
  236. " .word %2\n" \
  237. " mov %0, r2" \
  238. : "+r"(temp) \
  239. : "r"(&v->counter), "i"(asm_op) \
  240. : "r2", "r3", "memory"); \
  241. \
  242. smp_mb(); \
  243. \
  244. return temp; \
  245. }
  246. #define ATOMIC_OPS(op, c_op, asm_op) \
  247. ATOMIC_OP(op, c_op, asm_op) \
  248. ATOMIC_OP_RETURN(op, c_op, asm_op) \
  249. ATOMIC_FETCH_OP(op, c_op, asm_op)
  250. ATOMIC_OPS(add, +=, CTOP_INST_AADD_DI_R2_R2_R3)
  251. #define atomic_sub(i, v) atomic_add(-(i), (v))
  252. #define atomic_sub_return(i, v) atomic_add_return(-(i), (v))
  253. #define atomic_fetch_sub(i, v) atomic_fetch_add(-(i), (v))
  254. #undef ATOMIC_OPS
  255. #define ATOMIC_OPS(op, c_op, asm_op) \
  256. ATOMIC_OP(op, c_op, asm_op) \
  257. ATOMIC_FETCH_OP(op, c_op, asm_op)
  258. ATOMIC_OPS(and, &=, CTOP_INST_AAND_DI_R2_R2_R3)
  259. #define atomic_andnot(mask, v) atomic_and(~(mask), (v))
  260. #define atomic_fetch_andnot(mask, v) atomic_fetch_and(~(mask), (v))
  261. ATOMIC_OPS(or, |=, CTOP_INST_AOR_DI_R2_R2_R3)
  262. ATOMIC_OPS(xor, ^=, CTOP_INST_AXOR_DI_R2_R2_R3)
  263. #endif /* CONFIG_ARC_PLAT_EZNPS */
  264. #undef ATOMIC_OPS
  265. #undef ATOMIC_FETCH_OP
  266. #undef ATOMIC_OP_RETURN
  267. #undef ATOMIC_OP
  268. /**
  269. * __atomic_add_unless - add unless the number is a given value
  270. * @v: pointer of type atomic_t
  271. * @a: the amount to add to v...
  272. * @u: ...unless v is equal to u.
  273. *
  274. * Atomically adds @a to @v, so long as it was not @u.
  275. * Returns the old value of @v
  276. */
  277. #define __atomic_add_unless(v, a, u) \
  278. ({ \
  279. int c, old; \
  280. \
  281. /* \
  282. * Explicit full memory barrier needed before/after as \
  283. * LLOCK/SCOND thmeselves don't provide any such semantics \
  284. */ \
  285. smp_mb(); \
  286. \
  287. c = atomic_read(v); \
  288. while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c)\
  289. c = old; \
  290. \
  291. smp_mb(); \
  292. \
  293. c; \
  294. })
  295. #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
  296. #define atomic_inc(v) atomic_add(1, v)
  297. #define atomic_dec(v) atomic_sub(1, v)
  298. #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
  299. #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
  300. #define atomic_inc_return(v) atomic_add_return(1, (v))
  301. #define atomic_dec_return(v) atomic_sub_return(1, (v))
  302. #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
  303. #define atomic_add_negative(i, v) (atomic_add_return(i, v) < 0)
  304. #ifdef CONFIG_GENERIC_ATOMIC64
  305. #include <asm-generic/atomic64.h>
  306. #else /* Kconfig ensures this is only enabled with needed h/w assist */
  307. /*
  308. * ARCv2 supports 64-bit exclusive load (LLOCKD) / store (SCONDD)
  309. * - The address HAS to be 64-bit aligned
  310. * - There are 2 semantics involved here:
  311. * = exclusive implies no interim update between load/store to same addr
  312. * = both words are observed/updated together: this is guaranteed even
  313. * for regular 64-bit load (LDD) / store (STD). Thus atomic64_set()
  314. * is NOT required to use LLOCKD+SCONDD, STD suffices
  315. */
  316. typedef struct {
  317. aligned_u64 counter;
  318. } atomic64_t;
  319. #define ATOMIC64_INIT(a) { (a) }
  320. static inline long long atomic64_read(const atomic64_t *v)
  321. {
  322. unsigned long long val;
  323. __asm__ __volatile__(
  324. " ldd %0, [%1] \n"
  325. : "=r"(val)
  326. : "r"(&v->counter));
  327. return val;
  328. }
  329. static inline void atomic64_set(atomic64_t *v, long long a)
  330. {
  331. /*
  332. * This could have been a simple assignment in "C" but would need
  333. * explicit volatile. Otherwise gcc optimizers could elide the store
  334. * which borked atomic64 self-test
  335. * In the inline asm version, memory clobber needed for exact same
  336. * reason, to tell gcc about the store.
  337. *
  338. * This however is not needed for sibling atomic64_add() etc since both
  339. * load/store are explicitly done in inline asm. As long as API is used
  340. * for each access, gcc has no way to optimize away any load/store
  341. */
  342. __asm__ __volatile__(
  343. " std %0, [%1] \n"
  344. :
  345. : "r"(a), "r"(&v->counter)
  346. : "memory");
  347. }
  348. #define ATOMIC64_OP(op, op1, op2) \
  349. static inline void atomic64_##op(long long a, atomic64_t *v) \
  350. { \
  351. unsigned long long val; \
  352. \
  353. __asm__ __volatile__( \
  354. "1: \n" \
  355. " llockd %0, [%1] \n" \
  356. " " #op1 " %L0, %L0, %L2 \n" \
  357. " " #op2 " %H0, %H0, %H2 \n" \
  358. " scondd %0, [%1] \n" \
  359. " bnz 1b \n" \
  360. : "=&r"(val) \
  361. : "r"(&v->counter), "ir"(a) \
  362. : "cc"); \
  363. } \
  364. #define ATOMIC64_OP_RETURN(op, op1, op2) \
  365. static inline long long atomic64_##op##_return(long long a, atomic64_t *v) \
  366. { \
  367. unsigned long long val; \
  368. \
  369. smp_mb(); \
  370. \
  371. __asm__ __volatile__( \
  372. "1: \n" \
  373. " llockd %0, [%1] \n" \
  374. " " #op1 " %L0, %L0, %L2 \n" \
  375. " " #op2 " %H0, %H0, %H2 \n" \
  376. " scondd %0, [%1] \n" \
  377. " bnz 1b \n" \
  378. : [val] "=&r"(val) \
  379. : "r"(&v->counter), "ir"(a) \
  380. : "cc"); /* memory clobber comes from smp_mb() */ \
  381. \
  382. smp_mb(); \
  383. \
  384. return val; \
  385. }
  386. #define ATOMIC64_FETCH_OP(op, op1, op2) \
  387. static inline long long atomic64_fetch_##op(long long a, atomic64_t *v) \
  388. { \
  389. unsigned long long val, orig; \
  390. \
  391. smp_mb(); \
  392. \
  393. __asm__ __volatile__( \
  394. "1: \n" \
  395. " llockd %0, [%2] \n" \
  396. " " #op1 " %L1, %L0, %L3 \n" \
  397. " " #op2 " %H1, %H0, %H3 \n" \
  398. " scondd %1, [%2] \n" \
  399. " bnz 1b \n" \
  400. : "=&r"(orig), "=&r"(val) \
  401. : "r"(&v->counter), "ir"(a) \
  402. : "cc"); /* memory clobber comes from smp_mb() */ \
  403. \
  404. smp_mb(); \
  405. \
  406. return orig; \
  407. }
  408. #define ATOMIC64_OPS(op, op1, op2) \
  409. ATOMIC64_OP(op, op1, op2) \
  410. ATOMIC64_OP_RETURN(op, op1, op2) \
  411. ATOMIC64_FETCH_OP(op, op1, op2)
  412. #define atomic64_andnot atomic64_andnot
  413. ATOMIC64_OPS(add, add.f, adc)
  414. ATOMIC64_OPS(sub, sub.f, sbc)
  415. ATOMIC64_OPS(and, and, and)
  416. ATOMIC64_OPS(andnot, bic, bic)
  417. ATOMIC64_OPS(or, or, or)
  418. ATOMIC64_OPS(xor, xor, xor)
  419. #undef ATOMIC64_OPS
  420. #undef ATOMIC64_FETCH_OP
  421. #undef ATOMIC64_OP_RETURN
  422. #undef ATOMIC64_OP
  423. static inline long long
  424. atomic64_cmpxchg(atomic64_t *ptr, long long expected, long long new)
  425. {
  426. long long prev;
  427. smp_mb();
  428. __asm__ __volatile__(
  429. "1: llockd %0, [%1] \n"
  430. " brne %L0, %L2, 2f \n"
  431. " brne %H0, %H2, 2f \n"
  432. " scondd %3, [%1] \n"
  433. " bnz 1b \n"
  434. "2: \n"
  435. : "=&r"(prev)
  436. : "r"(ptr), "ir"(expected), "r"(new)
  437. : "cc"); /* memory clobber comes from smp_mb() */
  438. smp_mb();
  439. return prev;
  440. }
  441. static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
  442. {
  443. long long prev;
  444. smp_mb();
  445. __asm__ __volatile__(
  446. "1: llockd %0, [%1] \n"
  447. " scondd %2, [%1] \n"
  448. " bnz 1b \n"
  449. "2: \n"
  450. : "=&r"(prev)
  451. : "r"(ptr), "r"(new)
  452. : "cc"); /* memory clobber comes from smp_mb() */
  453. smp_mb();
  454. return prev;
  455. }
  456. /**
  457. * atomic64_dec_if_positive - decrement by 1 if old value positive
  458. * @v: pointer of type atomic64_t
  459. *
  460. * The function returns the old value of *v minus 1, even if
  461. * the atomic variable, v, was not decremented.
  462. */
  463. static inline long long atomic64_dec_if_positive(atomic64_t *v)
  464. {
  465. long long val;
  466. smp_mb();
  467. __asm__ __volatile__(
  468. "1: llockd %0, [%1] \n"
  469. " sub.f %L0, %L0, 1 # w0 - 1, set C on borrow\n"
  470. " sub.c %H0, %H0, 1 # if C set, w1 - 1\n"
  471. " brlt %H0, 0, 2f \n"
  472. " scondd %0, [%1] \n"
  473. " bnz 1b \n"
  474. "2: \n"
  475. : "=&r"(val)
  476. : "r"(&v->counter)
  477. : "cc"); /* memory clobber comes from smp_mb() */
  478. smp_mb();
  479. return val;
  480. }
  481. /**
  482. * atomic64_add_unless - add unless the number is a given value
  483. * @v: pointer of type atomic64_t
  484. * @a: the amount to add to v...
  485. * @u: ...unless v is equal to u.
  486. *
  487. * if (v != u) { v += a; ret = 1} else {ret = 0}
  488. * Returns 1 iff @v was not @u (i.e. if add actually happened)
  489. */
  490. static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
  491. {
  492. long long val;
  493. int op_done;
  494. smp_mb();
  495. __asm__ __volatile__(
  496. "1: llockd %0, [%2] \n"
  497. " mov %1, 1 \n"
  498. " brne %L0, %L4, 2f # continue to add since v != u \n"
  499. " breq.d %H0, %H4, 3f # return since v == u \n"
  500. " mov %1, 0 \n"
  501. "2: \n"
  502. " add.f %L0, %L0, %L3 \n"
  503. " adc %H0, %H0, %H3 \n"
  504. " scondd %0, [%2] \n"
  505. " bnz 1b \n"
  506. "3: \n"
  507. : "=&r"(val), "=&r" (op_done)
  508. : "r"(&v->counter), "r"(a), "r"(u)
  509. : "cc"); /* memory clobber comes from smp_mb() */
  510. smp_mb();
  511. return op_done;
  512. }
  513. #define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0)
  514. #define atomic64_inc(v) atomic64_add(1LL, (v))
  515. #define atomic64_inc_return(v) atomic64_add_return(1LL, (v))
  516. #define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
  517. #define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0)
  518. #define atomic64_dec(v) atomic64_sub(1LL, (v))
  519. #define atomic64_dec_return(v) atomic64_sub_return(1LL, (v))
  520. #define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0)
  521. #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL)
  522. #endif /* !CONFIG_GENERIC_ATOMIC64 */
  523. #endif /* !__ASSEMBLY__ */
  524. #endif