--- 2.3.15/arch/i386/kernel/semaphore.c	Thu Aug 26 02:54:55 1999
+++ 2.3.15-semaphores/arch/i386/kernel/semaphore.c	Sun Aug 29 00:29:30 1999
@@ -49,28 +49,25 @@
 {
 	struct task_struct *tsk = current;
 	DECLARE_WAITQUEUE(wait, tsk);
-	tsk->state = TASK_UNINTERRUPTIBLE;
-	add_wait_queue(&sem->wait, &wait);
+	tsk->state = TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE;
+	add_wait_queue_exclusive(&sem->wait, &wait);
 
 	spin_lock_irq(&semaphore_lock);
 	sem->sleepers++;
 	for (;;) {
-		int sleepers = sem->sleepers;
-
 		/*
 		 * Add "everybody else" into it. They aren't
 		 * playing, because we own the spinlock.
 		 */
-		if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+		if (!atomic_add_negative(sem->sleepers - 1, &sem->count)) {
 			sem->sleepers = 0;
-			wake_up(&sem->wait);
 			break;
 		}
 		sem->sleepers = 1;	/* us - see -1 above */
 		spin_unlock_irq(&semaphore_lock);
 
 		schedule();
-		tsk->state = TASK_UNINTERRUPTIBLE;
+		tsk->state = TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE;
 		spin_lock_irq(&semaphore_lock);
 	}
 	spin_unlock_irq(&semaphore_lock);
@@ -80,17 +77,15 @@
 
 int __down_interruptible(struct semaphore * sem)
 {
-	int retval;
+	int retval = 0;
 	struct task_struct *tsk = current;
 	DECLARE_WAITQUEUE(wait, tsk);
-	tsk->state = TASK_INTERRUPTIBLE;
-	add_wait_queue(&sem->wait, &wait);
+	tsk->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE;
+	add_wait_queue_exclusive(&sem->wait, &wait);
 
 	spin_lock_irq(&semaphore_lock);
 	sem->sleepers ++;
 	for (;;) {
-		int sleepers = sem->sleepers;
-
 		/*
 		 * With signals pending, this turns into
 		 * the trylock failure case - we won't be
@@ -98,12 +93,14 @@
 		 * it has contention. Just correct the count
 		 * and exit.
 		 */
-		retval = -EINTR;
 		if (signal_pending(current)) {
+			retval = -EINTR;
+			/* sync the count with the sleepers before changin
+			   the sleepers field, everybody must enforce this
+			   rule. Remeber to undo the count decrement
+			   before giving up (no -1 here). */
+			atomic_add(sem->sleepers, &sem->count);
 			sem->sleepers = 0;
-			if (atomic_add_negative(sleepers, &sem->count))
-				break;
-			wake_up(&sem->wait);
 			break;
 		}
 
@@ -113,9 +110,7 @@
 		 * "-1" is because we're still hoping to get
 		 * the lock.
 		 */
-		if (!atomic_add_negative(sleepers - 1, &sem->count)) {
-			wake_up(&sem->wait);
-			retval = 0;
+		if (!atomic_add_negative(sem->sleepers - 1, &sem->count)) {
 			sem->sleepers = 0;
 			break;
 		}
@@ -123,12 +118,17 @@
 		spin_unlock_irq(&semaphore_lock);
 
 		schedule();
-		tsk->state = TASK_INTERRUPTIBLE;
+		tsk->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE;
 		spin_lock_irq(&semaphore_lock);
 	}
 	spin_unlock_irq(&semaphore_lock);
 	tsk->state = TASK_RUNNING;
 	remove_wait_queue(&sem->wait, &wait);
+	if (retval)
+		/* here we'll sure wakeup another waiter since we aren't
+		   in the waitqueue anymore. We need this additional wakeup
+		   because an up() may be happened while we was giving up. */
+		wake_up(&sem->wait);
 	return retval;
 }
 
@@ -142,21 +142,22 @@
  */
 int __down_trylock(struct semaphore * sem)
 {
-	int retval, sleepers;
+	int failed;
 
 	spin_lock_irq(&semaphore_lock);
-	sleepers = sem->sleepers + 1;
-	sem->sleepers = 0;
-
 	/*
-	 * Add "everybody else" and us into it. They aren't
+	 * Add "everybody else" into it. They aren't
 	 * playing, because we own the spinlock.
 	 */
-	if (!atomic_add_negative(sleepers, &sem->count))
-		wake_up(&sem->wait);
-
+	failed = atomic_add_negative(sem->sleepers, &sem->count);
+	sem->sleepers = 0;
+	if (failed)
+		/* we didn't got the semaphore so undo the count
+		   decrement. */
+		atomic_inc(&sem->count);
 	spin_unlock_irq(&semaphore_lock);
-	return 1;
+
+	return failed;
 }