atomic.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /* Atomic operations. m68k version.
  2. Copyright (C) 2026 Ramin Moussavi <ramin.moussavi@yacoub.de>
  3. Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
  4. m68k (68020 and up, which is everything the MMU Linux port runs on) has
  5. the CAS instruction. We implement the compare-and-exchange primitive
  6. with cas.b/cas.w/cas.l; include/atomic.h derives every higher-level
  7. operation from it.
  8. ColdFire has no CAS at all, so there we use the kernel's
  9. atomic_cmpxchg_32 helper, which exists for exactly this reason.
  10. Without this file m68k fell back to the generic non-atomic
  11. bits/atomic.h, whose "atomic" compare-and-exchange is a plain
  12. read-compare-write. Under sustained contention a preemption between
  13. the read and the write corrupts NPTL's lock and condition-variable
  14. state, which showed up as lost wakeups (tst-cond16 hung until the test
  15. timeout). */
  16. #ifndef _M68K_BITS_ATOMIC_H
  17. #define _M68K_BITS_ATOMIC_H 1
  18. #include <stdint.h>
  19. #ifdef __mcoldfire__
  20. # include <sys/syscall.h>
  21. #endif
  22. typedef int8_t atomic8_t;
  23. typedef uint8_t uatomic8_t;
  24. typedef int_fast8_t atomic_fast8_t;
  25. typedef uint_fast8_t uatomic_fast8_t;
  26. typedef int16_t atomic16_t;
  27. typedef uint16_t uatomic16_t;
  28. typedef int_fast16_t atomic_fast16_t;
  29. typedef uint_fast16_t uatomic_fast16_t;
  30. typedef int32_t atomic32_t;
  31. typedef uint32_t uatomic32_t;
  32. typedef int_fast32_t atomic_fast32_t;
  33. typedef uint_fast32_t uatomic_fast32_t;
  34. typedef int64_t atomic64_t;
  35. typedef uint64_t uatomic64_t;
  36. typedef int_fast64_t atomic_fast64_t;
  37. typedef uint_fast64_t uatomic_fast64_t;
  38. typedef intptr_t atomicptr_t;
  39. typedef uintptr_t uatomicptr_t;
  40. typedef intmax_t atomic_max_t;
  41. typedef uintmax_t uatomic_max_t;
  42. /* UP m68k; the compare-and-exchange below is itself the read-modify-write
  43. atomic, so a plain compiler barrier around it is enough. (The kernel's
  44. sys_atomic_barrier is a no-op on uniprocessors.) */
  45. #define atomic_full_barrier() __asm__ __volatile__ ("" ::: "memory")
  46. #define atomic_read_barrier() atomic_full_barrier ()
  47. #define atomic_write_barrier() atomic_full_barrier ()
  48. #ifndef __mcoldfire__
  49. /* cas Dc,Du,<ea>: compare <ea> with Dc; if equal store Du, else load <ea>
  50. into Dc. Either way Dc ends up holding the original *MEM, which is the
  51. value compare-and-exchange must return. */
  52. #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
  53. ({ __typeof (*(mem)) __ret = (oldval); \
  54. __asm__ __volatile__ ("cas%.b %0,%2,%1" \
  55. : "=d" (__ret), "+m" (*(mem)) \
  56. : "d" (newval), "0" (__ret) \
  57. : "memory"); \
  58. __ret; })
  59. #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
  60. ({ __typeof (*(mem)) __ret = (oldval); \
  61. __asm__ __volatile__ ("cas%.w %0,%2,%1" \
  62. : "=d" (__ret), "+m" (*(mem)) \
  63. : "d" (newval), "0" (__ret) \
  64. : "memory"); \
  65. __ret; })
  66. #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
  67. ({ __typeof (*(mem)) __ret = (oldval); \
  68. __asm__ __volatile__ ("cas%.l %0,%2,%1" \
  69. : "=d" (__ret), "+m" (*(mem)) \
  70. : "d" (newval), "0" (__ret) \
  71. : "memory"); \
  72. __ret; })
  73. #else /* __mcoldfire__ */
  74. /* d0 = syscall number, d1 = newval, d2 = oldval, a0 = mem; the helper
  75. returns the previous value. The swap happens inside the syscall, so
  76. nothing in userspace can interpose between the load and the store. */
  77. #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
  78. ({ register uint32_t __d0 __asm__ ("d0") = __NR_atomic_cmpxchg_32; \
  79. register uint32_t __d1 __asm__ ("d1") = (uint32_t) (newval); \
  80. register uint32_t __d2 __asm__ ("d2") = (uint32_t) (oldval); \
  81. register uint32_t *__a0 __asm__ ("a0") = (uint32_t *) (mem); \
  82. __asm__ __volatile__ ("trap #0" \
  83. : "+d" (__d0), "+m" (*__a0) \
  84. : "a" (__a0), "d" (__d2), "d" (__d1) \
  85. : "memory"); \
  86. (__typeof (*(mem))) __d0; })
  87. /* The kernel helper is 32-bit only. uClibc's atomic users are int- and
  88. pointer-sized, so these stay non-atomic rather than have a 32-bit access
  89. clobber the neighbouring bytes. */
  90. #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
  91. ({ __typeof (mem) __gmem = (mem); \
  92. __typeof (*(mem)) __gret = *__gmem; \
  93. if (__gret == (oldval)) \
  94. *__gmem = (newval); \
  95. __gret; })
  96. #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
  97. __arch_compare_and_exchange_val_8_acq (mem, newval, oldval)
  98. #endif /* __mcoldfire__ */
  99. /* m68k has no 64-bit CAS; NPTL and include/atomic.h only use int- and
  100. pointer-sized objects, so the bysize dispatch never reaches this. */
  101. #define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
  102. ({ __typeof (*(mem)) __ret = *(mem); abort (); (void) (newval); \
  103. (void) (oldval); __ret; })
  104. #endif /* _M68K_BITS_ATOMIC_H */