From d079e9c5ef5f751f1b6d4d6515034b1889fb48af Mon Sep 17 00:00:00 2001
From: Matthew Wilcox <matthew@wil.cx>
Date: Sun, 27 Jan 2008 19:20:13 -0500
Subject: [PATCH] arm: Implement down_killable

Signed-off-by: Matthew Wilcox <matthew@wil.cx>
---
 arch/arm/kernel/semaphore.c |   61 +++++++++++++++++++++++++++++++++++++++++++
 include/asm-arm/semaphore.h |    8 +++++
 2 files changed, 69 insertions(+), 0 deletions(-)

diff --git a/arch/arm/kernel/semaphore.c b/arch/arm/kernel/semaphore.c
index 981fe5c..4459c3f 100644
--- a/arch/arm/kernel/semaphore.c
+++ b/arch/arm/kernel/semaphore.c
@@ -140,6 +140,57 @@ int __sched __down_interruptible(struct semaphore * sem)
 	return retval;
 }
 
+int __sched __down_killable(struct semaphore * sem)
+{
+	int retval = 0;
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	tsk->state = TASK_KILLABLE;
+	add_wait_queue_exclusive(&sem->wait, &wait);
+
+	spin_lock_irq(&semaphore_lock);
+	sem->sleepers ++;
+	for (;;) {
+		int sleepers = sem->sleepers;
+
+		/*
+		 * With fatal signals pending, this turns into
+		 * the trylock failure case - we won't be
+		 * sleeping, and we can't get the lock as
+		 * it has contention. Just correct the count
+		 * and exit.
+		 */
+		if (fatal_signal_pending(current)) {
+			retval = -EINTR;
+			sem->sleepers = 0;
+			atomic_add(sleepers, &sem->count);
+			break;
+		}
+
+		/*
+		 * Add "everybody else" into it. They aren't
+		 * playing, because we own the spinlock. The
+		 * "-1" is because we're still hoping to get
+		 * the lock.
+		 */
+		if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+			sem->sleepers = 0;
+			break;
+		}
+		sem->sleepers = 1;	/* us - see -1 above */
+		spin_unlock_irq(&semaphore_lock);
+
+		schedule();
+		tsk->state = TASK_KILLABLE;
+		spin_lock_irq(&semaphore_lock);
+	}
+	spin_unlock_irq(&semaphore_lock);
+	tsk->state = TASK_RUNNING;
+	remove_wait_queue(&sem->wait, &wait);
+	wake_up(&sem->wait);
+	return retval;
+}
+
 /*
  * Trylock failed - make sure we correct for
  * having decremented the count.
@@ -198,6 +249,15 @@ __down_interruptible_failed:			\n\
 	ldmfd	sp!, {r0 - r4, pc}		\n\
 						\n\
 	.align	5				\n\
+	.globl	__down_killable_failed		\n\
+__down_killable_failed:				\n\
+	stmfd	sp!, {r0 - r4, lr}		\n\
+	mov	r0, ip				\n\
+	bl	__down_killable			\n\
+	mov	ip, r0				\n\
+	ldmfd	sp!, {r0 - r4, pc}		\n\
+						\n\
+	.align	5				\n\
 	.globl	__down_trylock_failed		\n\
 __down_trylock_failed:				\n\
 	stmfd	sp!, {r0 - r4, lr}		\n\
@@ -217,5 +277,6 @@ __up_wakeup:					\n\
 
 EXPORT_SYMBOL(__down_failed);
 EXPORT_SYMBOL(__down_interruptible_failed);
+EXPORT_SYMBOL(__down_killable_failed);
 EXPORT_SYMBOL(__down_trylock_failed);
 EXPORT_SYMBOL(__up_wakeup);
diff --git a/include/asm-arm/semaphore.h b/include/asm-arm/semaphore.h
index 1c8b441..0837049 100644
--- a/include/asm-arm/semaphore.h
+++ b/include/asm-arm/semaphore.h
@@ -51,11 +51,13 @@ static inline void init_MUTEX_LOCKED(struct semaphore *sem)
  */
 asmlinkage void __down_failed(void);
 asmlinkage int  __down_interruptible_failed(void);
+asmlinkage int  __down_killable_failed(void);
 asmlinkage int  __down_trylock_failed(void);
 asmlinkage void __up_wakeup(void);
 
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
+extern int  __down_killable(struct semaphore * sem);
 extern int  __down_trylock(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
@@ -79,6 +81,12 @@ static inline int down_interruptible (struct semaphore * sem)
 	return __down_op_ret(sem, __down_interruptible_failed);
 }
 
+static inline int down_killable (struct semaphore * sem)
+{
+	might_sleep();
+	return __down_op_ret(sem, __down_killable_failed);
+}
+
 static inline int down_trylock(struct semaphore *sem)
 {
 	return __down_op_ret(sem, __down_trylock_failed);
-- 
1.5.3.8


