[parisc-linux-cvs] Semaphore patch

Matthew Wilcox willy@ldl.fc.hp.com
Thu, 15 Feb 2001 19:57:23 -0700


I know you're all sick of me sending out semaphore patches by now, but
this one's been tested on an SMP machine and appears to actually work :-)

Index: include/asm-parisc/semaphore.h
===================================================================
RCS file: /home/cvs/parisc/linux/include/asm-parisc/semaphore.h,v
retrieving revision 1.5
diff -u -p -r1.5 semaphore.h
--- semaphore.h	2000/08/11 23:40:13	1.5
+++ semaphore.h	2001/02/16 02:49:03
@@ -1,29 +1,28 @@
 #ifndef _ASM_PARISC_SEMAPHORE_H
 #define _ASM_PARISC_SEMAPHORE_H
 
-#include <linux/linkage.h>
-
 /*
  * SMP- and interrupt-safe semaphores.
  *
  * (C) Copyright 1996 Linus Torvalds
  *
- * SuperH verison by Niibe Yutaka
+ * PA-RISC version by Matthew Wilcox
  *
  */
 
-/* if you're going to use out-of-line slowpaths, use .section .lock.text,
- * not .text.lock or the -ffunction-sections monster will eat you alive
- */
-
 #include <linux/spinlock.h>
-
+#include <linux/wait.h>
 #include <asm/system.h>
-#include <asm/atomic.h>
 
+/*
+ * The `count' is initialised to the number of people who are allowed to
+ * take the lock.  (Normally we want a mutex, so this is `1').  if
+ * `count' is positive, the lock can be taken.  if it's 0, no-one is
+ * waiting on it.  if it's -1, at least one task is waiting.
+ */
 struct semaphore {
-	atomic_t count;
-	int waking;
+	spinlock_t	sentry;
+	int		count;
 	wait_queue_head_t wait;
 #if WAITQUEUE_DEBUG
 	long __magic;
@@ -38,7 +37,7 @@ struct semaphore {
 #endif
 
 #define __SEMAPHORE_INITIALIZER(name,count) \
-{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+{ SPIN_LOCK_UNLOCKED, count, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
 	__SEM_DEBUG_INIT(name) }
 
 #define __MUTEX_INITIALIZER(name) \
@@ -52,18 +51,7 @@ struct semaphore {
 
 extern inline void sema_init (struct semaphore *sem, int val)
 {
-/*
- *	*sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val);
- *
- * i'd rather use the more flexible initialization above, but sadly
- * GCC 2.7.2.3 emits a bogus warning. EGCS doesnt. Oh well.
- */
-	atomic_set(&sem->count, val);
-	sem->waking = 0;
-	init_waitqueue_head(&sem->wait);
-#if WAITQUEUE_DEBUG
-	sem->__magic = (long)&sem->__magic;
-#endif
+	*sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val);
 }
 
 static inline void init_MUTEX (struct semaphore *sem)
@@ -76,17 +64,13 @@ static inline void init_MUTEX_LOCKED (st
 	sema_init(sem, 0);
 }
 
-asmlinkage void __down_failed(void /* special register calling convention */);
-asmlinkage int  __down_failed_interruptible(void  /* params in registers */);
-asmlinkage int  __down_failed_trylock(void  /* params in registers */);
-asmlinkage void __up_wakeup(void /* special register calling convention */);
-
 asmlinkage void __down(struct semaphore * sem);
 asmlinkage int  __down_interruptible(struct semaphore * sem);
-asmlinkage int  __down_trylock(struct semaphore * sem);
 asmlinkage void __up(struct semaphore * sem);
 
-extern spinlock_t semaphore_wake_lock;
+/* Sempahores can be `tried' from irq context.  So we have to disable
+ * interrupts while we're messing with the semaphore.  Sorry.
+ */
 
 extern __inline__ void down(struct semaphore * sem)
 {
@@ -94,8 +78,11 @@ extern __inline__ void down(struct semap
 	CHECK_MAGIC(sem->__magic);
 #endif
 
-	if (atomic_dec_return(&sem->count) < 0)
+	spin_lock_irq(&sem->sentry);
+	if (sem->count <= 0)
 		__down(sem);
+	sem->count--;
+	spin_unlock_irq(&sem->sentry);
 }
 
 extern __inline__ int down_interruptible(struct semaphore * sem)
@@ -105,21 +92,32 @@ extern __inline__ int down_interruptible
 	CHECK_MAGIC(sem->__magic);
 #endif
 
-	if (atomic_dec_return(&sem->count) < 0)
+	spin_lock_irq(&sem->sentry);
+	if (sem->count <= 0)
 		ret = __down_interruptible(sem);
+	if (ret == 0)
+		sem->count--;
+	spin_unlock_irq(&sem->sentry);
 	return ret;
 }
 
+/*
+ * down_trylock returns 0 on success, 1 if we failed to get the lock.
+ * May not sleep, but must preserve irq state
+ */
 extern __inline__ int down_trylock(struct semaphore * sem)
 {
-	int ret = 0;
+	int flags, count;
 #if WAITQUEUE_DEBUG
 	CHECK_MAGIC(sem->__magic);
 #endif
 
-	if (atomic_dec_return(&sem->count) < 0)
-		ret = __down_trylock(sem);
-	return ret;
+	spin_lock_irqsave(&sem->sentry, flags);
+	count = sem->count - 1;
+	if (count >= 0)
+		sem->count = count;
+	spin_unlock_irqrestore(&sem->sentry, flags);
+	return (count < 0);
 }
 
 /*
@@ -128,11 +126,14 @@ extern __inline__ int down_trylock(struc
  */
 extern __inline__ void up(struct semaphore * sem)
 {
+	int flags;
 #if WAITQUEUE_DEBUG
 	CHECK_MAGIC(sem->__magic);
 #endif
-	if (atomic_inc_return(&sem->count) <= 0)
+	spin_lock_irqsave(&sem->sentry, flags);
+	if (++sem->count <= 0)
 		__up(sem);
+	spin_unlock_irqrestore(&sem->sentry, flags);
 }
 
 /* rw mutexes (should that be mutices? =) -- throw rw
@@ -146,9 +147,9 @@ extern __inline__ void up(struct semapho
  * (in which case it goes to sleep).
  *
  * The value 0x01000000 supports up to 128 processors and
- * lots of processes.  BIAS must be chosen such that subl'ing
- * BIAS once per CPU will result in the long remaining
- * negative.
+ * lots of processes.  BIAS must be chosen such that subtracting
+ * BIAS three times will result in the value remaining negative,
+ * and it must be at least the number of processes in the system.
  *
  * In terms of fairness, this should result in the lock
  * flopping back and forth between readers and writers
@@ -156,146 +157,131 @@ extern __inline__ void up(struct semapho
  *
  *              -ben
  */
+/* NOTE: There is currently no provision for attempting to acquire
+ * rw_sems from interrupt context.  These routines will require more
+ * work if this is to be allowed.
+ */
+
 struct rw_semaphore {
-        atomic_t                count;
-        volatile unsigned char  write_bias_granted;
-        volatile unsigned char  read_bias_granted;
-        volatile unsigned char  pad1;
-        volatile unsigned char  pad2;
-        wait_queue_head_t       wait;
-        wait_queue_head_t       write_bias_wait;
-#if WAITQUEUE_DEBUG
-        long                    __magic;
-        atomic_t                readers;
-        atomic_t                writers;
+	spinlock_t		sentry;
+	signed int			count;
+	wait_queue_head_t	wait;
+#if WAITQUEUE_DEBUG
+	long			__magic;
+	volatile int		readers;
+	volatile int		writers;
 #endif
 };
 
 #if WAITQUEUE_DEBUG
-#define __RWSEM_DEBUG_INIT      , ATOMIC_INIT(0), ATOMIC_INIT(0)
+#define __RWSEM_DEBUG_INIT	, 0, 0
 #else
-#define __RWSEM_DEBUG_INIT      /* */
+#define __RWSEM_DEBUG_INIT	/* */
 #endif
 
-#define RW_LOCK_BIAS 0x01000000
+#define RWSEM_BIAS	0x01000000
 
 #define __RWSEM_INITIALIZER(name,count) \
-{ ATOMIC_INIT(count), 0, 0, 0, 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait), \
-        __WAIT_QUEUE_HEAD_INITIALIZER((name).write_bias_wait) \
-        __SEM_DEBUG_INIT(name) __RWSEM_DEBUG_INIT }
+{ SPIN_LOCK_UNLOCKED, count, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait), \
+	__SEM_DEBUG_INIT(name) __RWSEM_DEBUG_INIT }
 
 #define __DECLARE_RWSEM_GENERIC(name,count) \
-        struct rw_semaphore name = __RWSEM_INITIALIZER(name,count)
+	struct rw_semaphore name = __RWSEM_INITIALIZER(name,count)
 
-#define DECLARE_RWSEM(name) __DECLARE_RWSEM_GENERIC(name,RW_LOCK_BIAS)
-#define DECLARE_RWSEM_READ_LOCKED(name) __DECLARE_RWSEM_GENERIC(name,RW_LOCK_BIAS-1)
-#define DECLARE_RWSEM_WRITE_LOCKED(name) __DECLARE_RWSEM_GENERIC(name,0)
+#define DECLARE_RWSEM(name) __DECLARE_RWSEM_GENERIC(name, RWSEM_BIAS)
+#define DECLARE_RWSEM_READ_LOCKED(name) __DECLARE_RWSEM_GENERIC(name, RWSEM_BIAS - 1)
+#define DECLARE_RWSEM_WRITE_LOCKED(name) __DECLARE_RWSEM_GENERIC(name, 0)
 
 extern inline void init_rwsem(struct rw_semaphore *sem)
 {
-        atomic_set(&sem->count, RW_LOCK_BIAS);
-        sem->read_bias_granted = 0;
-        sem->write_bias_granted = 0;
-        init_waitqueue_head(&sem->wait);
-        init_waitqueue_head(&sem->write_bias_wait);
-#if WAITQUEUE_DEBUG
-        sem->__magic = (long)&sem->__magic;
-        atomic_set(&sem->readers, 0);
-        atomic_set(&sem->writers, 0);
-#endif
+	*sem = (struct rw_semaphore) __RWSEM_INITIALIZER(*sem, RWSEM_BIAS);
 }
 
-#ifdef FIXME_WILLY_FIXME_FOR_REAL_THIS_TIME
-extern struct rw_semaphore *__build_read_lock(struct rw_semaphore *sem, const char *what);
-extern struct rw_semaphore *__build_write_lock(struct rw_semaphore *sem, const char *what);
-#endif
-
 /* we use FASTCALL convention for the helpers */
-extern struct rw_semaphore *FASTCALL(__down_read_failed(struct rw_semaphore *sem));
-extern struct rw_semaphore *FASTCALL(__down_write_failed(struct rw_semaphore *sem));
-extern struct rw_semaphore *FASTCALL(__rwsem_wake(struct rw_semaphore *sem));
+extern void FASTCALL(down_read_failed(struct rw_semaphore *sem));
+extern void FASTCALL(down_write_failed(struct rw_semaphore *sem));
+extern void FASTCALL(up_read_wakeup(struct rw_semaphore *sem));
+extern void FASTCALL(up_write_wakeup(struct rw_semaphore *sem));
 
 extern inline void down_read(struct rw_semaphore *sem)
 {
+	spin_lock(&sem->sentry);
+
 #if WAITQUEUE_DEBUG
-        if (sem->__magic != (long)&sem->__magic)
-                BUG();
-#endif
-#ifdef FIXME_WILLY_FIXME_FOR_REAL_THIS_TIME
-        __build_read_lock(sem, "__down_read_failed");
+	if (sem->__magic != (long)&sem->__magic)
+		BUG();
 #endif
+
+	if (sem->count <= 0)
+		down_read_failed(sem);
+	sem->count--;
+
 #if WAITQUEUE_DEBUG
-        if (sem->write_bias_granted)
-                BUG();
-        if (atomic_read(&sem->writers))
-                BUG();
-        atomic_inc(&sem->readers);
+	if (sem->n_writers)
+		BUG();
+	sem->n_readers++;
 #endif
+
+	spin_unlock(&sem->sentry);
 }
 
 extern inline void down_write(struct rw_semaphore *sem)
 {
+	int count;
+	spin_lock(&sem->sentry);
+
 #if WAITQUEUE_DEBUG
-        if (sem->__magic != (long)&sem->__magic)
-                BUG();
-#endif
-#ifdef FIXME_WILLY_FIXME_FOR_REAL_THIS_TIME
-        __build_write_lock(sem, "__down_write_failed");
+	if (sem->__magic != (long)&sem->__magic)
+		BUG();
 #endif
+
+	count = sem->count - RWSEM_BIAS;
+	if (count != 0)
+		down_write_failed(sem);
+	sem->count = 0;
+
 #if WAITQUEUE_DEBUG
-        if (atomic_read(&sem->writers))
-                BUG();
-        if (atomic_read(&sem->readers))
-                BUG();
-        if (sem->read_bias_granted)
-                BUG();
-        if (sem->write_bias_granted)
-                BUG();
-        atomic_inc(&sem->writers);
+	if (sem->n_writers)
+		BUG();
+	if (sem->n_readers)
+		BUG();
+	sem->n_writers++;
 #endif
-}
 
-/* When a reader does a release, the only significant
- * case is when there was a writer waiting, and we've
- * bumped the count to 0: we must wake the writer up.
- */
-extern inline void __up_read(struct rw_semaphore *sem)
-{
-}
-
-/* releasing the writer is easy -- just release it and
- * wake up any sleepers.
- */
-extern inline void __up_write(struct rw_semaphore *sem)
-{
+	spin_unlock(&sem->sentry);
 }
 
 extern inline void up_read(struct rw_semaphore *sem)
 {
+	spin_lock(&sem->sentry);
+
 #if WAITQUEUE_DEBUG
-        if (sem->write_bias_granted)
-                BUG();
-        if (atomic_read(&sem->writers))
-                BUG();
-        atomic_dec(&sem->readers);
+	if (sem->n_writers)
+		BUG();
+	sem->n_readers++;
 #endif
-        __up_read(sem);
+
+	if (++sem->count == 0)
+		up_read_wakeup(sem);
+	spin_unlock(&sem->sentry);
 }
 
 extern inline void up_write(struct rw_semaphore *sem)
 {
+	spin_lock(&sem->sentry);
+
 #if WAITQUEUE_DEBUG
-        if (sem->read_bias_granted)
-                BUG();
-        if (sem->write_bias_granted)
-                BUG();
-        if (atomic_read(&sem->readers))
-                BUG();
-        if (atomic_read(&sem->writers) != 1)
-                BUG();
-        atomic_dec(&sem->writers);
+	if (sem->n_readers)
+		BUG();
+	if (sem->n_writers != 1)
+		BUG();
+	sem->n_writers--;
 #endif
-        __up_write(sem);
+
+	sem->count += RWSEM_BIAS;
+	if (sem->count != RWSEM_BIAS)
+		up_write_wakeup(sem);
+	spin_unlock(&sem->sentry);
 }
 
 #endif /* _ASM_PARISC_SEMAPHORE_H */
Index: arch/parisc/kernel/semaphore.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/semaphore.c,v
retrieving revision 1.4
diff -u -p -r1.4 semaphore.c
--- semaphore.c	2001/01/24 23:59:51	1.4
+++ semaphore.c	2001/02/16 02:49:03
@@ -1,15 +1,9 @@
 /*
- * Just taken from alpha implementation.
- * This can't work well, perhaps.
+ * Semaphore implementation Copyright (c) 2001 Matthew Wilcox
  */
-/*
- *  Generic semaphore code. Buyer beware. Do your own
- * specific changes in <asm/semaphore-helper.h>
- */
 
 #include <linux/sched.h>
-#include <asm/semaphore-helper.h>
-#include <asm/atomic.h>	/* for xchg() definitions */
+#include <linux/spinlock.h>
 
 /*
  * Semaphores are implemented using a two-way counter:
@@ -23,9 +17,6 @@
  * needs to do something only if count was negative before
  * the increment operation.
  *
- * waking_non_zero() (from asm/semaphore.h) must execute
- * atomically.
- *
  * When __up() is called, the count was negative before
  * incrementing it, and we need to wake up somebody.
  *
@@ -43,198 +34,159 @@
  */
 void __up(struct semaphore *sem)
 {
-	wake_one_more(sem);
+	printk("process %d on cpu %d waking up %p\n", current->pid,
+			current->processor, &sem->wait);
 	wake_up(&sem->wait);
 }
-
-/*
- * Perform the "down" function.  Return zero for semaphore acquired,
- * return negative for signalled out of the function.
- *
- * If called from __down, the return is ignored and the wait loop is
- * not interruptible.  This means that a task waiting on a semaphore
- * using "down()" cannot be killed until someone does an "up()" on
- * the semaphore.
- *
- * If called from __down_interruptible, the return value gets checked
- * upon return.  If the return value is negative then the task continues
- * with the negative value in the return register (it can be tested by
- * the caller).
- *
- * Either form may be used in conjunction with "up()".
- *
- */
 
-
-#define DOWN_HEAD(task_state)						\
-									\
+#define DOWN_HEAD							\
+	DECLARE_WAITQUEUE(wait, current);				\
 									\
-	current->state = (task_state);					\
-	add_wait_queue(&sem->wait, &wait);				\
+	/* Note that someone is waiting */				\
+	if (sem->count == 0)						\
+		sem->count = -1;					\
 									\
-	/*								\
-	 * Ok, we're set up.  sem->count is known to be less than zero	\
-	 * so we must wait.						\
-	 *								\
-	 * We can let go the lock for purposes of waiting.		\
-	 * We re-acquire it after awaking so as to protect		\
-	 * all semaphore operations.					\
-	 *								\
-	 * If "up()" is called before we call waking_non_zero() then	\
-	 * we will catch it right away.  If it is called later then	\
-	 * we will have to go through a wakeup cycle to catch it.	\
-	 *								\
-	 * Multiple waiters contend for the semaphore lock to see	\
-	 * who gets to gate through and who has to wait some more.	\
-	 */								\
-	for (;;) {
-
-#define DOWN_TAIL(task_state)			\
-		current->state = (task_state);	\
-	}					\
-	current->state = TASK_RUNNING;		\
-	remove_wait_queue(&sem->wait, &wait);
+	/* protected by the sentry still -- use unlocked version */	\
+	wait.flags = WQ_FLAG_EXCLUSIVE;					\
+	__add_wait_queue_tail(&sem->wait, &wait);			\
+	printk("process %d on cpu %d sleeping on %p\n", current->pid,	\
+			current->processor, &sem->wait);		\
+lost_race:								\
+	spin_unlock_irq(&sem->sentry);					\
+
+#define DOWN_TAIL							\
+	spin_lock_irq(&sem->sentry);					\
+	if (sem->count < 0)						\
+		goto lost_race;						\
+	__remove_wait_queue(&sem->wait, &wait);				\
+	printk("process %d on cpu %d waking on %p\n", current->pid,	\
+			current->processor, &sem->wait);		\
+	current->state = TASK_RUNNING;					\
+	if (!waitqueue_active(&sem->wait))				\
+		sem->count = 1;
 
 void __down(struct semaphore * sem)
 {
-	DECLARE_WAITQUEUE(wait, current);
+	DOWN_HEAD
 
-	DOWN_HEAD(TASK_UNINTERRUPTIBLE)
-	if (waking_non_zero(sem))
-		break;
-	schedule();
-	DOWN_TAIL(TASK_UNINTERRUPTIBLE)
+	for(;;) {
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		/* we can _read_ this without the sentry */
+		printk("process %d on cpu %d checking count %d\n", current->pid,
+			current->processor, sem->count);
+		if (sem->count >= 0)
+			break;
+ 		schedule();
+ 	}
+
+	DOWN_TAIL
 }
 
 int __down_interruptible(struct semaphore * sem)
 {
-	DECLARE_WAITQUEUE(wait, current);
 	int ret = 0;
-
-	DOWN_HEAD(TASK_INTERRUPTIBLE)
 
-	ret = waking_non_zero_interruptible(sem, current);
-	if (ret)
-	{
-		if (ret == 1)
-			/* ret != 0 only if we get interrupted -arca */
-			ret = 0;
-		break;
-	}
-	schedule();
-	DOWN_TAIL(TASK_INTERRUPTIBLE)
-	return ret;
-}
+	DOWN_HEAD
 
-int __down_trylock(struct semaphore * sem)
-{
-	return waking_non_zero_trylock(sem);
-}
-
-
-/* Wait for the lock to become unbiased.  Readers
- * are non-exclusive. =)
- */
-void down_read_failed(struct rw_semaphore *sem)
-{
-	DECLARE_WAITQUEUE(wait, current);
-
-	__up_read(sem);	/* this takes care of granting the lock */
-
-	add_wait_queue(&sem->wait, &wait);
+	for(;;) {
+		set_task_state(current, TASK_INTERRUPTIBLE);
+		/* we can _read_ this without the sentry */
+		printk("process %d on cpu %d checking count %d\n", current->pid,
+			current->processor, sem->count);
+		if (sem->count >= 0)
+			break;
 
-	while (atomic_read(&sem->count) < 0) {
-		set_task_state(current, TASK_UNINTERRUPTIBLE);
-		if (atomic_read(&sem->count) >= 0)
+		if (signal_pending(current)) {
+			ret = -EINTR;
 			break;
+		}
 		schedule();
 	}
 
-	remove_wait_queue(&sem->wait, &wait);
-	current->state = TASK_RUNNING;
+	DOWN_TAIL
+
+	return ret;
 }
+
+/* Read/write semaphores below this point */
 
-void down_read_failed_biased(struct rw_semaphore *sem)
+/* We must wait for a writer to up the semaphore */
+void down_read_failed(struct rw_semaphore *sem)
 {
 	DECLARE_WAITQUEUE(wait, current);
 
-	add_wait_queue(&sem->wait, &wait);	/* put ourselves at the head of the list */
+	/* Indicate that we exist */
+	if (!waitqueue_active(&sem->wait))
+		sem->count = -1;
+
+	__add_wait_queue_tail(&sem->wait, &wait);
+lost_race:
+	spin_unlock(&sem->sentry);
 
 	for (;;) {
-		if (sem->read_bias_granted && xchg(&sem->read_bias_granted, 0))
-			break;
 		set_task_state(current, TASK_UNINTERRUPTIBLE);
-                if (!sem->read_bias_granted)
-			schedule();
+		if (sem->count > 0)
+			break;	/* We get the lock */
+		schedule();
 	}
 
-	remove_wait_queue(&sem->wait, &wait);
+	spin_lock(&sem->sentry);
+	if (sem->count <= 0)
+		goto lost_race;
+	__remove_wait_queue(&sem->wait, &wait);
 	current->state = TASK_RUNNING;
 }
-
 
-/* Wait for the lock to become unbiased. Since we're
- * a writer, we'll make ourselves exclusive.
+/* Wait for all previous readers to up the sempahore.
+ * Since we're a writer, we'll make ourselves exclusive.
  */
 void down_write_failed(struct rw_semaphore *sem)
 {
+	int count;
 	DECLARE_WAITQUEUE(wait, current);
 
-	__up_write(sem);	/* this takes care of granting the lock */
+	/* Indicate that we exist */
+	if (!waitqueue_active(&sem->wait))
+		sem->count -= RWSEM_BIAS;
 
-	add_wait_queue_exclusive(&sem->wait, &wait);
+	wait.flags = WQ_FLAG_EXCLUSIVE;
+	__add_wait_queue_tail(&sem->wait, &wait);
+lost_race:
+	spin_unlock(&sem->sentry);
 
-	while (atomic_read(&sem->count) < 0) {
+	for (;;) {
 		set_task_state(current, TASK_UNINTERRUPTIBLE);
-		if (atomic_read(&sem->count) >= 0)
-			break;	/* we must attempt to aquire or bias the lock */
+		if (sem->count == RWSEM_BIAS)
+			break;	/* We get the lock */
 		schedule();
 	}
-
-	remove_wait_queue(&sem->wait, &wait);
-	current->state = TASK_RUNNING;
-}
-
-void down_write_failed_biased(struct rw_semaphore *sem)
-{
-	DECLARE_WAITQUEUE(wait, current);
 
-	add_wait_queue_exclusive(&sem->write_bias_wait, &wait);	/* put ourselves at the end of the list */
-
-	for (;;) {
-		if (sem->write_bias_granted && xchg(&sem->write_bias_granted, 0))
-			break;
-		set_task_state(current, TASK_UNINTERRUPTIBLE);
-		if (!sem->write_bias_granted)
-			schedule();
+	spin_lock(&sem->sentry);
+	count = sem->count - RWSEM_BIAS;
+	if (count == 0) {
+		__remove_wait_queue(&sem->wait, &wait);
+		current->state = TASK_RUNNING;
+		return;
 	}
-
-	remove_wait_queue(&sem->write_bias_wait, &wait);
-	current->state = TASK_RUNNING;
-
-	/* if the lock is currently unbiased, awaken the sleepers
-	 * FIXME: this wakes up the readers early in a bit of a
-	 * stampede -> bad!
-	 */
-	if (atomic_read(&sem->count) >= 0)
-		wake_up(&sem->wait);
+	/* We lost the race.  Figure out who won and go back to sleep. */
+	if (sem->count > 0) {
+		/* The reader(s) won.  Bias the lock. */
+		sem->count = count;
+	} else {
+		/* Another writer slipped in ahead of us. */
+		sem->count = -1;
+	}
+	goto lost_race;
 }
 
-
-/* Called when someone has done an up that transitioned from
- * negative to non-negative, meaning that the lock has been
- * granted to whomever owned the bias.
- */
-void rwsem_wake_readers(struct rw_semaphore *sem)
+void up_read_wakeup(struct rw_semaphore *sem)
 {
-	if (xchg(&sem->read_bias_granted, 1))
-		BUG();
+	sem->count = RWSEM_BIAS;
 	wake_up(&sem->wait);
 }
 
-void rwsem_wake_writer(struct rw_semaphore *sem)
+void up_write_wakeup(struct rw_semaphore *sem)
 {
-	if (xchg(&sem->write_bias_granted, 1))
-		BUG();
-	wake_up(&sem->write_bias_wait);
+	sem->count = RWSEM_BIAS;
+	wake_up(&sem->wait);
 }