atomic.h 15 KB

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