From e72c3715bbe5d454a53fe2b8f72819ab3025d5c0 Mon Sep 17 00:00:00 2001
From: Matthew Wilcox <matthew@wil.cx>
Date: Sun, 27 Jan 2008 22:57:30 -0500
Subject: [PATCH] sparc64: Implement down_killable

Signed-off-by: Matthew Wilcox <matthew@wil.cx>
---
 arch/sparc64/kernel/semaphore.c     |   68 +++++++++++++++++++++++++++++++++++
 arch/sparc64/kernel/sparc64_ksyms.c |    1 +
 include/asm-sparc64/semaphore.h     |    1 +
 3 files changed, 70 insertions(+), 0 deletions(-)

diff --git a/arch/sparc64/kernel/semaphore.c b/arch/sparc64/kernel/semaphore.c
index 9974a68..cb6f233 100644
--- a/arch/sparc64/kernel/semaphore.c
+++ b/arch/sparc64/kernel/semaphore.c
@@ -252,3 +252,71 @@ int __sched down_interruptible(struct semaphore *sem)
 	: "g1", "g2", "g3", "g7", "memory", "cc");
 	return ret;
 }
+
+static 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);
+
+	while (__sem_update_count(sem, -1) <= 0) {
+		if (fatal_signal_pending(current)) {
+			__sem_update_count(sem, 0);
+			retval = -EINTR;
+			break;
+		}
+		schedule();
+		tsk->state = TASK_KILLABLE;
+	}
+	tsk->state = TASK_RUNNING;
+	remove_wait_queue(&sem->wait, &wait);
+	wake_up(&sem->wait);
+	return retval;
+}
+
+int __sched down_killable(struct semaphore *sem)
+{
+	int ret = 0;
+	
+	might_sleep();
+	/* This atomically does:
+	 * 	old_val = sem->count;
+	 *	new_val = sem->count - 1;
+	 *	sem->count = new_val;
+	 *	if (old_val < 1)
+	 *		ret = __down_killable(sem);
+	 *
+	 * The (old_val < 1) test is equivalent to
+	 * the more straightforward (new_val < 0),
+	 * but it is easier to test the former because
+	 * of how the CAS instruction works.
+	 */
+
+	__asm__ __volatile__("\n"
+"	! down_killable sem(%2) ret(%0)\n"
+"1:	lduw	[%2], %%g1\n"
+"	sub	%%g1, 1, %%g7\n"
+"	cas	[%2], %%g1, %%g7\n"
+"	cmp	%%g1, %%g7\n"
+"	bne,pn	%%icc, 1b\n"
+"	 cmp	%%g7, 1\n"
+"	membar	#StoreLoad | #StoreStore\n"
+"	bl,pn	%%icc, 3f\n"
+"	 nop\n"
+"2:\n"
+"	.subsection 2\n"
+"3:	mov	%2, %%g1\n"
+"	save	%%sp, -160, %%sp\n"
+"	call	%3\n"
+"	 mov	%%g1, %%o0\n"
+"	ba,pt	%%xcc, 2b\n"
+"	 restore\n"
+"	.previous\n"
+	: "=r" (ret)
+	: "0" (ret), "r" (sem), "i" (__down_killable)
+	: "g1", "g2", "g3", "g7", "memory", "cc");
+	return ret;
+}
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 60765e3..78367b1 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -136,6 +136,7 @@ EXPORT_SYMBOL(sparc64_get_clock_tick);
 EXPORT_SYMBOL(down);
 EXPORT_SYMBOL(down_trylock);
 EXPORT_SYMBOL(down_interruptible);
+EXPORT_SYMBOL(down_killable);
 EXPORT_SYMBOL(up);
 
 /* RW semaphores */
diff --git a/include/asm-sparc64/semaphore.h b/include/asm-sparc64/semaphore.h
index 7f7c0c4..489d7e4 100644
--- a/include/asm-sparc64/semaphore.h
+++ b/include/asm-sparc64/semaphore.h
@@ -47,6 +47,7 @@ extern void up(struct semaphore *sem);
 extern void down(struct semaphore *sem);
 extern int down_trylock(struct semaphore *sem);
 extern int down_interruptible(struct semaphore *sem);
+extern int down_killable(struct semaphore *sem);
 
 #endif /* __KERNEL__ */
 
-- 
1.5.3.8


