1/* 2 * PowerPC spinlock operations. 3 * With the help of various source: Darwin, Linux kernel... 4 * 5 * $Id: spinlock.h.in,v 1.1 2007/05/24 12:54:10 mbuna Exp $ 6 */ 7#ifndef INCLUDED_PEAK_SPINLOCK_H_ 8#define INCLUDED_PEAK_SPINLOCK_H_ 9 10#define PEAK_SPINLOCK_ITERATIONS 1000 11 12#ifdef HAVE_CONFIG_H 13#include "config.h" 14#endif 15#include <sys/types.h> 16#include <sys/time.h> 17#include <sys/signal.h> 18#include <pthread.h> 19#include <unistd.h> 20#ifdef HAVE_SCHED_H 21#include <sched.h> 22#endif 23#ifdef HAVE_ATOMIC_OP_H 24#include <sys/atomic_op.h> /* AIX */ 25#endif 26 27#include <peak/stdint.h> 28 29/* Erratum #77 on the 405 means we need a sync or dcbt before every stwcx. 30 * The old ATOMIC_SYNC_FIX covered some but not all of this. 31 */ 32#ifdef CONFIG_IBM405_ERR77 33#define PPC405_ERR77(ra,rb) "dcbt " #ra "," #rb ";" 34#else 35#define PPC405_ERR77(ra,rb) 36#endif 37 38/* PEAK defaults to SMP 39 */ 40#ifdef PEAK_CONFIG_UNIPROCESSOR 41#define SMP_WMB 42#define SMP_MB 43#else 44#define SMP_WMB "eieio\n" 45#define SMP_MB "\nsync" 46#endif /* PEAK_CONFIG_UNIPROCESSOR */ 47 48 49#if defined(__cplusplus) 50extern "C" { 51#endif 52 53/* PEAK INTERNAL SPIN LOCK 54 * 55 * _peak_spinlock_lock(lockp) 56 * _peak_spinlock_lock_try(lockp) - returns 0 (busy) or 1 (got the lock) 57 * _peak_spinlock_unlock(lockp) 58 * 59 */ 60 61extern int _peak_is_threaded; 62 63#if defined(HAVE__SPIN_LOCK) && defined(HAVE__SPIN_LOCK_TRY) && defined(HAVE__SPIN_UNLOCK) 64#define USE_DARWIN_SPINLOCK 1 65#else 66#define USE_DARWIN_SPINLOCK 0 67#endif 68 69/* Type and initializer for this architecture. 70 */ 71#if USE_DARWIN_SPINLOCK 72typedef volatile long peak_spinlock_t; 73#define PEAK_SPINLOCK_INITIALIZER (0) 74#elif defined(__GNUC__) 75typedef struct { volatile unsigned long lock; } peak_spinlock_t; 76#define PEAK_SPINLOCK_INITIALIZER ((peak_spinlock_t){ (0) }) 77#elif defined(_AIX) 78typedef int peak_spinlock_t; /* AIX: typedef int *atomic_p; */ 79#define PEAK_SPINLOCK_INITIALIZER (0) 80#endif 81 82#if USE_DARWIN_SPINLOCK 83extern void _spin_lock(peak_spinlock_t *lockp); 84extern int _spin_lock_try(peak_spinlock_t *lockp); 85extern void _spin_unlock(peak_spinlock_t *lockp); 86#endif 87 88static inline void 89_peak_spinlock_lock(peak_spinlock_t *lockp) 90 { 91#if USE_DARWIN_SPINLOCK 92 93 if (!_peak_is_threaded) /* set only if peak uses several threads */ 94 return; 95 96 _spin_lock(lockp); /* Lucky, we have a system function for that. 97 * I checked on Darwin 7.0 and it deals properly 98 * with 32 or 64 bit, UP (always depress) or 99 * MP (1000 spin tries before relinquish). 100 * On Darwin 6.x, however, the kernel use generic 101 * PPC MP code, but well. --mbuna 102 */ 103 104#elif defined(__GNUC__) 105 106 unsigned long tmp; 107 108 if (!_peak_is_threaded) /* set only if peak uses several threads */ 109 return; 110 111 __asm__ __volatile__( 112 "b 1f # spin_lock\n\ 1132: lwzx %0,0,%1\n\ 114 cmpwi 0,%0,0\n\ 115 bne+ 2b\n\ 1161: lwarx %0,0,%1\n\ 117 cmpwi 0,%0,0\n\ 118 bne- 2b\n" 119 PPC405_ERR77(0,%1) 120" stwcx. %2,0,%1\n\ 121 bne- 2b\n\ 122 isync" 123 : "=&r"(tmp) 124 : "r"(&lockp->lock), "r"(1) 125 : "cr0", "memory"); 126 127#elif defined(_AIX) 128 129 unsigned int tries = PEAK_SPINLOCK_ITERATIONS; 130 131 if (!_peak_is_threaded) /* set only if peak uses several threads */ 132 return; 133 134 while (!_check_lock(lockp, 0, 1)) 135 { 136 if (--tries > 0) 137 { 138 sched_yield(); 139 tries = PEAK_SPINLOCK_ITERATIONS; 140 } 141 } 142 143#else 144#error _peak_spinlock_lock not supported 145#endif /* USE_DARWIN_SPINLOCK */ 146 } 147 148static inline int 149_peak_spinlock_lock_try(peak_spinlock_t *lockp) 150 { 151#if USE_DARWIN_SPINLOCK 152 if (!_peak_is_threaded) /* set only if peak uses several threads */ 153 return 1; /* always succeed */ 154 155 return _spin_lock_try(lockp); 156#elif defined(__GNUC__) 157 unsigned int old, t; 158 unsigned int mask = 1; 159 volatile unsigned int *p = ((volatile unsigned int *)lockp->lock); 160 161 if (!_peak_is_threaded) /* set only if peak uses several threads */ 162 return 1; /* always succeed */ 163 164 __asm__ __volatile__(SMP_WMB "\n\ 1651: lwarx %0,0,%4 \n\ 166 or %1,%0,%3 \n" 167 PPC405_ERR77(0,%4) 168" stwcx. %1,0,%4 \n\ 169 bne 1b" 170 SMP_MB 171 : "=&r" (old), "=&r" (t), "=m" (*p) 172 : "r" (mask), "r" (p), "m" (*p) 173 : "cc", "memory"); 174 175 return (old & mask) == 0; 176#elif defined(_AIX) 177 return !_check_lock(lockp, 0, 1); 178#else 179#error _peak_spinlock_lock_try not supported 180#endif 181 } 182 183static inline void 184_peak_spinlock_unlock(peak_spinlock_t *lockp) 185 { 186 if (!_peak_is_threaded) /* set only if peak uses several threads */ 187 return; 188 189#if USE_DARWIN_SPINLOCK 190 if (!_peak_is_threaded) /* set only if peak uses several threads */ 191 return; 192 193 _spin_unlock(lockp); 194 195#elif defined(__GNUC__) 196 /* The eieio instruction is a faster sync: a barrier providing an ordering 197 * (separately) for (a) cacheable stores and (b) loads and stores to 198 * non-cacheable memory (e.g. I/O devices). 199 */ 200 __asm__ __volatile__("eieio # spin_unlock": : :"memory"); 201 lockp->lock = 0; 202#elif defined(_AIX) 203 _clear_lock(lockp, 0); 204#else 205#error _peak_spinlock_unlock not supported 206#endif 207 } 208 209#if defined(__cplusplus) 210} 211#endif 212 213#endif /* INCLUDED_PEAK_SPINLOCK_H_ */ 214