From 516826829b5b3686db1f7d149c3fdbc60fedb271 Mon Sep 17 00:00:00 2001
From: Matthew Wilcox <matthew@wil.cx>
Date: Sun, 27 Jan 2008 22:55:14 -0500
Subject: [PATCH] sparc: Implement down_killable

Signed-off-by: Matthew Wilcox <matthew@wil.cx>
---
 arch/sparc/kernel/semaphore.c   |   51 +++++++++++++++++++++++++++++++++++++++
 arch/sparc/kernel/sparc_ksyms.c |    1 +
 include/asm-sparc/semaphore.h   |   38 +++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+), 0 deletions(-)

diff --git a/arch/sparc/kernel/semaphore.c b/arch/sparc/kernel/semaphore.c
index 0c37c1a..f05e3a5 100644
--- a/arch/sparc/kernel/semaphore.c
+++ b/arch/sparc/kernel/semaphore.c
@@ -130,6 +130,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 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;
+			atomic24_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 (!atomic24_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.
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
index ef647ac..1386267 100644
--- a/arch/sparc/kernel/sparc_ksyms.c
+++ b/arch/sparc/kernel/sparc_ksyms.c
@@ -113,6 +113,7 @@ EXPORT_SYMBOL(__up);
 EXPORT_SYMBOL(__down);
 EXPORT_SYMBOL(__down_trylock);
 EXPORT_SYMBOL(__down_interruptible);
+EXPORT_SYMBOL(__down_killable);
 
 EXPORT_SYMBOL(sparc_valid_addr_bitmap);
 EXPORT_SYMBOL(phys_base);
diff --git a/include/asm-sparc/semaphore.h b/include/asm-sparc/semaphore.h
index 8018f9f..70f5ad7 100644
--- a/include/asm-sparc/semaphore.h
+++ b/include/asm-sparc/semaphore.h
@@ -46,6 +46,7 @@ static inline void init_MUTEX_LOCKED (struct semaphore *sem)
 
 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);
 
@@ -120,6 +121,43 @@ static inline int down_interruptible(struct semaphore * sem)
 	return increment;
 }
 
+static inline int down_killable(struct semaphore * sem)
+{
+	register volatile int *ptr asm("g1");
+	register int increment asm("g2");
+
+	might_sleep();
+
+	ptr = &(sem->count.counter);
+	increment = 1;
+
+	__asm__ __volatile__(
+	"mov	%%o7, %%g4\n\t"
+	"call	___atomic24_sub\n\t"
+	" add	%%o7, 8, %%o7\n\t"
+	"tst	%%g2\n\t"
+	"bl	2f\n\t"
+	" clr	%%g2\n"
+	"1:\n\t"
+	".subsection 2\n"
+	"2:\n\t"
+	"save	%%sp, -64, %%sp\n\t"
+	"mov	%%g1, %%l1\n\t"
+	"mov	%%g5, %%l5\n\t"
+	"call	%3\n\t"
+	" mov	%%g1, %%o0\n\t"
+	"mov	%%l1, %%g1\n\t"
+	"mov	%%l5, %%g5\n\t"
+	"ba	1b\n\t"
+	" restore %%o0, %%g0, %%g2\n\t"
+	".previous\n"
+	: "=&r" (increment)
+	: "0" (increment), "r" (ptr), "i" (__down_killable)
+	: "g3", "g4", "g7", "memory", "cc");
+
+	return increment;
+}
+
 static inline int down_trylock(struct semaphore * sem)
 {
 	register volatile int *ptr asm("g1");
-- 
1.5.3.8


