atomic.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. The CAS instruction arrived with the 68020. Where it exists we implement
  5. the compare-and-exchange primitive with cas.b/cas.w/cas.l and let
  6. include/atomic.h derive every higher-level operation from it.
  7. ColdFire, 68000 and 68010 have no atomic read-modify-write instruction at
  8. all. There we use the kernel's atomic_cmpxchg_32 helper, which exists for
  9. exactly this reason and is available on every m68k (syscall 335, "common"
  10. in arch/m68k/kernel/syscalls/syscall.tbl, with an implementation for both
  11. the MMU and the noMMU port).
  12. Without this file m68k fell back to the generic non-atomic
  13. bits/atomic.h, whose "atomic" compare-and-exchange is a plain
  14. read-compare-write. Under sustained contention a preemption between
  15. the read and the write corrupts NPTL's lock and condition-variable
  16. state, which showed up as lost wakeups (tst-cond16 hung until the test
  17. timeout). */
  18. #ifndef _M68K_BITS_ATOMIC_H
  19. #define _M68K_BITS_ATOMIC_H 1
  20. #include <stdint.h>
  21. /* CAS came with the 68020. gcc sets a separate macro per core and does not
  22. imply __mc68020__ on the later ones, hence the list; __mc68000__ is no help
  23. here, it is defined on every m68k, the 68040 and ColdFire included.
  24. Everything without CAS - ColdFire, 68000, 68010 - uses the kernel helper. */
  25. #if defined __mc68020__ || defined __mc68030__ \
  26. || defined __mc68040__ || defined __mc68060__
  27. # define __M68K_HAVE_CAS 1
  28. #endif
  29. #ifndef __M68K_HAVE_CAS
  30. # include <sys/syscall.h>
  31. #endif
  32. typedef int8_t atomic8_t;
  33. typedef uint8_t uatomic8_t;
  34. typedef int_fast8_t atomic_fast8_t;
  35. typedef uint_fast8_t uatomic_fast8_t;
  36. typedef int16_t atomic16_t;
  37. typedef uint16_t uatomic16_t;
  38. typedef int_fast16_t atomic_fast16_t;
  39. typedef uint_fast16_t uatomic_fast16_t;
  40. typedef int32_t atomic32_t;
  41. typedef uint32_t uatomic32_t;
  42. typedef int_fast32_t atomic_fast32_t;
  43. typedef uint_fast32_t uatomic_fast32_t;
  44. typedef int64_t atomic64_t;
  45. typedef uint64_t uatomic64_t;
  46. typedef int_fast64_t atomic_fast64_t;
  47. typedef uint_fast64_t uatomic_fast64_t;
  48. typedef intptr_t atomicptr_t;
  49. typedef uintptr_t uatomicptr_t;
  50. typedef intmax_t atomic_max_t;
  51. typedef uintmax_t uatomic_max_t;
  52. /* UP m68k; the compare-and-exchange below is itself the read-modify-write
  53. atomic, so a plain compiler barrier around it is enough. (The kernel's
  54. sys_atomic_barrier is a no-op on uniprocessors.) */
  55. #define atomic_full_barrier() __asm__ __volatile__ ("" ::: "memory")
  56. #define atomic_read_barrier() atomic_full_barrier ()
  57. #define atomic_write_barrier() atomic_full_barrier ()
  58. #ifdef __M68K_HAVE_CAS
  59. /* cas Dc,Du,<ea>: compare <ea> with Dc; if equal store Du, else load <ea>
  60. into Dc. Either way Dc ends up holding the original *MEM, which is the
  61. value compare-and-exchange must return. */
  62. #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
  63. ({ __typeof (*(mem)) __ret = (oldval); \
  64. __asm__ __volatile__ ("cas%.b %0,%2,%1" \
  65. : "=d" (__ret), "+m" (*(mem)) \
  66. : "d" (newval), "0" (__ret) \
  67. : "memory"); \
  68. __ret; })
  69. #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
  70. ({ __typeof (*(mem)) __ret = (oldval); \
  71. __asm__ __volatile__ ("cas%.w %0,%2,%1" \
  72. : "=d" (__ret), "+m" (*(mem)) \
  73. : "d" (newval), "0" (__ret) \
  74. : "memory"); \
  75. __ret; })
  76. #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
  77. ({ __typeof (*(mem)) __ret = (oldval); \
  78. __asm__ __volatile__ ("cas%.l %0,%2,%1" \
  79. : "=d" (__ret), "+m" (*(mem)) \
  80. : "d" (newval), "0" (__ret) \
  81. : "memory"); \
  82. __ret; })
  83. #else /* !__M68K_HAVE_CAS */
  84. /* d0 = syscall number, d1 = newval, d2 = oldval, a0 = mem; the helper
  85. returns the previous value. The swap happens inside the syscall, so
  86. nothing in userspace can interpose between the load and the store. */
  87. #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
  88. ({ register uint32_t __d0 __asm__ ("d0") = __NR_atomic_cmpxchg_32; \
  89. register uint32_t __d1 __asm__ ("d1") = (uint32_t) (newval); \
  90. register uint32_t __d2 __asm__ ("d2") = (uint32_t) (oldval); \
  91. register uint32_t *__a0 __asm__ ("a0") = (uint32_t *) (mem); \
  92. __asm__ __volatile__ ("trap #0" \
  93. : "+d" (__d0), "+m" (*__a0) \
  94. : "a" (__a0), "d" (__d2), "d" (__d1) \
  95. : "memory"); \
  96. (__typeof (*(mem))) __d0; })
  97. /* The kernel helper is 32-bit only. uClibc's atomic users are int- and
  98. pointer-sized, so these stay non-atomic rather than have a 32-bit access
  99. clobber the neighbouring bytes. */
  100. #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
  101. ({ __typeof (mem) __gmem = (mem); \
  102. __typeof (*(mem)) __gret = *__gmem; \
  103. if (__gret == (oldval)) \
  104. *__gmem = (newval); \
  105. __gret; })
  106. #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
  107. __arch_compare_and_exchange_val_8_acq (mem, newval, oldval)
  108. #endif /* __M68K_HAVE_CAS */
  109. /* m68k has no 64-bit CAS; NPTL and include/atomic.h only use int- and
  110. pointer-sized objects, so the bysize dispatch never reaches this. */
  111. #define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
  112. ({ __typeof (*(mem)) __ret = *(mem); abort (); (void) (newval); \
  113. (void) (oldval); __ret; })
  114. #endif /* _M68K_BITS_ATOMIC_H */