Kaynağa Gözat

microblaze: implement atomic operations with lwx/swx

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; the most visible
symptom was lost wakeups, with tst-cond16/tst-cond17 hanging until the
test-skeleton timeout (and intermittent tst-eintr1 failures).

ISA v8+ provides 32-bit load/store-exclusive (lwx/swx); implement the
compare-and-exchange primitive directly with them in a hand-written
retry loop, structurally the same as the kernel's own microblaze
futex cmpxchg.  The __sync_* builtins are deliberately NOT used: gcc's
MicroBlaze code generation for them is broken (it emits the lwx/swx
pair without the compare-branch and without retrying a failed swx, so
the builtin neither swaps nor returns the right value).

Only 32-bit objects are supported (lwx/swx are word operations), which
is all the NPTL and include/atomic.h users need; the higher-level
operations are derived from compare-and-exchange.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
ramin 2 gün önce
ebeveyn
işleme
57842a04eb
1 değiştirilmiş dosya ile 89 ekleme ve 0 silme
  1. 89 0
      libc/sysdeps/linux/microblaze/bits/atomic.h

+ 89 - 0
libc/sysdeps/linux/microblaze/bits/atomic.h

@@ -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 */