[PATCH] spufs: Improved SPU preemptability.

This patch makes it easier to preempt an SPU context by
having the scheduler hold ctx->state_sema for much shorter
periods of time.

As part of this restructuring, the control logic for the "run"
operation is moved from arch/ppc64/kernel/spu_base.c to
fs/spufs/file.c.  Of course the base retains "bottom half"
handlers for class{0,1} irqs.  The new run loop will re-acquire
an SPU if preempted.

From: Mark Nutter <mnutter@us.ibm.com>
Signed-off-by: Arnd Bergmann <arndb@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index 167580c..8abd4bd 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -130,7 +130,8 @@
 	spu->dar = ea;
 	spu->dsisr = dsisr;
 	mb();
-	wake_up(&spu->stop_wq);
+	if (spu->stop_callback)
+		spu->stop_callback(spu);
 	return 0;
 }
 
@@ -151,7 +152,8 @@
 {
 	pr_debug("%s\n", __FUNCTION__);
 	spu->stop_code = in_be32(&spu->problem->spu_status_R);
-	wake_up(&spu->stop_wq);
+	if (spu->stop_callback)
+		spu->stop_callback(spu);
 	return 0;
 }
 
@@ -159,7 +161,8 @@
 {
 	pr_debug("%s\n", __FUNCTION__);
 	spu->stop_code = in_be32(&spu->problem->spu_status_R);
-	wake_up(&spu->stop_wq);
+	if (spu->stop_callback)
+		spu->stop_callback(spu);
 	return 0;
 }
 
@@ -190,12 +193,13 @@
 
 	spu = data;
 	spu->class_0_pending = 1;
-	wake_up(&spu->stop_wq);
+	if (spu->stop_callback)
+		spu->stop_callback(spu);
 
 	return IRQ_HANDLED;
 }
 
-static int
+int
 spu_irq_class_0_bottom(struct spu *spu)
 {
 	unsigned long stat;
@@ -214,8 +218,10 @@
 		__spu_trap_error(spu);
 
 	out_be64(&spu->priv1->int_stat_class0_RW, stat);
-	return 0;
+
+	return (stat & 0x7) ? -EIO : 0;
 }
+EXPORT_SYMBOL_GPL(spu_irq_class_0_bottom);
 
 static irqreturn_t
 spu_irq_class_1(int irq, void *data, struct pt_regs *regs)
@@ -250,6 +256,7 @@
 
 	return stat ? IRQ_HANDLED : IRQ_NONE;
 }
+EXPORT_SYMBOL_GPL(spu_irq_class_1_bottom);
 
 static irqreturn_t
 spu_irq_class_2(int irq, void *data, struct pt_regs *regs)
@@ -478,7 +485,7 @@
 	return -EFAULT;
 }
 
-static int spu_handle_pte_fault(struct spu *spu)
+int spu_irq_class_1_bottom(struct spu *spu)
 {
 	u64 ea, dsisr, access, error = 0UL;
 	int ret = 0;
@@ -508,76 +515,6 @@
 	return ret;
 }
 
-static inline int spu_pending(struct spu *spu, u32 * stat)
-{
-	struct spu_problem __iomem *prob = spu->problem;
-	u64 pte_fault;
-
-	*stat = in_be32(&prob->spu_status_R);
-	pte_fault = spu->dsisr &
-		    (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
-	return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
-}
-
-int spu_run(struct spu *spu)
-{
-	struct spu_problem __iomem *prob;
-	struct spu_priv1 __iomem *priv1;
-	struct spu_priv2 __iomem *priv2;
-	u32 status;
-	int ret;
-
-	prob = spu->problem;
-	priv1 = spu->priv1;
-	priv2 = spu->priv2;
-
-	/* Let SPU run.  */
-	eieio();
-	out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE);
-
-	do {
-		ret = wait_event_interruptible(spu->stop_wq,
-					       spu_pending(spu, &status));
-
-		if (spu->dsisr &
-		    (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))
-			ret = spu_handle_pte_fault(spu);
-
-		if (spu->class_0_pending)
-			spu_irq_class_0_bottom(spu);
-
-		if (!ret && signal_pending(current))
-			ret = -ERESTARTSYS;
-
-	} while (!ret && !(status &
-			   (SPU_STATUS_STOPPED_BY_STOP |
-			    SPU_STATUS_STOPPED_BY_HALT)));
-
-	/* Ensure SPU is stopped.  */
-	out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP);
-	eieio();
-	while (in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING)
-		cpu_relax();
-
-	out_be64(&priv2->slb_invalidate_all_W, 0);
-	out_be64(&priv1->tlb_invalidate_entry_W, 0UL);
-	eieio();
-
-	/* Check for SPU breakpoint.  */
-	if (unlikely(current->ptrace & PT_PTRACED)) {
-		status = in_be32(&prob->spu_status_R);
-
-		if ((status & SPU_STATUS_STOPPED_BY_STOP)
-		    && status >> SPU_STOP_STATUS_SHIFT == 0x3fff) {
-			force_sig(SIGTRAP, current);
-			ret = -ERESTARTSYS;
-		}
-	}
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(spu_run);
-
 static void __iomem * __init map_spe_prop(struct device_node *n,
 						 const char *name)
 {
@@ -693,9 +630,9 @@
 	out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1));
 	out_be64(&spu->priv1->mfc_sr1_RW, 0x33);
 
-	init_waitqueue_head(&spu->stop_wq);
 	spu->ibox_callback = NULL;
 	spu->wbox_callback = NULL;
+	spu->stop_callback = NULL;
 
 	down(&spu_mutex);
 	spu->number = number++;