On 2014-06-30 19:22:59 +0200, Andres Freund wrote:
> On 2014-06-30 12:46:29 -0400, Robert Haas wrote:
> >, which if I understand you correctly are ARM without GCC
> > atomics, Sparc, and MIPS.
> 
> I've to revise my statement on MIPS, it actually looks safe. I seem to
> have missed that it has its own S_UNLOCK.

So, here's my first blind attempt at fixing these. Too tired to think
much more about it. Sparc's configurable cache coherency drives me
nuts. Linux apparently switched somewhere in 2011 from RMO (relaxed
memory order) to TSO (total store order), solaris always used TSO, but
the BSDs don't. Man.

Accordingly there's a somewhat ugly thingy attached. I don't think this
is really ready, but it's what I can come up today.

Greetings,

Andres Freund

-- 
 Andres Freund                     http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services
>From 691839dc8fbb78134b530096d33d8a1307231eef Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Tue, 1 Jul 2014 00:11:17 +0200
Subject: [PATCH] Fix spinlock implementations for some sparc and arm
 platforms.

When compiling postgres on arm using a older gcc, that doesn't yet
understand __sync_lock_test_and_set(), the default S_UNLOCK routine
was used. Unfortunately that's not correct for ARM's memory model. The
support for older gccs is using the swbp instruction (available for
both arvm5 and most armv6) - unfortunately there's not a common
barrier instruction for those architecture versions. So instead
implement unlock using swbp again, that's possibly a bit slower but
correct.

Some Sparc CPUs can be run in various coherence models, ranging from
RMO (relaxed) over PSO (partial) to TSO (total). Solaris has always
run CPUs in TSO mode, but linux didn't use to and the various *BSDs
still don't. Unfortunately the sparc TAS/S_UNLOCK were only correct
under TSO. Fix that by adding the necessary memory barrier
instructions. On sparcv8+, which should be all relavant CPUs, these
are treated as NOPs if the current consistency model doesn't require
the barriers.

Blindly written, so possibly not working.

They didn't use to use correct acquire/
---
 src/backend/port/tas/sunstudio_sparc.s |  2 +
 src/include/storage/s_lock.h           | 74 +++++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/src/backend/port/tas/sunstudio_sparc.s b/src/backend/port/tas/sunstudio_sparc.s
index 486b7be..dcf54e2 100644
--- a/src/backend/port/tas/sunstudio_sparc.s
+++ b/src/backend/port/tas/sunstudio_sparc.s
@@ -37,6 +37,8 @@ pg_atomic_cas:
 	!
 	!   http://cvs.opensolaris.org/source/xref/on/usr/src/lib/libc/sparc/threads/sparc.il
 	!
+	! NB: We're assuming we're running on a TSO system here - solaris
+	! userland luckily always has done so.
 
 #if defined(__sparcv9) || defined(__sparcv8plus)
 	cas     [%o0],%o2,%o1
diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h
index 895abe6..9aa9e8e 100644
--- a/src/include/storage/s_lock.h
+++ b/src/include/storage/s_lock.h
@@ -341,6 +341,30 @@ tas(volatile slock_t *lock)
 	return (int) _res;
 }
 
+/*
+ * Implement unlocking using swpb as well, that guarantees release
+ * semantics. Only some armv5 models have the data synchronization barrier
+ * instructions - but those need it. Luckily swpb always works.
+ */
+#define S_UNLOCK(lock)		s_unlock(lock)
+
+static __inline__ void
+s_unlock(volatile slock_t *lock)
+{
+	register slock_t _res = 0;
+
+	__asm__ __volatile__(
+		"	swpb 	%0, %0, [%2]	\n"
+:		"+r"(_res), "+m"(*lock)
+:		"r"(lock)
+:		"memory");
+
+	/* lock should have been held */
+	Assert(res == 1);
+}
+
+#define S_INIT_LOCK(lock)	(*(lock) == 0)
+
 #endif	 /* HAVE_GCC_INT_ATOMICS */
 #endif	 /* __arm__ */
 
@@ -393,6 +417,12 @@ tas(volatile slock_t *lock)
 
 
 #if defined(__sparc__)		/* Sparc */
+/*
+ * Solaris has always run sparc processors in TSO (total store) mode, but
+ * linux didn't use to and the *BSDs still don't. So, be careful about
+ * acquire/release semantics. The CPU will treat superflous membars as NOPs,
+ * so it's just code space.
+ */
 #define HAS_TEST_AND_SET
 
 typedef unsigned char slock_t;
@@ -414,9 +444,51 @@ tas(volatile slock_t *lock)
 :		"=r"(_res), "+m"(*lock)
 :		"r"(lock)
 :		"memory");
+#if defined(__sparcv7)
+	/*
+	 * No stbar or membar available, luckily no actually produced hardware
+	 * requires a barrier
+	 */
+#elif defined(__sparcv8)
+	/* stbar is available (and required for both PSO, RMO), membar isn't */
+	__asm__ __volatile__ ("stbar	 \n":::"memory");
+#else
+	/*
+	 * #StoreLoad (RMO) | #StoreStore (PSO, RMO)are the approppriate release
+	 * barrier for sparcv8 upwards.
+	 */
+	__asm__ __volatile__ ("membar  #StoreLoad | #StoreStore \n":::"memory");
+#endif
 	return (int) _res;
 }
 
+#if defined(__sparcv7)
+/*
+ * No stbar or membar available, luckily no actually produced hardware
+ * requires a barrier
+ */
+#define S_UNLOCK(lock)		(*((volatile slock_t *) (lock)) = 0)
+#elif  __sparcv8
+/* stbar is available (and required for both PSO, RMO), membar isn't */
+#define S_UNLOCK(lock)	\
+do \
+{ \
+	__asm__ __volatile__ ("stbar	 \n":::"memory"); \
+	*((volatile slock_t *) (lock)) = 0; \
+} while (0)
+#else
+/*
+ * #StoreStore (PSO, RMO) | #LoadStore (RMO) are the approppriate release
+ * barrier for sparcv8+ upwards.
+ */
+do \
+{ \
+	__asm__ __volatile__ ("membar #LoadStore | #StoreStore \n":::"memory"); \
+	*((volatile slock_t *) (lock)) = 0; \
+} while (0)
+#error "Unknown or unsupported Sparc architecture"
+#endif
+
 #endif	 /* __sparc__ */
 
 
@@ -782,7 +854,7 @@ typedef int slock_t;
 #endif	 /* _AIX */
 
 
-/* These are in s_lock.c */
+/* These are in sunstudio_(sparc|x86).s */
 
 #if defined(__SUNPRO_C) && (defined(__i386) || defined(__x86_64__) || defined(__sparc__) || defined(__sparc))
 #define HAS_TEST_AND_SET
-- 
2.0.0.rc2.4.g1dc51c6.dirty

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to