Subject: [nathanw_sa] RAS locking in libpthread
To: None <tech-userlevel@netbsd.org>
From: Jason R Thorpe <thorpej@wasabisystems.com>
List: tech-userlevel
Date: 12/29/2002 17:17:39
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

The following adds support for using RAS locking to libpthread.  It is
preferred on uniprocessors where RAS is available.  Multiprocessors always
use atomic locking.

This is only minimally tested at this time -- If there are no objections,
I'll check it in after a more thorough round of testing.

-- 
        -- Jason R. Thorpe <thorpej@wasabisystems.com>

--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=pthread-ras-patch

Index: TODO
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/TODO,v
retrieving revision 1.1.2.16
diff -c -r1.1.2.16 TODO
*** TODO	2002/10/30 07:35:31	1.1.2.16
--- TODO	2002/12/30 01:12:36
***************
*** 21,29 ****
  - Consider moving pthread__signal_tramp() to its own file, and building
    it with -fasync-unwind-tables, so that DWARF2 EH unwinding works through
    it.  (This is required for e.g. GCC's libjava.)
- - locks: Add support for RAS on uniprocessors.
- - pthread_lock.c: pthread_spin_lock() should use a loop around
-   __cpu_simple_lock_try() so that it is easy to add support for RAS.
  - Add locking to ld.elf_so so that multiple threads doing lazy binding
    doesn't trash things.
  - Verify the cancel stub symbol trickery.
--- 21,26 ----
Index: pthread.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread.c,v
retrieving revision 1.1.2.36
diff -c -r1.1.2.36 pthread.c
*** pthread.c	2002/12/20 01:06:16	1.1.2.36
--- pthread.c	2002/12/30 01:12:36
***************
*** 141,146 ****
--- 141,147 ----
  	PTQ_INSERT_HEAD(&allqueue, first, pt_allq);
  
  	/* Start subsystems */
+ 	pthread__lock_init();
  	pthread__alarm_init();
  	pthread__signal_init();
  	PTHREAD_MD_INIT
Index: pthread_int.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_int.h,v
retrieving revision 1.1.2.36
diff -c -r1.1.2.36 pthread_int.h
*** pthread_int.h	2002/12/16 18:17:46	1.1.2.36
--- pthread_int.h	2002/12/30 01:12:37
***************
*** 151,158 ****
  #endif
  };
  
  
- 
  /* Thread types */
  #define PT_THREAD_NORMAL	1
  #define PT_THREAD_UPCALL	2
--- 151,162 ----
  #endif
  };
  
+ struct pthread_lock_ops {
+ 	void	(*plo_init)(__cpu_simple_lock_t *);
+ 	int	(*plo_try)(__cpu_simple_lock_t *);
+ 	void	(*plo_unlock)(__cpu_simple_lock_t *);
+ };
  
  /* Thread types */
  #define PT_THREAD_NORMAL	1
  #define PT_THREAD_UPCALL	2
***************
*** 235,244 ****
--- 239,255 ----
  void	pthread__alarm_process(pthread_t self, void *arg);
  
  /* Internal locking primitives */
+ void	pthread__lock_init(void);
  void	pthread_lockinit(pthread_spin_t *lock);
  void	pthread_spinlock(pthread_t thread, pthread_spin_t *lock);
  int	pthread_spintrylock(pthread_t thread, pthread_spin_t *lock);
  void	pthread_spinunlock(pthread_t thread, pthread_spin_t *lock);
+ 
+ extern const struct pthread_lock_ops *pthread__lock_ops;
+ 
+ #define	pthread__simple_lock_init(alp)	(*pthread__lock_ops->plo_init)(alp)
+ #define	pthread__simple_lock_try(alp)	(*pthread__lock_ops->plo_try)(alp)
+ #define	pthread__simple_unlock(alp)	(*pthread__lock_ops->plo_unlock)(alp)
  
  int	_getcontext_u(ucontext_t *);
  int	_setcontext_u(const ucontext_t *);
Index: pthread_lock.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_lock.c,v
retrieving revision 1.1.2.13
diff -c -r1.1.2.13 pthread_lock.c
*** pthread_lock.c	2002/12/18 22:53:14	1.1.2.13
--- pthread_lock.c	2002/12/30 01:12:37
***************
*** 36,41 ****
--- 36,45 ----
   * POSSIBILITY OF SUCH DAMAGE.
   */
  
+ #include <sys/param.h>
+ #include <sys/ras.h>
+ #include <sys/sysctl.h>
+ 
  #include <assert.h>
  #include <errno.h>
  #include <unistd.h>
***************
*** 56,66 ****
  
  static int nspins = NSPINS;
  
  void
  pthread_lockinit(pthread_spin_t *lock)
  {
  
! 	__cpu_simple_lock_init(lock);
  }
  
  void
--- 60,164 ----
  
  static int nspins = NSPINS;
  
+ extern char pthread__lock_ras_start[], pthread__lock_ras_end[];
+ 
+ static void
+ pthread__ras_simple_lock_init(__cpu_simple_lock_t *alp)
+ {
+ 
+ 	*alp = __SIMPLELOCK_UNLOCKED;
+ }
+ 
+ static int
+ pthread__ras_simple_lock_try(__cpu_simple_lock_t *alp)
+ {
+ 	__cpu_simple_lock_t old;
+ 
+ 	/* This is the atomic sequence. */
+ 	__asm __volatile("pthread__lock_ras_start:");
+ 	old = *alp;
+ 	*alp = __SIMPLELOCK_LOCKED;
+ 	__asm __volatile("pthread__lock_ras_stop:");
+ 
+ 	return (old == __SIMPLELOCK_UNLOCKED);
+ }
+ 
+ static void
+ pthread__ras_simple_unlock(__cpu_simple_lock_t *alp)
+ {
+ 
+ 	*alp = __SIMPLELOCK_UNLOCKED;
+ }
+ 
+ static const struct pthread_lock_ops pthread__lock_ops_ras = {
+ 	pthread__ras_simple_lock_init,
+ 	pthread__ras_simple_lock_try,
+ 	pthread__ras_simple_unlock,
+ };
+ 
+ static void
+ pthread__atomic_simple_lock_init(__cpu_simple_lock_t *alp)
+ {
+ 
+ 	__cpu_simple_lock_init(alp);
+ }
+ 
+ static int
+ pthread__atomic_simple_lock_try(__cpu_simple_lock_t *alp)
+ {
+ 
+ 	return (__cpu_simple_lock_try(alp));
+ }
+ 
+ static void
+ pthread__atomic_simple_unlock(__cpu_simple_lock_t *alp)
+ {
+ 
+ 	__cpu_simple_unlock(alp);
+ }
+ 
+ static const struct pthread_lock_ops pthread__lock_ops_atomic = {
+ 	pthread__atomic_simple_lock_init,
+ 	pthread__atomic_simple_lock_try,
+ 	pthread__atomic_simple_unlock,
+ };
+ 
+ const struct pthread_lock_ops *pthread__lock_ops;
+ 
+ /*
+  * Initialize the locking primitives.  On uniprocessors, we always
+  * use Restartable Atomic Sequences if they are available.  Otherwise,
+  * we fall back onto machine-dependent atomic lock primitives.
+  */
+ void
+ pthread__lock_init(void)
+ {
+ 	int mib[2];
+ 	size_t len; 
+ 	int ncpu;
+  
+ 	mib[0] = CTL_HW;
+ 	mib[1] = HW_NCPU; 
+  
+ 	len = sizeof(ncpu);
+ 	sysctl(mib, 2, &ncpu, &len, NULL, 0);
+ 
+ 	if (ncpu == 1 &&
+ 	    rasctl(pthread__lock_ras_start,
+ 	    	   (caddr_t)pthread__lock_ras_end -
+ 	    	   (caddr_t)pthread__lock_ras_start, RAS_INSTALL) == 0) {
+ 		pthread__lock_ops = &pthread__lock_ops_ras;
+ 		return;
+ 	}
+ 
+ 	pthread__lock_ops = &pthread__lock_ops_atomic;
+ }
+ 
  void
  pthread_lockinit(pthread_spin_t *lock)
  {
  
! 	pthread__simple_lock_init(lock);
  }
  
  void
***************
*** 80,86 ****
  	++thread->pt_spinlocks;
  
  	do {
! 		while (((ret = __cpu_simple_lock_try(lock)) == 0) && --count)
  			;
  
  		if (ret == 1)
--- 178,184 ----
  	++thread->pt_spinlocks;
  
  	do {
! 		while (((ret = pthread__simple_lock_try(lock)) == 0) && --count)
  			;
  
  		if (ret == 1)
***************
*** 126,132 ****
  		thread, thread->pt_spinlocks));
  	++thread->pt_spinlocks;
  
! 	ret = __cpu_simple_lock_try(lock);
  	
  	if (ret == 0) {
  	SDPRINTF(("(pthread_spintrylock %p) decrementing spinlock from %d\n",
--- 224,230 ----
  		thread, thread->pt_spinlocks));
  	++thread->pt_spinlocks;
  
! 	ret = pthread__simple_lock_try(lock);
  	
  	if (ret == 0) {
  	SDPRINTF(("(pthread_spintrylock %p) decrementing spinlock from %d\n",
***************
*** 147,153 ****
  pthread_spinunlock(pthread_t thread, pthread_spin_t *lock)
  {
  
! 	__cpu_simple_unlock(lock);
  	SDPRINTF(("(pthread_spinunlock %p) decrementing spinlock %p (count %d)\n",
  		thread, lock, thread->pt_spinlocks));
  	--thread->pt_spinlocks;
--- 245,251 ----
  pthread_spinunlock(pthread_t thread, pthread_spin_t *lock)
  {
  
! 	pthread__simple_unlock(lock);
  	SDPRINTF(("(pthread_spinunlock %p) decrementing spinlock %p (count %d)\n",
  		thread, lock, thread->pt_spinlocks));
  	--thread->pt_spinlocks;
***************
*** 228,234 ****
  		return EINVAL;
  #endif
  
! 	__cpu_simple_lock(&lock->pts_spin);
  
  	return 0;
  }
--- 326,333 ----
  		return EINVAL;
  #endif
  
! 	while (pthread__simple_lock_try(&lock->pts_spin) == 0)
! 		/* spin */ ;
  
  	return 0;
  }
***************
*** 242,248 ****
  		return EINVAL;
  #endif
  
! 	if (__cpu_simple_lock_try(&lock->pts_spin) == 0)
  		return EBUSY;
  
  	return 0;
--- 341,347 ----
  		return EINVAL;
  #endif
  
! 	if (pthread__simple_lock_try(&lock->pts_spin) == 0)
  		return EBUSY;
  
  	return 0;
***************
*** 257,263 ****
  		return EINVAL;
  #endif
  
! 	__cpu_simple_unlock(&lock->pts_spin);
  
  	return 0;
  }
--- 356,362 ----
  		return EINVAL;
  #endif
  
! 	pthread__simple_unlock(&lock->pts_spin);
  
  	return 0;
  }
Index: pthread_mutex.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_mutex.c,v
retrieving revision 1.1.2.15
diff -c -r1.1.2.15 pthread_mutex.c
*** pthread_mutex.c	2002/10/26 02:17:44	1.1.2.15
--- pthread_mutex.c	2002/12/30 01:12:37
***************
*** 89,96 ****
   * same mutex.
   * 
   * A memory barrier after a lock and before an unlock will provide
!  * this behavior. This code relies on __cpu_simple_lock_try() to issue
!  * a barrier after obtaining a lock, and on __cpu_simple_unlock() to
   * issue a barrier before releasing a lock.
   */
  
--- 89,96 ----
   * same mutex.
   * 
   * A memory barrier after a lock and before an unlock will provide
!  * this behavior. This code relies on pthread__simple_lock_try() to issue
!  * a barrier after obtaining a lock, and on pthread__simple_unlock() to
   * issue a barrier before releasing a lock.
   */
  
***************
*** 103,109 ****
  		return EINVAL;
  #endif
  
! 	if (__predict_false(__cpu_simple_lock_try(&mutex->ptm_lock) == 0))
  		pthread_mutex_lock_slow(mutex);
  
  	/* We have the lock! */
--- 103,109 ----
  		return EINVAL;
  #endif
  
! 	if (__predict_false(pthread__simple_lock_try(&mutex->ptm_lock) == 0))
  		pthread_mutex_lock_slow(mutex);
  
  	/* We have the lock! */
***************
*** 121,127 ****
  	self = pthread__self();
  
  	while (/*CONSTCOND*/1) {
! 		if (__cpu_simple_lock_try(&mutex->ptm_lock))
  		    break; /* got it! */
  		
  		/* Okay, didn't look free. Get the interlock... */
--- 121,127 ----
  	self = pthread__self();
  
  	while (/*CONSTCOND*/1) {
! 		if (pthread__simple_lock_try(&mutex->ptm_lock))
  		    break; /* got it! */
  		
  		/* Okay, didn't look free. Get the interlock... */
***************
*** 169,175 ****
  		return EINVAL;
  #endif
  
! 	if (__cpu_simple_lock_try(&mutex->ptm_lock) == 0)
  		return EBUSY;
  
  #ifdef ERRORCHECK
--- 169,175 ----
  		return EINVAL;
  #endif
  
! 	if (pthread__simple_lock_try(&mutex->ptm_lock) == 0)
  		return EBUSY;
  
  #ifdef ERRORCHECK
***************
*** 201,207 ****
  #ifdef ERRORCHECK
  	mutex->ptm_owner = NULL;
  #endif
! 	__cpu_simple_unlock(&mutex->ptm_lock);
  	pthread_spinunlock(self, &mutex->ptm_interlock);
  
  	/* Give the head of the blocked queue another try. */
--- 201,207 ----
  #ifdef ERRORCHECK
  	mutex->ptm_owner = NULL;
  #endif
! 	pthread__simple_unlock(&mutex->ptm_lock);
  	pthread_spinunlock(self, &mutex->ptm_interlock);
  
  	/* Give the head of the blocked queue another try. */
Index: pthread_sa.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_sa.c,v
retrieving revision 1.1.2.34
diff -c -r1.1.2.34 pthread_sa.c
*** pthread_sa.c	2002/12/16 18:32:21	1.1.2.34
--- pthread_sa.c	2002/12/30 01:12:38
***************
*** 381,387 ****
  						 */
  						lock = victim->pt_heldlock;
  						victim->pt_heldlock = NULL;
! 						__cpu_simple_unlock(lock);
  						victim->pt_uc = 
  						    victim->pt_sleepuc;
  						victim->pt_sleepuc = NULL;
--- 381,387 ----
  						 */
  						lock = victim->pt_heldlock;
  						victim->pt_heldlock = NULL;
! 						pthread__simple_unlock(lock);
  						victim->pt_uc = 
  						    victim->pt_sleepuc;
  						victim->pt_sleepuc = NULL;
Index: pthread_types.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_types.h,v
retrieving revision 1.1.2.9
diff -c -r1.1.2.9 pthread_types.h
*** pthread_types.h	2002/10/28 00:06:09	1.1.2.9
--- pthread_types.h	2002/12/30 01:12:38
***************
*** 89,95 ****
  	unsigned int	ptm_magic;
  
  	/* Not a real spinlock; will never be spun on. Locked with
! 	 * __cpu_simple_lock_try() or not at all. Therefore, not
  	 * subject to preempted-spinlock-continuation.
  	 * 
  	 * Open research question: Would it help threaded applications if
--- 89,95 ----
  	unsigned int	ptm_magic;
  
  	/* Not a real spinlock; will never be spun on. Locked with
! 	 * pthread__simple_lock_try() or not at all. Therefore, not
  	 * subject to preempted-spinlock-continuation.
  	 * 
  	 * Open research question: Would it help threaded applications if

--0OAP2g/MAC+5xKAE--