[parisc-linux-cvs] Semaphores again

Matthew Wilcox willy@ldl.fc.hp.com
Sun, 18 Mar 2001 13:48:10 -0700


 * Update documentation to reflect the code.
 * Correct operation with multiple processes allowed to take a semaphore.

Index: arch/parisc/kernel/semaphore.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/semaphore.c,v
retrieving revision 1.7
diff -u -p -r1.7 semaphore.c
--- semaphore.c	2001/03/17 04:29:24	1.7
+++ semaphore.c	2001/03/18 20:38:36
@@ -1,36 +1,26 @@
 /*
- * Semaphore implementation Copyright (c) 2001 Matthew Wilcox
+ * Semaphore implementation Copyright (c) 2001 Matthew Wilcox, Hewlett-Packard
  */
 
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 
 /*
- * Semaphores are implemented using a two-way counter:
- * The "count" variable is decremented for each process
- * that tries to sleep, while the "waking" variable is
- * incremented when the "up()" code goes to wake up waiting
- * processes.
+ * Semaphores are complex as we wish to avoid using two variables.
+ * `count' has multiple roles, depending on its value.  If it is positive
+ * or zero, there are no waiters.  The functions here will never be
+ * called; see <asm/semaphore.h>
  *
- * Notably, the inline "up()" and "down()" functions can
- * efficiently test if they need to do any extra work (up
- * needs to do something only if count was negative before
- * the increment operation.
+ * When count is -1 it indicates there is at least one task waiting
+ * for the semaphore.
  *
- * When __up() is called, the count was negative before
- * incrementing it, and we need to wake up somebody.
+ * When count is less than that, there are '- count - 1' wakeups
+ * pending.  ie if it has value -3, there are 2 wakeups pending.
  *
- * This routine adds one to the count of processes that need to
- * wake up and exit.  ALL waiting processes actually wake up but
- * only the one that gets to the "waking" field first will gate
- * through and acquire the semaphore.  The others will go back
- * to sleep.
- *
- * Note that these functions are only called when there is
- * contention on the lock, and as such all this is the
- * "non-critical" part of the whole semaphore business. The
- * critical part is the inline stuff in <asm/semaphore.h>
- * where we want to avoid any extra jumps and calls.
+ * Note that these functions are only called when there is contention
+ * on the lock, and as such all this is the "non-critical" part of the
+ * whole semaphore business. The critical part is the inline stuff in
+ * <asm/semaphore.h> where we want to avoid any extra jumps and calls.
  */
 void __up(struct semaphore *sem)
 {
@@ -38,6 +28,8 @@ void __up(struct semaphore *sem)
 	wake_up(&sem->wait);
 }
 
+#define wakers(count) (-1 - count)
+
 #define DOWN_HEAD							\
 	DECLARE_WAITQUEUE(wait, current);				\
 									\
@@ -53,16 +45,17 @@ void __up(struct semaphore *sem)
 
 #define DOWN_TAIL							\
 	spin_lock_irq(&sem->sentry);					\
-	if (sem->count == -1)						\
+	if (wakers(sem->count) == 0)					\
 		goto lost_race;	/* Someone stole our wakeup */		\
 	__remove_wait_queue(&sem->wait, &wait);				\
 	current->state = TASK_RUNNING;					\
-	if (!waitqueue_active(&sem->wait)) {				\
-		sem->count = 0;						\
-	} else {							\
-		sem->count += 1;					\
-	}
+	if (!waitqueue_active(&sem->wait) && (sem->count < 0))		\
+		sem->count = wakers(sem->count);
 
+#define UPDATE_COUNT							\
+	sem->count += (sem->count < 0) ? 1 : - 1;
+	
+
 void __down(struct semaphore * sem)
 {
 	DOWN_HEAD
@@ -76,6 +69,7 @@ void __down(struct semaphore * sem)
  	}
 
 	DOWN_TAIL
+	UPDATE_COUNT
 }
 
 int __down_interruptible(struct semaphore * sem)
@@ -98,6 +92,10 @@ int __down_interruptible(struct semaphor
 	}
 
 	DOWN_TAIL
+
+	if (!ret) {
+		UPDATE_COUNT
+	}
 
 	return ret;
 }
Index: include/asm-parisc/semaphore.h
===================================================================
RCS file: /home/cvs/parisc/linux/include/asm-parisc/semaphore.h,v
retrieving revision 1.7
diff -u -p -r1.7 semaphore.h
--- semaphore.h	2001/03/17 04:29:25	1.7
+++ semaphore.h	2001/03/18 20:38:36
@@ -79,9 +79,11 @@ extern __inline__ void down(struct semap
 #endif
 
 	spin_lock_irq(&sem->sentry);
-	if (sem->count <= 0)
+	if (sem->count > 0) {
+		sem->count--;
+	} else {
 		__down(sem);
-	sem->count--;
+	}
 	spin_unlock_irq(&sem->sentry);
 }
 
@@ -93,10 +95,11 @@ extern __inline__ int down_interruptible
 #endif
 
 	spin_lock_irq(&sem->sentry);
-	if (sem->count <= 0)
-		ret = __down_interruptible(sem);
-	if (ret == 0)
+	if (sem->count > 0) {
 		sem->count--;
+	} else {
+		ret = __down_interruptible(sem);
+	}
 	spin_unlock_irq(&sem->sentry);
 	return ret;
 }