|
|
@@ -0,0 +1,89 @@
|
|
|
+/* Atomic operations. MicroBlaze version.
|
|
|
+ Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
|
|
|
+
|
|
|
+ MicroBlaze (ISA v8+) has 32-bit load/store-exclusive instructions
|
|
|
+ (lwx/swx). We implement the compare-and-exchange primitive directly
|
|
|
+ with them rather than via the __sync_* builtins: gcc's MicroBlaze
|
|
|
+ __sync code generation is broken (it emits the lwx/swx pair without
|
|
|
+ the compare-branch and without retrying on a failed swx), so the
|
|
|
+ builtin compare-and-exchange neither swaps nor returns the right
|
|
|
+ value. A hand-written lwx/swx loop works correctly.
|
|
|
+
|
|
|
+ Without this file MicroBlaze fell back to the generic non-atomic
|
|
|
+ bits/atomic.h, whose "atomic" compare-and-exchange is a plain
|
|
|
+ read-compare-write. Under sustained contention a preemption between
|
|
|
+ the read and the write corrupts NPTL's lock and condition-variable
|
|
|
+ state, which showed up as lost wakeups (tst-cond16/tst-cond17 hung
|
|
|
+ until the test timeout).
|
|
|
+
|
|
|
+ Only 32-bit objects are supported (lwx/swx are word operations); the
|
|
|
+ NPTL and include/atomic.h users only ever operate on int- and
|
|
|
+ pointer-sized objects, and the higher-level operations are derived
|
|
|
+ from the compare-and-exchange primitive below. */
|
|
|
+
|
|
|
+#ifndef _MICROBLAZE_BITS_ATOMIC_H
|
|
|
+#define _MICROBLAZE_BITS_ATOMIC_H 1
|
|
|
+
|
|
|
+#include <stdint.h>
|
|
|
+
|
|
|
+typedef int8_t atomic8_t;
|
|
|
+typedef uint8_t uatomic8_t;
|
|
|
+typedef int_fast8_t atomic_fast8_t;
|
|
|
+typedef uint_fast8_t uatomic_fast8_t;
|
|
|
+
|
|
|
+typedef int16_t atomic16_t;
|
|
|
+typedef uint16_t uatomic16_t;
|
|
|
+typedef int_fast16_t atomic_fast16_t;
|
|
|
+typedef uint_fast16_t uatomic_fast16_t;
|
|
|
+
|
|
|
+typedef int32_t atomic32_t;
|
|
|
+typedef uint32_t uatomic32_t;
|
|
|
+typedef int_fast32_t atomic_fast32_t;
|
|
|
+typedef uint_fast32_t uatomic_fast32_t;
|
|
|
+
|
|
|
+typedef int64_t atomic64_t;
|
|
|
+typedef uint64_t uatomic64_t;
|
|
|
+typedef int_fast64_t atomic_fast64_t;
|
|
|
+typedef uint_fast64_t uatomic_fast64_t;
|
|
|
+
|
|
|
+typedef intptr_t atomicptr_t;
|
|
|
+typedef uintptr_t uatomicptr_t;
|
|
|
+typedef intmax_t atomic_max_t;
|
|
|
+typedef uintmax_t uatomic_max_t;
|
|
|
+
|
|
|
+/* UP MicroBlaze; lwx/swx provide the ordering for the atomic itself, a
|
|
|
+ plain compiler barrier is enough around it. */
|
|
|
+#define atomic_full_barrier() __asm__ __volatile__ ("" ::: "memory")
|
|
|
+#define atomic_read_barrier() atomic_full_barrier ()
|
|
|
+#define atomic_write_barrier() atomic_full_barrier ()
|
|
|
+
|
|
|
+/* Store NEWVAL in *MEM if *MEM equals OLDVAL; return the old *MEM value.
|
|
|
+ lwx loads exclusive (arming the reservation), swx stores exclusive and
|
|
|
+ sets MSR[C]=1 if the reservation was lost, in which case we retry. */
|
|
|
+#define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \
|
|
|
+ ({ __typeof (*(mem)) __acev_ret; \
|
|
|
+ __typeof (*(mem)) __acev_nv = (newval); \
|
|
|
+ __typeof (*(mem)) __acev_ov = (oldval); \
|
|
|
+ int __acev_fail; \
|
|
|
+ __asm__ __volatile__ ( \
|
|
|
+ "1: lwx %0, %2, r0\n\t" \
|
|
|
+ " cmpu %1, %0, %3\n\t" \
|
|
|
+ " bnei %1, 2f\n\t" \
|
|
|
+ " swx %4, %2, r0\n\t" \
|
|
|
+ " addic %1, r0, 0\n\t" \
|
|
|
+ " bnei %1, 1b\n\t" \
|
|
|
+ "2:" \
|
|
|
+ : "=&r" (__acev_ret), "=&r" (__acev_fail) \
|
|
|
+ : "r" (mem), "r" (__acev_ov), "r" (__acev_nv) \
|
|
|
+ : "cc", "memory"); \
|
|
|
+ __acev_ret; })
|
|
|
+
|
|
|
+#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \
|
|
|
+ ({ __typeof (*(mem)) __aceb_ov = (oldval); \
|
|
|
+ atomic_compare_and_exchange_val_acq ((mem), (newval), __aceb_ov) \
|
|
|
+ != __aceb_ov; })
|
|
|
+
|
|
|
+/* atomic_exchange_acq and the higher-level operations are derived from
|
|
|
+ the compare-and-exchange primitive by include/atomic.h. */
|
|
|
+
|
|
|
+#endif /* _MICROBLAZE_BITS_ATOMIC_H */
|