powerpc/spufs: synchronize interaction between spu exception handling and time slicing

Time slicing can occur at the same time as spu exception handling
resulting in the wakeup of the wrong thread.

This change uses the the spu's register_lock to enforce synchronization
between bind/unbind and spu exception handling so that they are
mutually exclusive.

Signed-off-by: Luke Browning <lukebrowning@us.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index 96b5f0f..78f905b 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -219,15 +219,25 @@
 extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX
 static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
 {
+	int ret;
+
 	pr_debug("%s, %lx, %lx\n", __func__, dsisr, ea);
 
-	/* Handle kernel space hash faults immediately.
-	   User hash faults need to be deferred to process context. */
-	if ((dsisr & MFC_DSISR_PTE_NOT_FOUND)
-	    && REGION_ID(ea) != USER_REGION_ID
-	    && hash_page(ea, _PAGE_PRESENT, 0x300) == 0) {
-		spu_restart_dma(spu);
-		return 0;
+	/*
+	 * Handle kernel space hash faults immediately. User hash
+	 * faults need to be deferred to process context.
+	 */
+	if ((dsisr & MFC_DSISR_PTE_NOT_FOUND) &&
+	    (REGION_ID(ea) != USER_REGION_ID)) {
+
+		spin_unlock(&spu->register_lock);
+		ret = hash_page(ea, _PAGE_PRESENT, 0x300);
+		spin_lock(&spu->register_lock);
+
+		if (!ret) {
+			spu_restart_dma(spu);
+			return 0;
+		}
 	}
 
 	spu->class_1_dar = ea;
@@ -325,14 +335,12 @@
 
 	spu->class_0_pending |= stat;
 	spu->class_0_dar = spu_mfc_dar_get(spu);
-	spin_unlock(&spu->register_lock);
-
 	spu->stop_callback(spu, 0);
-
 	spu->class_0_pending = 0;
 	spu->class_0_dar = 0;
 
 	spu_int_stat_clear(spu, 0, stat);
+	spin_unlock(&spu->register_lock);
 
 	return IRQ_HANDLED;
 }
@@ -355,13 +363,12 @@
 		spu_mfc_dsisr_set(spu, 0ul);
 	spu_int_stat_clear(spu, 1, stat);
 
-	if (stat & CLASS1_SEGMENT_FAULT_INTR)
-		__spu_trap_data_seg(spu, dar);
-
-	spin_unlock(&spu->register_lock);
 	pr_debug("%s: %lx %lx %lx %lx\n", __func__, mask, stat,
 			dar, dsisr);
 
+	if (stat & CLASS1_SEGMENT_FAULT_INTR)
+		__spu_trap_data_seg(spu, dar);
+
 	if (stat & CLASS1_STORAGE_FAULT_INTR)
 		__spu_trap_data_map(spu, dar, dsisr);
 
@@ -374,6 +381,8 @@
 	spu->class_1_dsisr = 0;
 	spu->class_1_dar = 0;
 
+	spin_unlock(&spu->register_lock);
+
 	return stat ? IRQ_HANDLED : IRQ_NONE;
 }
 
@@ -392,14 +401,12 @@
 	mask = spu_int_mask_get(spu, 2);
 	/* ignore interrupts we're not waiting for */
 	stat &= mask;
-
 	/* mailbox interrupts are level triggered. mask them now before
 	 * acknowledging */
 	if (stat & mailbox_intrs)
 		spu_int_mask_and(spu, 2, ~(stat & mailbox_intrs));
 	/* acknowledge all interrupts before the callbacks */
 	spu_int_stat_clear(spu, 2, stat);
-	spin_unlock(&spu->register_lock);
 
 	pr_debug("class 2 interrupt %d, %lx, %lx\n", irq, stat, mask);
 
@@ -419,6 +426,9 @@
 		spu->wbox_callback(spu);
 
 	spu->stats.class2_intr++;
+
+	spin_unlock(&spu->register_lock);
+
 	return stat ? IRQ_HANDLED : IRQ_NONE;
 }