[v2,09/15] RISC-V: Atomic and Locking Routines

Message ID 20171220072022.26909-10-palmer@dabbelt.com
State New, archived
Headers

Commit Message

Palmer Dabbelt Dec. 20, 2017, 7:20 a.m. UTC
  This patch implements various atomic and locking routines on RISC-V,
either via the A extension (when present) or via a Linux system call
that does a compare-and-exchange.  This contains both the library
routines and the syscall wrapper.
---
 sysdeps/riscv/nptl/bits/pthreadtypes-arch.h    |  72 ++++++++
 sysdeps/riscv/nptl/bits/semaphore.h            |  33 ++++
 sysdeps/riscv/nptl/libc-lowlevellock.c         |   8 +
 sysdeps/riscv/rv64/rvd/s_roundeven.c           |   1 -
 sysdeps/riscv/rv64/rvf/s_llrintf.c             |   3 +-
 sysdeps/unix/sysv/linux/riscv/atomic-machine.h | 227 +++++++++++++++++++++++++
 6 files changed, 341 insertions(+), 3 deletions(-)
 create mode 100644 sysdeps/riscv/nptl/bits/pthreadtypes-arch.h
 create mode 100644 sysdeps/riscv/nptl/bits/semaphore.h
 create mode 100644 sysdeps/riscv/nptl/libc-lowlevellock.c
 create mode 100644 sysdeps/unix/sysv/linux/riscv/atomic-machine.h
  

Comments

Joseph Myers Dec. 20, 2017, 5:08 p.m. UTC | #1
On Tue, 19 Dec 2017, Palmer Dabbelt wrote:

> This patch implements various atomic and locking routines on RISC-V,
> either via the A extension (when present) or via a Linux system call
> that does a compare-and-exchange.  This contains both the library
> routines and the syscall wrapper.

You said in the introduction that the 'A' extension would be required for 
Linux.  I think you need to update the patch description - *and* the patch 
itself - to reflect that.

> diff --git a/sysdeps/riscv/rv64/rvd/s_roundeven.c b/sysdeps/riscv/rv64/rvd/s_roundeven.c

Does not belong in this patch.

> diff --git a/sysdeps/riscv/rv64/rvf/s_llrintf.c b/sysdeps/riscv/rv64/rvf/s_llrintf.c

Likewise.

> +#ifdef __riscv_atomic
> +
> +#define __HAVE_64B_ATOMICS (__riscv_xlen >= 64)
> +#define USE_ATOMIC_COMPILER_BUILTINS 1
> +#define ATOMIC_EXCHANGE_USES_CAS 0

Missing preprocessor indentation.

> +/* Miscellaneous. */
> +
> +#define asm_amo(which, ordering, mem, value) ({ 		\

Likewise, in this part of the file.

> +#else /* __riscv_atomic */

And this part of the file should now just have a #error.  (Giving an error 
for non-A at configure time would be a good idea as well, so the bad 
configuration gets detected as early as possible.)
  
Palmer Dabbelt Dec. 24, 2017, 1:26 a.m. UTC | #2
On Wed, 20 Dec 2017 09:08:18 PST (-0800), joseph@codesourcery.com wrote:
> On Tue, 19 Dec 2017, Palmer Dabbelt wrote:
>
>> This patch implements various atomic and locking routines on RISC-V,
>> either via the A extension (when present) or via a Linux system call
>> that does a compare-and-exchange.  This contains both the library
>> routines and the syscall wrapper.
>
> You said in the introduction that the 'A' extension would be required for
> Linux.  I think you need to update the patch description - *and* the patch
> itself - to reflect that.
>
>> diff --git a/sysdeps/riscv/rv64/rvd/s_roundeven.c b/sysdeps/riscv/rv64/rvd/s_roundeven.c
>
> Does not belong in this patch.
>
>> diff --git a/sysdeps/riscv/rv64/rvf/s_llrintf.c b/sysdeps/riscv/rv64/rvf/s_llrintf.c
>
> Likewise.
>
>> +#ifdef __riscv_atomic
>> +
>> +#define __HAVE_64B_ATOMICS (__riscv_xlen >= 64)
>> +#define USE_ATOMIC_COMPILER_BUILTINS 1
>> +#define ATOMIC_EXCHANGE_USES_CAS 0
>
> Missing preprocessor indentation.
>
>> +/* Miscellaneous. */
>> +
>> +#define asm_amo(which, ordering, mem, value) ({ 		\
>
> Likewise, in this part of the file.
>
>> +#else /* __riscv_atomic */
>
> And this part of the file should now just have a #error.  (Giving an error
> for non-A at configure time would be a good idea as well, so the bad
> configuration gets detected as early as possible.)

I've already made it #error here, and I added a preconfigure check for the 
atomic extension as well.  I'll fix up the commit for the v3.

Thanks!
  

Patch

diff --git a/sysdeps/riscv/nptl/bits/pthreadtypes-arch.h b/sysdeps/riscv/nptl/bits/pthreadtypes-arch.h
new file mode 100644
index 000000000000..f15e024826ac
--- /dev/null
+++ b/sysdeps/riscv/nptl/bits/pthreadtypes-arch.h
@@ -0,0 +1,72 @@ 
+/* Machine-specific pthread type layouts.  RISC-V version.
+   Copyright (C) 2011-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _BITS_PTHREADTYPES_ARCH_H
+#define _BITS_PTHREADTYPES_ARCH_H	1
+
+#include <endian.h>
+
+#if __riscv_xlen == 64
+# define __SIZEOF_PTHREAD_ATTR_T 		56
+# define __SIZEOF_PTHREAD_MUTEX_T 		40
+# define __SIZEOF_PTHREAD_MUTEXATTR_T 		 4
+# define __SIZEOF_PTHREAD_COND_T 		48
+# define __SIZEOF_PTHREAD_CONDATTR_T 		 4
+# define __SIZEOF_PTHREAD_RWLOCK_T 		56
+# define __SIZEOF_PTHREAD_RWLOCKATTR_T 		 8
+# define __SIZEOF_PTHREAD_BARRIER_T 		32
+# define __SIZEOF_PTHREAD_BARRIERATTR_T 	 4
+#else
+# define __SIZEOF_PTHREAD_ATTR_T 		32
+# define __SIZEOF_PTHREAD_MUTEX_T 		32
+# define __SIZEOF_PTHREAD_MUTEXATTR_T 		 4
+# define __SIZEOF_PTHREAD_COND_T 		48
+# define __SIZEOF_PTHREAD_CONDATTR_T 		 4
+# define __SIZEOF_PTHREAD_RWLOCK_T 		48
+# define __SIZEOF_PTHREAD_RWLOCKATTR_T 		 8
+# define __SIZEOF_PTHREAD_BARRIER_T 		20
+# define __SIZEOF_PTHREAD_BARRIERATTR_T 	 4
+#endif
+
+#define __PTHREAD_COMPAT_PADDING_MID
+#define __PTHREAD_COMPAT_PADDING_END
+#define __PTHREAD_MUTEX_LOCK_ELISION		0
+#define __PTHREAD_MUTEX_USE_UNION	 	0
+#define __PTHREAD_MUTEX_NUSERS_AFTER_KIND	0
+
+#define __LOCK_ALIGNMENT
+#define __ONCE_ALIGNMENT
+
+struct __pthread_rwlock_arch_t
+{
+  unsigned int __readers;
+  unsigned int __writers;
+  unsigned int __wrphase_futex;
+  unsigned int __writers_futex;
+  unsigned int __pad3;
+  unsigned int __pad4;
+  int __cur_writer;
+  int __shared;
+  unsigned long int __pad1;
+  unsigned long int __pad2;
+  unsigned int __flags;
+};
+
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 		0
+
+#endif	/* bits/pthreadtypes.h */
diff --git a/sysdeps/riscv/nptl/bits/semaphore.h b/sysdeps/riscv/nptl/bits/semaphore.h
new file mode 100644
index 000000000000..db6bfba34324
--- /dev/null
+++ b/sysdeps/riscv/nptl/bits/semaphore.h
@@ -0,0 +1,33 @@ 
+/* Machine-specific POSIX semaphore type layouts.  RISC-V version.
+   Copyright (C) 2002-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SEMAPHORE_H
+# error "Never use <bits/semaphore.h> directly; include <semaphore.h> instead."
+#endif
+
+#define __SIZEOF_SEM_T	(4 * __SIZEOF_POINTER__)
+
+/* Value returned if `sem_open' failed.  */
+#define SEM_FAILED      ((sem_t *) 0)
+
+
+typedef union
+{
+  char __size[__SIZEOF_SEM_T];
+  long int __align;
+} sem_t;
diff --git a/sysdeps/riscv/nptl/libc-lowlevellock.c b/sysdeps/riscv/nptl/libc-lowlevellock.c
new file mode 100644
index 000000000000..0ecd41e630f3
--- /dev/null
+++ b/sysdeps/riscv/nptl/libc-lowlevellock.c
@@ -0,0 +1,8 @@ 
+/* This kludge works around a libpthread static linking problem:
+   https://sourceware.org/bugzilla/show_bug.cgi?id=15648 */
+
+#ifndef SHARED
+# define __lll_lock_wait_private weak_function __lll_lock_wait_private
+#endif
+
+#include <lowlevellock.c>
diff --git a/sysdeps/riscv/rv64/rvd/s_roundeven.c b/sysdeps/riscv/rv64/rvd/s_roundeven.c
index 145955c3d6fc..5d857025f2c4 100644
--- a/sysdeps/riscv/rv64/rvd/s_roundeven.c
+++ b/sysdeps/riscv/rv64/rvd/s_roundeven.c
@@ -1,5 +1,4 @@ 
 /* Round to nearest integer value, rounding halfway cases to even.
-   RISC-V version.
    Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
diff --git a/sysdeps/riscv/rv64/rvf/s_llrintf.c b/sysdeps/riscv/rv64/rvf/s_llrintf.c
index 69f4e8ea9b56..4dc9fbc534f4 100644
--- a/sysdeps/riscv/rv64/rvf/s_llrintf.c
+++ b/sysdeps/riscv/rv64/rvf/s_llrintf.c
@@ -1,5 +1,4 @@ 
-/* Round argument to nearest integral value according to current rounding
-   direction.  RISC-V version.
+/* Round argument to nearest integral value according to current direction.
    Copyright (C) 2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
diff --git a/sysdeps/unix/sysv/linux/riscv/atomic-machine.h b/sysdeps/unix/sysv/linux/riscv/atomic-machine.h
new file mode 100644
index 000000000000..29a5599f5319
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/riscv/atomic-machine.h
@@ -0,0 +1,227 @@ 
+/* Low-level functions for atomic operations. RISC-V version.
+   Copyright (C) 2014-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LINUX_RISCV_BITS_ATOMIC_H
+#define _LINUX_RISCV_BITS_ATOMIC_H 1
+
+#include <inttypes.h>
+
+typedef int32_t atomic32_t;
+typedef uint32_t uatomic32_t;
+
+typedef int64_t atomic64_t;
+typedef uint64_t uatomic64_t;
+
+typedef intptr_t atomicptr_t;
+typedef uintptr_t uatomicptr_t;
+typedef intmax_t atomic_max_t;
+typedef uintmax_t uatomic_max_t;
+
+#define atomic_full_barrier() __sync_synchronize()
+
+#ifdef __riscv_atomic
+
+#define __HAVE_64B_ATOMICS (__riscv_xlen >= 64)
+#define USE_ATOMIC_COMPILER_BUILTINS 1
+#define ATOMIC_EXCHANGE_USES_CAS 0
+
+/* Compare and exchange.
+   For all "bool" routines, we return FALSE if exchange succesful.  */
+
+# define __arch_compare_and_exchange_bool_8_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    !__atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				  model, __ATOMIC_RELAXED);		\
+  })
+
+# define __arch_compare_and_exchange_bool_16_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    !__atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				  model, __ATOMIC_RELAXED);		\
+  })
+
+# define __arch_compare_and_exchange_bool_32_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    !__atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				  model, __ATOMIC_RELAXED);		\
+  })
+
+#  define __arch_compare_and_exchange_bool_64_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    !__atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				  model, __ATOMIC_RELAXED);		\
+  })
+
+# define __arch_compare_and_exchange_val_8_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    __atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				 model, __ATOMIC_RELAXED);		\
+    __oldval;								\
+  })
+
+# define __arch_compare_and_exchange_val_16_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    __atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				 model, __ATOMIC_RELAXED);		\
+    __oldval;								\
+  })
+
+# define __arch_compare_and_exchange_val_32_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    __atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				 model, __ATOMIC_RELAXED);		\
+    __oldval;								\
+  })
+
+#  define __arch_compare_and_exchange_val_64_int(mem, newval, oldval, model) \
+  ({									\
+    typeof (*mem) __oldval = (oldval);					\
+    __atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0,	\
+				 model, __ATOMIC_RELAXED);		\
+    __oldval;								\
+  })
+
+/* Atomic compare and exchange. */
+
+# define atomic_compare_and_exchange_bool_acq(mem, new, old)	\
+  __atomic_bool_bysize (__arch_compare_and_exchange_bool, int,	\
+			mem, new, old, __ATOMIC_ACQUIRE)
+
+# define atomic_compare_and_exchange_val_acq(mem, new, old)	\
+  __atomic_val_bysize (__arch_compare_and_exchange_val, int,	\
+		       mem, new, old, __ATOMIC_ACQUIRE)
+
+# define atomic_compare_and_exchange_val_rel(mem, new, old)	 \
+  __atomic_val_bysize (__arch_compare_and_exchange_val, int,    \
+                       mem, new, old, __ATOMIC_RELEASE)
+
+/* Atomic exchange (without compare).  */
+
+# define __arch_exchange_8_int(mem, newval, model)	\
+  __atomic_exchange_n (mem, newval, model)
+
+# define __arch_exchange_16_int(mem, newval, model)	\
+  __atomic_exchange_n (mem, newval, model)
+
+# define __arch_exchange_32_int(mem, newval, model)	\
+  __atomic_exchange_n (mem, newval, model)
+
+#  define __arch_exchange_64_int(mem, newval, model)	\
+  __atomic_exchange_n (mem, newval, model)
+
+# define atomic_exchange_acq(mem, value)				\
+  __atomic_val_bysize (__arch_exchange, int, mem, value, __ATOMIC_ACQUIRE)
+
+# define atomic_exchange_rel(mem, value)				\
+  __atomic_val_bysize (__arch_exchange, int, mem, value, __ATOMIC_RELEASE)
+
+/* Atomically add value and return the previous (unincremented) value.  */
+
+# define __arch_exchange_and_add_8_int(mem, value, model)	\
+  __atomic_fetch_add (mem, value, model)
+
+# define __arch_exchange_and_add_16_int(mem, value, model)	\
+  __atomic_fetch_add (mem, value, model)
+
+# define __arch_exchange_and_add_32_int(mem, value, model)	\
+  __atomic_fetch_add (mem, value, model)
+
+#  define __arch_exchange_and_add_64_int(mem, value, model)	\
+  __atomic_fetch_add (mem, value, model)
+
+# define atomic_exchange_and_add_acq(mem, value)			\
+  __atomic_val_bysize (__arch_exchange_and_add, int, mem, value,	\
+		       __ATOMIC_ACQUIRE)
+
+# define atomic_exchange_and_add_rel(mem, value)			\
+  __atomic_val_bysize (__arch_exchange_and_add, int, mem, value,	\
+		       __ATOMIC_RELEASE)
+
+/* Miscellaneous. */
+
+#define asm_amo(which, ordering, mem, value) ({ 		\
+  __atomic_check_size(mem);					\
+  typeof(*mem) __tmp; 						\
+  if (sizeof(__tmp) == 4)					\
+    asm volatile (which ".w" ordering "\t%0, %z2, %1"		\
+		  : "=r"(__tmp), "+A"(*(mem))			\
+		  : "rJ"(value));				\
+  else if (sizeof(__tmp) == 8)					\
+    asm volatile (which ".d" ordering "\t%0, %z2, %1"		\
+		  : "=r"(__tmp), "+A"(*(mem))			\
+		  : "rJ"(value));				\
+  else								\
+    abort();							\
+  __tmp; })
+
+#define atomic_max(mem, value) asm_amo("amomaxu", ".aq", mem, value)
+#define atomic_min(mem, value) asm_amo("amominu", ".aq", mem, value)
+
+#define atomic_bit_test_set(mem, bit)                   \
+  ({ typeof(*mem) __mask = (typeof(*mem))1 << (bit);    \
+     asm_amo("amoor", ".aq", mem, __mask) & __mask; })
+
+#define catomic_exchange_and_add(mem, value)		\
+  atomic_exchange_and_add(mem, value)
+#define catomic_max(mem, value) atomic_max(mem, value)
+
+#else /* __riscv_atomic */
+
+/* If the A (atomic) extension is not present, we need help from the
+   kernel to do atomic accesses.  Linux provides two system calls for
+   this purpose.  RISCV_ATOMIC_CMPXCHG will perform an atomic compare
+   and exchange operation for a 32-bit value.  RISCV_ATOMIC_CMPXCHG64
+   will do the same for a 64-bit value. */
+
+#include <sys/syscall.h>
+#include <sysdep.h>
+
+#define __HAVE_64B_ATOMICS (__riscv_xlen >= 64)
+#define USE_ATOMIC_COMPILER_BUILTINS 0
+
+#define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
+  (abort (), (__typeof (*mem)) 0)
+
+#define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
+  (abort (), (__typeof (*mem)) 0)
+
+/* The only basic operation needed is compare and exchange.  */
+#define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
+  ({									      \
+    INTERNAL_SYSCALL_DECL (__err);					      \
+    (__typeof (*mem)) INTERNAL_SYSCALL (sysriscv, __err, 4,		      \
+		      RISCV_ATOMIC_CMPXCHG, mem, oldval, newval);	      \
+  })
+
+#define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
+  ({									      \
+    INTERNAL_SYSCALL_DECL (__err);					      \
+    (__typeof (*mem)) INTERNAL_SYSCALL (sysriscv, __err, 4,		      \
+		      RISCV_ATOMIC_CMPXCHG64, mem, oldval, newval);	      \
+  })
+
+#endif /* !__riscv_atomic */
+
+#endif /* bits/atomic.h */