[PATCH] Fix PPC rwlock code on SMP
Paul Mackerras
paulus at samba.org
Sat Feb 5 14:44:11 EST 2005
Currently, the kernel won't compile for SMP ppc32 if preempt is
enabled. This patch adds suitable read_can_lock and write_can_lock
definitions.
This patch also adds a real _raw_read_trylock (inline and out-of-line
versions), changes the rwlock->lock field to a signed int, which is
what it really was all along, and cleans up the out-of-line rwlock
code in arch/ppc/lib/locks.c. It removes the debug fields from the
rwlock struct because we were never using them, even with
CONFIG_DEBUG_SPINLOCK set.
I have compile and boot tested this with the four combinations of
CONFIG_DEBUG_SPINLOCKS on and off, and CONFIG_PREEMPT on and off.
Please put this patch into 2.6.11 so that 2.6.11 will work for
ppc32 with CONFIG_SMP and CONFIG_PREEMPT.
Signed-off-by: Paul Mackerras <paulus at samba.org>
diff -urN linux-2.5/arch/ppc/lib/locks.c nanango/arch/ppc/lib/locks.c
--- linux-2.5/arch/ppc/lib/locks.c 2004-06-04 15:14:12.000000000 +1000
+++ nanango/arch/ppc/lib/locks.c 2005-02-05 14:02:33.427658848 +1100
@@ -91,44 +91,57 @@
}
EXPORT_SYMBOL(_raw_spin_unlock);
-
/*
- * Just like x86, implement read-write locks as a 32-bit counter
- * with the high bit (sign) being the "write" bit.
- * -- Cort
+ * For rwlocks, zero is unlocked, -1 is write-locked,
+ * positive is read-locked.
*/
+static __inline__ int __read_trylock(rwlock_t *rw)
+{
+ signed int tmp;
+
+ __asm__ __volatile__(
+"2: lwarx %0,0,%1 # __read_trylock\n\
+ addic. %0,%0,1\n\
+ ble- 1f\n"
+ PPC405_ERR77(0,%1)
+" stwcx. %0,0,%1\n\
+ bne- 2b\n\
+ isync\n\
+1:"
+ : "=&r"(tmp)
+ : "r"(&rw->lock)
+ : "cr0", "memory");
+
+ return tmp;
+}
+
+int _raw_read_trylock(rwlock_t *rw)
+{
+ return __read_trylock(rw) > 0;
+}
+EXPORT_SYMBOL(_raw_read_trylock);
+
void _raw_read_lock(rwlock_t *rw)
{
- unsigned long stuck = INIT_STUCK;
- int cpu = smp_processor_id();
+ unsigned int stuck;
-again:
- /* get our read lock in there */
- atomic_inc((atomic_t *) &(rw)->lock);
- if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */
- {
- /* turn off our read lock */
- atomic_dec((atomic_t *) &(rw)->lock);
- /* wait for the write lock to go away */
- while ((signed long)((rw)->lock) < 0)
- {
- if(!--stuck)
- {
- printk("_read_lock(%p) CPU#%d\n", rw, cpu);
+ while (__read_trylock(rw) <= 0) {
+ stuck = INIT_STUCK;
+ while (!read_can_lock(rw)) {
+ if (--stuck == 0) {
+ printk("_read_lock(%p) CPU#%d lock %d\n",
+ rw, _smp_processor_id(), rw->lock);
stuck = INIT_STUCK;
}
}
- /* try to get the read lock again */
- goto again;
}
- wmb();
}
EXPORT_SYMBOL(_raw_read_lock);
void _raw_read_unlock(rwlock_t *rw)
{
if ( rw->lock == 0 )
- printk("_read_unlock(): %s/%d (nip %08lX) lock %lx\n",
+ printk("_read_unlock(): %s/%d (nip %08lX) lock %d\n",
current->comm,current->pid,current->thread.regs->nip,
rw->lock);
wmb();
@@ -138,40 +151,17 @@
void _raw_write_lock(rwlock_t *rw)
{
- unsigned long stuck = INIT_STUCK;
- int cpu = smp_processor_id();
+ unsigned int stuck;
-again:
- if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */
- {
- while ( (rw)->lock & (1<<31) ) /* wait for write lock */
- {
- if(!--stuck)
- {
- printk("write_lock(%p) CPU#%d lock %lx)\n",
- rw, cpu,rw->lock);
+ while (cmpxchg(&rw->lock, 0, -1) != 0) {
+ stuck = INIT_STUCK;
+ while (!write_can_lock(rw)) {
+ if (--stuck == 0) {
+ printk("write_lock(%p) CPU#%d lock %d)\n",
+ rw, _smp_processor_id(), rw->lock);
stuck = INIT_STUCK;
}
- barrier();
}
- goto again;
- }
-
- if ( (rw)->lock & ~(1<<31)) /* someone has a read lock */
- {
- /* clear our write lock and wait for reads to go away */
- clear_bit(31,&(rw)->lock);
- while ( (rw)->lock & ~(1<<31) )
- {
- if(!--stuck)
- {
- printk("write_lock(%p) 2 CPU#%d lock %lx)\n",
- rw, cpu,rw->lock);
- stuck = INIT_STUCK;
- }
- barrier();
- }
- goto again;
}
wmb();
}
@@ -179,14 +169,8 @@
int _raw_write_trylock(rwlock_t *rw)
{
- if (test_and_set_bit(31, &(rw)->lock)) /* someone has a write lock */
- return 0;
-
- if ((rw)->lock & ~(1<<31)) { /* someone has a read lock */
- /* clear our write lock and wait for reads to go away */
- clear_bit(31,&(rw)->lock);
+ if (cmpxchg(&rw->lock, 0, -1) != 0)
return 0;
- }
wmb();
return 1;
}
@@ -194,12 +178,12 @@
void _raw_write_unlock(rwlock_t *rw)
{
- if ( !(rw->lock & (1<<31)) )
- printk("_write_lock(): %s/%d (nip %08lX) lock %lx\n",
+ if (rw->lock >= 0)
+ printk("_write_lock(): %s/%d (nip %08lX) lock %d\n",
current->comm,current->pid,current->thread.regs->nip,
rw->lock);
wmb();
- clear_bit(31,&(rw)->lock);
+ rw->lock = 0;
}
EXPORT_SYMBOL(_raw_write_unlock);
diff -urN linux-2.5/include/asm-ppc/spinlock.h nanango/include/asm-ppc/spinlock.h
--- linux-2.5/include/asm-ppc/spinlock.h 2005-01-21 08:40:04.000000000 +1100
+++ nanango/include/asm-ppc/spinlock.h 2005-02-05 13:57:51.351671856 +1100
@@ -82,29 +82,43 @@
* read-locks.
*/
typedef struct {
- volatile unsigned long lock;
-#ifdef CONFIG_DEBUG_SPINLOCK
- volatile unsigned long owner_pc;
-#endif
+ volatile signed int lock;
#ifdef CONFIG_PREEMPT
unsigned int break_lock;
#endif
} rwlock_t;
-#ifdef CONFIG_DEBUG_SPINLOCK
-#define RWLOCK_DEBUG_INIT , 0
-#else
-#define RWLOCK_DEBUG_INIT /* */
-#endif
-
-#define RW_LOCK_UNLOCKED (rwlock_t) { 0 RWLOCK_DEBUG_INIT }
+#define RW_LOCK_UNLOCKED (rwlock_t) { 0 }
#define rwlock_init(lp) do { *(lp) = RW_LOCK_UNLOCKED; } while(0)
+#define read_can_lock(rw) ((rw)->lock >= 0)
+#define write_can_lock(rw) (!(rw)->lock)
+
#ifndef CONFIG_DEBUG_SPINLOCK
+static __inline__ int _raw_read_trylock(rwlock_t *rw)
+{
+ signed int tmp;
+
+ __asm__ __volatile__(
+"2: lwarx %0,0,%1 # read_trylock\n\
+ addic. %0,%0,1\n\
+ ble- 1f\n"
+ PPC405_ERR77(0,%1)
+" stwcx. %0,0,%1\n\
+ bne- 2b\n\
+ isync\n\
+1:"
+ : "=&r"(tmp)
+ : "r"(&rw->lock)
+ : "cr0", "memory");
+
+ return tmp > 0;
+}
+
static __inline__ void _raw_read_lock(rwlock_t *rw)
{
- unsigned int tmp;
+ signed int tmp;
__asm__ __volatile__(
"b 2f # read_lock\n\
@@ -125,7 +139,7 @@
static __inline__ void _raw_read_unlock(rwlock_t *rw)
{
- unsigned int tmp;
+ signed int tmp;
__asm__ __volatile__(
"eieio # read_unlock\n\
@@ -141,7 +155,7 @@
static __inline__ int _raw_write_trylock(rwlock_t *rw)
{
- unsigned int tmp;
+ signed int tmp;
__asm__ __volatile__(
"2: lwarx %0,0,%1 # write_trylock\n\
@@ -161,7 +175,7 @@
static __inline__ void _raw_write_lock(rwlock_t *rw)
{
- unsigned int tmp;
+ signed int tmp;
__asm__ __volatile__(
"b 2f # write_lock\n\
@@ -192,11 +206,10 @@
extern void _raw_read_unlock(rwlock_t *rw);
extern void _raw_write_lock(rwlock_t *rw);
extern void _raw_write_unlock(rwlock_t *rw);
+extern int _raw_read_trylock(rwlock_t *rw);
extern int _raw_write_trylock(rwlock_t *rw);
#endif
-#define _raw_read_trylock(lock) generic_raw_read_trylock(lock)
-
#endif /* __ASM_SPINLOCK_H */
#endif /* __KERNEL__ */
More information about the Linuxppc-dev
mailing list