summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/cell/spufs/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/cell/spufs/fault.c')
-rw-r--r--arch/powerpc/platforms/cell/spufs/fault.c107
1 files changed, 65 insertions, 42 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c
index 0635f292ae19..720e111f1f6a 100644
--- a/arch/powerpc/platforms/cell/spufs/fault.c
+++ b/arch/powerpc/platforms/cell/spufs/fault.c
@@ -28,46 +28,69 @@
#include "spufs.h"
-static void spufs_handle_dma_error(struct spu_context *ctx,
+/**
+ * Handle an SPE event, depending on context SPU_CREATE_EVENTS_ENABLED flag.
+ *
+ * If the context was created with events, we just set the return event.
+ * Otherwise, send an appropriate signal to the process.
+ */
+static void spufs_handle_event(struct spu_context *ctx,
unsigned long ea, int type)
{
+ siginfo_t info;
+
if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
ctx->event_return |= type;
wake_up_all(&ctx->stop_wq);
- } else {
- siginfo_t info;
- memset(&info, 0, sizeof(info));
-
- switch (type) {
- case SPE_EVENT_INVALID_DMA:
- info.si_signo = SIGBUS;
- info.si_code = BUS_OBJERR;
- break;
- case SPE_EVENT_SPE_DATA_STORAGE:
- info.si_signo = SIGBUS;
- info.si_addr = (void __user *)ea;
- info.si_code = BUS_ADRERR;
- break;
- case SPE_EVENT_DMA_ALIGNMENT:
- info.si_signo = SIGBUS;
- /* DAR isn't set for an alignment fault :( */
- info.si_code = BUS_ADRALN;
- break;
- case SPE_EVENT_SPE_ERROR:
- info.si_signo = SIGILL;
- info.si_addr = (void __user *)(unsigned long)
- ctx->ops->npc_read(ctx) - 4;
- info.si_code = ILL_ILLOPC;
- break;
- }
- if (info.si_signo)
- force_sig_info(info.si_signo, &info, current);
+ return;
}
+
+ memset(&info, 0, sizeof(info));
+
+ switch (type) {
+ case SPE_EVENT_INVALID_DMA:
+ info.si_signo = SIGBUS;
+ info.si_code = BUS_OBJERR;
+ break;
+ case SPE_EVENT_SPE_DATA_STORAGE:
+ info.si_signo = SIGBUS;
+ info.si_addr = (void __user *)ea;
+ info.si_code = BUS_ADRERR;
+ break;
+ case SPE_EVENT_DMA_ALIGNMENT:
+ info.si_signo = SIGBUS;
+ /* DAR isn't set for an alignment fault :( */
+ info.si_code = BUS_ADRALN;
+ break;
+ case SPE_EVENT_SPE_ERROR:
+ info.si_signo = SIGILL;
+ info.si_addr = (void __user *)(unsigned long)
+ ctx->ops->npc_read(ctx) - 4;
+ info.si_code = ILL_ILLOPC;
+ break;
+ }
+
+ if (info.si_signo)
+ force_sig_info(info.si_signo, &info, current);
}
-void spufs_dma_callback(struct spu *spu, int type)
+int spufs_handle_class0(struct spu_context *ctx)
{
- spufs_handle_dma_error(spu->ctx, spu->dar, type);
+ unsigned long stat = ctx->csa.class_0_pending & CLASS0_INTR_MASK;
+
+ if (likely(!stat))
+ return 0;
+
+ if (stat & CLASS0_DMA_ALIGNMENT_INTR)
+ spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_DMA_ALIGNMENT);
+
+ if (stat & CLASS0_INVALID_DMA_COMMAND_INTR)
+ spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_INVALID_DMA);
+
+ if (stat & CLASS0_SPU_ERROR_INTR)
+ spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_SPE_ERROR);
+
+ return -EIO;
}
/*
@@ -95,16 +118,8 @@ int spufs_handle_class1(struct spu_context *ctx)
* in time, we can still expect to get the same fault
* the immediately after the context restore.
*/
- if (ctx->state == SPU_STATE_RUNNABLE) {
- ea = ctx->spu->dar;
- dsisr = ctx->spu->dsisr;
- ctx->spu->dar= ctx->spu->dsisr = 0;
- } else {
- ea = ctx->csa.priv1.mfc_dar_RW;
- dsisr = ctx->csa.priv1.mfc_dsisr_RW;
- ctx->csa.priv1.mfc_dar_RW = 0;
- ctx->csa.priv1.mfc_dsisr_RW = 0;
- }
+ ea = ctx->csa.dar;
+ dsisr = ctx->csa.dsisr;
if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
return 0;
@@ -132,6 +147,14 @@ int spufs_handle_class1(struct spu_context *ctx)
ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt);
spu_acquire(ctx);
+
+ /*
+ * Clear dsisr under ctxt lock after handling the fault, so that
+ * time slicing will not preempt the context while the page fault
+ * handler is running. Context switch code removes mappings.
+ */
+ ctx->csa.dar = ctx->csa.dsisr = 0;
+
/*
* If we handled the fault successfully and are in runnable
* state, restart the DMA.
@@ -152,7 +175,7 @@ int spufs_handle_class1(struct spu_context *ctx)
if (ctx->spu)
ctx->ops->restart_dma(ctx);
} else
- spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
+ spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
return ret;
OpenPOWER on IntegriCloud