From 668f8c188520dade4a11772c8fd84359ee4ac0d3 Mon Sep 17 00:00:00 2001
From: Matthew Wilcox <matthew@wil.cx>
Date: Mon, 7 Jan 2008 09:28:54 -0500
Subject: [PATCH] x86: Implement down_killable

---
 arch/x86/kernel/i386_ksyms_32.c  |    1 +
 arch/x86/kernel/x8664_ksyms_64.c |    1 +
 arch/x86/lib/semaphore_32.S      |   21 ++++++++++++++
 arch/x86/lib/thunk_64.S          |    1 +
 include/asm-x86/semaphore_32.h   |   23 ++++++++++++++++
 include/asm-x86/semaphore_64.h   |   25 +++++++++++++++++
 lib/semaphore-sleepers.c         |   55 ++++++++++++++++++++++++++++++++++++++
 7 files changed, 127 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c
index 02112fc..24cd4e8 100644
--- a/arch/x86/kernel/i386_ksyms_32.c
+++ b/arch/x86/kernel/i386_ksyms_32.c
@@ -6,6 +6,7 @@
 
 EXPORT_SYMBOL(__down_failed);
 EXPORT_SYMBOL(__down_failed_interruptible);
+EXPORT_SYMBOL(__down_failed_killable);
 EXPORT_SYMBOL(__down_failed_trylock);
 EXPORT_SYMBOL(__up_wakeup);
 /* Networking helper routines. */
diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
index 77c25b3..915d61f 100644
--- a/arch/x86/kernel/x8664_ksyms_64.c
+++ b/arch/x86/kernel/x8664_ksyms_64.c
@@ -13,6 +13,7 @@ EXPORT_SYMBOL(kernel_thread);
 
 EXPORT_SYMBOL(__down_failed);
 EXPORT_SYMBOL(__down_failed_interruptible);
+EXPORT_SYMBOL(__down_failed_killable);
 EXPORT_SYMBOL(__down_failed_trylock);
 EXPORT_SYMBOL(__up_wakeup);
 
diff --git a/arch/x86/lib/semaphore_32.S b/arch/x86/lib/semaphore_32.S
index 444fba4..d5b33fa 100644
--- a/arch/x86/lib/semaphore_32.S
+++ b/arch/x86/lib/semaphore_32.S
@@ -72,6 +72,27 @@ ENTRY(__down_failed_interruptible)
 	CFI_ENDPROC
 	END(__down_failed_interruptible)
 
+ENTRY(__down_failed_killable)
+	CFI_STARTPROC
+	FRAME
+	pushl %edx
+	CFI_ADJUST_CFA_OFFSET 4
+	CFI_REL_OFFSET edx,0
+	pushl %ecx
+	CFI_ADJUST_CFA_OFFSET 4
+	CFI_REL_OFFSET ecx,0
+	call __down_killable
+	popl %ecx
+	CFI_ADJUST_CFA_OFFSET -4
+	CFI_RESTORE ecx
+	popl %edx
+	CFI_ADJUST_CFA_OFFSET -4
+	CFI_RESTORE edx
+	ENDFRAME
+	ret
+	CFI_ENDPROC
+	END(__down_failed_killable)
+
 ENTRY(__down_failed_trylock)
 	CFI_STARTPROC
 	FRAME
diff --git a/arch/x86/lib/thunk_64.S b/arch/x86/lib/thunk_64.S
index 6ea73f3..5800dc0 100644
--- a/arch/x86/lib/thunk_64.S
+++ b/arch/x86/lib/thunk_64.S
@@ -43,6 +43,7 @@
 	
 	thunk __down_failed,__down
 	thunk_retrax __down_failed_interruptible,__down_interruptible
+	thunk_retrax __down_failed_killable,__down_killable
 	thunk_retrax __down_failed_trylock,__down_trylock
 	thunk __up_wakeup,__up
 
diff --git a/include/asm-x86/semaphore_32.h b/include/asm-x86/semaphore_32.h
index 835c1d7..024679f 100644
--- a/include/asm-x86/semaphore_32.h
+++ b/include/asm-x86/semaphore_32.h
@@ -132,6 +132,29 @@ static inline int down_interruptible(struct semaphore * sem)
 }
 
 /*
+ * Killably try to acquire a semaphore.  If we obtained
+ * it, return zero.  If we were interrupted, returns -EINTR
+ */
+static inline int down_killable(struct semaphore * sem)
+{
+	int result;
+
+	might_sleep();
+	__asm__ __volatile__(
+		"# atomic killable down operation\n\t"
+		"xorl %0,%0\n\t"
+		LOCK_PREFIX "decl %1\n\t"     /* --sem->count */
+		"jns 2f\n\t"
+		"lea %1,%%eax\n\t"
+		"call __down_failed_killable\n"
+		"2:"
+		:"=&a" (result), "+m" (sem->count)
+		:
+		:"memory");
+	return result;
+}
+
+/*
  * Non-blockingly attempt to down() a semaphore.
  * Returns zero if we acquired it
  */
diff --git a/include/asm-x86/semaphore_64.h b/include/asm-x86/semaphore_64.h
index 7969430..66947d6 100644
--- a/include/asm-x86/semaphore_64.h
+++ b/include/asm-x86/semaphore_64.h
@@ -86,11 +86,13 @@ static inline void init_MUTEX_LOCKED (struct semaphore *sem)
 
 asmlinkage void __down_failed(void /* special register calling convention */);
 asmlinkage int  __down_failed_interruptible(void  /* params in registers */);
+asmlinkage int  __down_failed_killable(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_killable(struct semaphore * sem);
 asmlinkage int  __down_trylock(struct semaphore * sem);
 asmlinkage void __up(struct semaphore * sem);
 
@@ -138,6 +140,29 @@ static inline int down_interruptible(struct semaphore * sem)
 }
 
 /*
+ * Killable try to acquire a semaphore.  If we obtained
+ * it, return zero.  If we were interrupted, returns -EINTR
+ */
+static inline int down_killable(struct semaphore * sem)
+{
+	int result;
+
+	might_sleep();
+
+	__asm__ __volatile__(
+		"# atomic killable down operation\n\t"
+		"xorl %0,%0\n\t"
+		LOCK_PREFIX "decl %1\n\t"     /* --sem->count */
+		"jns 2f\n\t"
+		"call __down_failed_killable\n"
+		"2:\n"
+		:"=&a" (result), "=m" (sem->count)
+		:"D" (sem)
+		:"memory");
+	return result;
+}
+
+/*
  * Non-blockingly attempt to down() a semaphore.
  * Returns zero if we acquired it
  */
diff --git a/lib/semaphore-sleepers.c b/lib/semaphore-sleepers.c
index 1281805..ca040ee 100644
--- a/lib/semaphore-sleepers.c
+++ b/lib/semaphore-sleepers.c
@@ -145,6 +145,61 @@ fastcall int __sched __down_interruptible(struct semaphore * sem)
 	return retval;
 }
 
+fastcall int __sched __down_killable(struct semaphore * sem)
+{
+	int retval = 0;
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	unsigned long flags;
+
+	tsk->state = TASK_KILLABLE;
+	spin_lock_irqsave(&sem->wait.lock, flags);
+	add_wait_queue_exclusive_locked(&sem->wait, &wait);
+
+	sem->sleepers++;
+	for (;;) {
+		int sleepers = sem->sleepers;
+
+		/*
+		 * With 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 in
+		 * wait_queue_head. The "-1" is because we're
+		 * still hoping to get the semaphore.
+		 */
+		if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+			sem->sleepers = 0;
+			break;
+		}
+		sem->sleepers = 1;	/* us - see -1 above */
+		spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+		schedule();
+
+		spin_lock_irqsave(&sem->wait.lock, flags);
+		tsk->state = TASK_KILLABLE;
+	}
+	remove_wait_queue_locked(&sem->wait, &wait);
+	wake_up_locked(&sem->wait);
+	spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+	tsk->state = TASK_RUNNING;
+	return retval;
+}
+
 /*
  * Trylock failed - make sure we correct for
  * having decremented the count.
-- 
1.5.3.8


