aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2007-12-20 02:39:59 -0500
committerPaul Mackerras <paulus@samba.org>2007-12-21 03:46:20 -0500
commitd6ad39bc53521275d14fde86bfb94d9b2ddb7a08 (patch)
tree07dcc592b343395cb7fbfb3053aa21103fb94352
parent8af30675c3e7b945bbaf6f57b724f246e56eb209 (diff)
[POWERPC] spufs: rework class 0 and 1 interrupt handling
Based on original patches from Arnd Bergmann <arnd.bergman@de.ibm.com>; and Luke Browning <lukebr@linux.vnet.ibm.com> Currently, spu contexts need to be loaded to the SPU in order to take class 0 and class 1 exceptions. This change makes the actual interrupt-handlers much simpler (ie, set the exception information in the context save area), and defers the handling code to the spufs_handle_class[01] functions, called from spufs_run_spu. This should improve the concurrency of the spu scheduling leading to greater SPU utilization when SPUs are overcommited. Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/platforms/cell/spu_base.c57
-rw-r--r--arch/powerpc/platforms/cell/spufs/fault.c107
-rw-r--r--arch/powerpc/platforms/cell/spufs/run.c50
-rw-r--r--arch/powerpc/platforms/cell/spufs/sched.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h1
-rw-r--r--arch/powerpc/platforms/cell/spufs/switch.c7
-rw-r--r--include/asm-powerpc/spu.h3
-rw-r--r--include/asm-powerpc/spu_csa.h2
8 files changed, 108 insertions, 121 deletions
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index a560277b3ad7..fb266ae32095 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -132,27 +132,6 @@ int spu_64k_pages_available(void)
132} 132}
133EXPORT_SYMBOL_GPL(spu_64k_pages_available); 133EXPORT_SYMBOL_GPL(spu_64k_pages_available);
134 134
135static int __spu_trap_invalid_dma(struct spu *spu)
136{
137 pr_debug("%s\n", __FUNCTION__);
138 spu->dma_callback(spu, SPE_EVENT_INVALID_DMA);
139 return 0;
140}
141
142static int __spu_trap_dma_align(struct spu *spu)
143{
144 pr_debug("%s\n", __FUNCTION__);
145 spu->dma_callback(spu, SPE_EVENT_DMA_ALIGNMENT);
146 return 0;
147}
148
149static int __spu_trap_error(struct spu *spu)
150{
151 pr_debug("%s\n", __FUNCTION__);
152 spu->dma_callback(spu, SPE_EVENT_SPE_ERROR);
153 return 0;
154}
155
156static void spu_restart_dma(struct spu *spu) 135static void spu_restart_dma(struct spu *spu)
157{ 136{
158 struct spu_priv2 __iomem *priv2 = spu->priv2; 137 struct spu_priv2 __iomem *priv2 = spu->priv2;
@@ -252,10 +231,12 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
252 return 1; 231 return 1;
253 } 232 }
254 233
234 spu->class_0_pending = 0;
255 spu->dar = ea; 235 spu->dar = ea;
256 spu->dsisr = dsisr; 236 spu->dsisr = dsisr;
257 mb(); 237
258 spu->stop_callback(spu); 238 spu->stop_callback(spu);
239
259 return 0; 240 return 0;
260} 241}
261 242
@@ -335,12 +316,13 @@ spu_irq_class_0(int irq, void *data)
335 316
336 spu = data; 317 spu = data;
337 318
319 spin_lock(&spu->register_lock);
338 mask = spu_int_mask_get(spu, 0); 320 mask = spu_int_mask_get(spu, 0);
339 stat = spu_int_stat_get(spu, 0); 321 stat = spu_int_stat_get(spu, 0) & mask;
340 stat &= mask;
341 322
342 spin_lock(&spu->register_lock);
343 spu->class_0_pending |= stat; 323 spu->class_0_pending |= stat;
324 spu->dsisr = spu_mfc_dsisr_get(spu);
325 spu->dar = spu_mfc_dar_get(spu);
344 spin_unlock(&spu->register_lock); 326 spin_unlock(&spu->register_lock);
345 327
346 spu->stop_callback(spu); 328 spu->stop_callback(spu);
@@ -350,31 +332,6 @@ spu_irq_class_0(int irq, void *data)
350 return IRQ_HANDLED; 332 return IRQ_HANDLED;
351} 333}
352 334
353int
354spu_irq_class_0_bottom(struct spu *spu)
355{
356 unsigned long flags;
357 unsigned long stat;
358
359 spin_lock_irqsave(&spu->register_lock, flags);
360 stat = spu->class_0_pending;
361 spu->class_0_pending = 0;
362
363 if (stat & CLASS0_DMA_ALIGNMENT_INTR)
364 __spu_trap_dma_align(spu);
365
366 if (stat & CLASS0_INVALID_DMA_COMMAND_INTR)
367 __spu_trap_invalid_dma(spu);
368
369 if (stat & CLASS0_SPU_ERROR_INTR)
370 __spu_trap_error(spu);
371
372 spin_unlock_irqrestore(&spu->register_lock, flags);
373
374 return (stat & CLASS0_INTR_MASK) ? -EIO : 0;
375}
376EXPORT_SYMBOL_GPL(spu_irq_class_0_bottom);
377
378static irqreturn_t 335static irqreturn_t
379spu_irq_class_1(int irq, void *data) 336spu_irq_class_1(int irq, void *data)
380{ 337{
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 @@
28 28
29#include "spufs.h" 29#include "spufs.h"
30 30
31static void spufs_handle_dma_error(struct spu_context *ctx, 31/**
32 * Handle an SPE event, depending on context SPU_CREATE_EVENTS_ENABLED flag.
33 *
34 * If the context was created with events, we just set the return event.
35 * Otherwise, send an appropriate signal to the process.
36 */
37static void spufs_handle_event(struct spu_context *ctx,
32 unsigned long ea, int type) 38 unsigned long ea, int type)
33{ 39{
40 siginfo_t info;
41
34 if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) { 42 if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
35 ctx->event_return |= type; 43 ctx->event_return |= type;
36 wake_up_all(&ctx->stop_wq); 44 wake_up_all(&ctx->stop_wq);
37 } else { 45 return;
38 siginfo_t info;
39 memset(&info, 0, sizeof(info));
40
41 switch (type) {
42 case SPE_EVENT_INVALID_DMA:
43 info.si_signo = SIGBUS;
44 info.si_code = BUS_OBJERR;
45 break;
46 case SPE_EVENT_SPE_DATA_STORAGE:
47 info.si_signo = SIGBUS;
48 info.si_addr = (void __user *)ea;
49 info.si_code = BUS_ADRERR;
50 break;
51 case SPE_EVENT_DMA_ALIGNMENT:
52 info.si_signo = SIGBUS;
53 /* DAR isn't set for an alignment fault :( */
54 info.si_code = BUS_ADRALN;
55 break;
56 case SPE_EVENT_SPE_ERROR:
57 info.si_signo = SIGILL;
58 info.si_addr = (void __user *)(unsigned long)
59 ctx->ops->npc_read(ctx) - 4;
60 info.si_code = ILL_ILLOPC;
61 break;
62 }
63 if (info.si_signo)
64 force_sig_info(info.si_signo, &info, current);
65 } 46 }
47
48 memset(&info, 0, sizeof(info));
49
50 switch (type) {
51 case SPE_EVENT_INVALID_DMA:
52 info.si_signo = SIGBUS;
53 info.si_code = BUS_OBJERR;
54 break;
55 case SPE_EVENT_SPE_DATA_STORAGE:
56 info.si_signo = SIGBUS;
57 info.si_addr = (void __user *)ea;
58 info.si_code = BUS_ADRERR;
59 break;
60 case SPE_EVENT_DMA_ALIGNMENT:
61 info.si_signo = SIGBUS;
62 /* DAR isn't set for an alignment fault :( */
63 info.si_code = BUS_ADRALN;
64 break;
65 case SPE_EVENT_SPE_ERROR:
66 info.si_signo = SIGILL;
67 info.si_addr = (void __user *)(unsigned long)
68 ctx->ops->npc_read(ctx) - 4;
69 info.si_code = ILL_ILLOPC;
70 break;
71 }
72
73 if (info.si_signo)
74 force_sig_info(info.si_signo, &info, current);
66} 75}
67 76
68void spufs_dma_callback(struct spu *spu, int type) 77int spufs_handle_class0(struct spu_context *ctx)
69{ 78{
70 spufs_handle_dma_error(spu->ctx, spu->dar, type); 79 unsigned long stat = ctx->csa.class_0_pending & CLASS0_INTR_MASK;
80
81 if (likely(!stat))
82 return 0;
83
84 if (stat & CLASS0_DMA_ALIGNMENT_INTR)
85 spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_DMA_ALIGNMENT);
86
87 if (stat & CLASS0_INVALID_DMA_COMMAND_INTR)
88 spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_INVALID_DMA);
89
90 if (stat & CLASS0_SPU_ERROR_INTR)
91 spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_SPE_ERROR);
92
93 return -EIO;
71} 94}
72 95
73/* 96/*
@@ -95,16 +118,8 @@ int spufs_handle_class1(struct spu_context *ctx)
95 * in time, we can still expect to get the same fault 118 * in time, we can still expect to get the same fault
96 * the immediately after the context restore. 119 * the immediately after the context restore.
97 */ 120 */
98 if (ctx->state == SPU_STATE_RUNNABLE) { 121 ea = ctx->csa.dar;
99 ea = ctx->spu->dar; 122 dsisr = ctx->csa.dsisr;
100 dsisr = ctx->spu->dsisr;
101 ctx->spu->dar= ctx->spu->dsisr = 0;
102 } else {
103 ea = ctx->csa.priv1.mfc_dar_RW;
104 dsisr = ctx->csa.priv1.mfc_dsisr_RW;
105 ctx->csa.priv1.mfc_dar_RW = 0;
106 ctx->csa.priv1.mfc_dsisr_RW = 0;
107 }
108 123
109 if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) 124 if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
110 return 0; 125 return 0;
@@ -132,6 +147,14 @@ int spufs_handle_class1(struct spu_context *ctx)
132 ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); 147 ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt);
133 148
134 spu_acquire(ctx); 149 spu_acquire(ctx);
150
151 /*
152 * Clear dsisr under ctxt lock after handling the fault, so that
153 * time slicing will not preempt the context while the page fault
154 * handler is running. Context switch code removes mappings.
155 */
156 ctx->csa.dar = ctx->csa.dsisr = 0;
157
135 /* 158 /*
136 * If we handled the fault successfully and are in runnable 159 * If we handled the fault successfully and are in runnable
137 * state, restart the DMA. 160 * state, restart the DMA.
@@ -152,7 +175,7 @@ int spufs_handle_class1(struct spu_context *ctx)
152 if (ctx->spu) 175 if (ctx->spu)
153 ctx->ops->restart_dma(ctx); 176 ctx->ops->restart_dma(ctx);
154 } else 177 } else
155 spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); 178 spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
156 179
157 spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); 180 spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
158 return ret; 181 return ret;
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c
index b3cc1dd72185..3b3de6c7ee5b 100644
--- a/arch/powerpc/platforms/cell/spufs/run.c
+++ b/arch/powerpc/platforms/cell/spufs/run.c
@@ -15,7 +15,30 @@ void spufs_stop_callback(struct spu *spu)
15{ 15{
16 struct spu_context *ctx = spu->ctx; 16 struct spu_context *ctx = spu->ctx;
17 17
18 wake_up_all(&ctx->stop_wq); 18 /*
19 * It should be impossible to preempt a context while an exception
20 * is being processed, since the context switch code is specially
21 * coded to deal with interrupts ... But, just in case, sanity check
22 * the context pointer. It is OK to return doing nothing since
23 * the exception will be regenerated when the context is resumed.
24 */
25 if (ctx) {
26 /* Copy exception arguments into module specific structure */
27 ctx->csa.class_0_pending = spu->class_0_pending;
28 ctx->csa.dsisr = spu->dsisr;
29 ctx->csa.dar = spu->dar;
30
31 /* ensure that the exception status has hit memory before a
32 * thread waiting on the context's stop queue is woken */
33 smp_wmb();
34
35 wake_up_all(&ctx->stop_wq);
36 }
37
38 /* Clear callback arguments from spu structure */
39 spu->class_0_pending = 0;
40 spu->dsisr = 0;
41 spu->dar = 0;
19} 42}
20 43
21static inline int spu_stopped(struct spu_context *ctx, u32 *stat) 44static inline int spu_stopped(struct spu_context *ctx, u32 *stat)
@@ -29,9 +52,9 @@ static inline int spu_stopped(struct spu_context *ctx, u32 *stat)
29 if (ctx->state != SPU_STATE_RUNNABLE || 52 if (ctx->state != SPU_STATE_RUNNABLE ||
30 test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) 53 test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
31 return 1; 54 return 1;
32 pte_fault = spu->dsisr & 55 pte_fault = ctx->csa.dsisr &
33 (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED); 56 (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
34 return (!(*stat & SPU_STATUS_RUNNING) || pte_fault || spu->class_0_pending) ? 57 return (!(*stat & SPU_STATUS_RUNNING) || pte_fault || ctx->csa.class_0_pending) ?
35 1 : 0; 58 1 : 0;
36} 59}
37 60
@@ -287,18 +310,6 @@ static int spu_process_callback(struct spu_context *ctx)
287 return ret; 310 return ret;
288} 311}
289 312
290static inline int spu_process_events(struct spu_context *ctx)
291{
292 struct spu *spu = ctx->spu;
293 int ret = 0;
294
295 if (spu->class_0_pending)
296 ret = spu_irq_class_0_bottom(spu);
297 if (!ret && signal_pending(current))
298 ret = -ERESTARTSYS;
299 return ret;
300}
301
302long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) 313long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
303{ 314{
304 int ret; 315 int ret;
@@ -364,13 +375,20 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
364 if (ret) 375 if (ret)
365 break; 376 break;
366 377
378 ret = spufs_handle_class0(ctx);
379 if (ret)
380 break;
381
367 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { 382 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
368 ret = spu_reacquire_runnable(ctx, npc, &status); 383 ret = spu_reacquire_runnable(ctx, npc, &status);
369 if (ret) 384 if (ret)
370 goto out2; 385 goto out2;
371 continue; 386 continue;
372 } 387 }
373 ret = spu_process_events(ctx); 388
389 if (signal_pending(current))
390 ret = -ERESTARTSYS;
391
374 392
375 } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | 393 } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
376 SPU_STATUS_STOPPED_BY_HALT | 394 SPU_STATUS_STOPPED_BY_HALT |
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 52215aa2f3c6..82ea576c53a3 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -245,7 +245,6 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx)
245 spu->wbox_callback = spufs_wbox_callback; 245 spu->wbox_callback = spufs_wbox_callback;
246 spu->stop_callback = spufs_stop_callback; 246 spu->stop_callback = spufs_stop_callback;
247 spu->mfc_callback = spufs_mfc_callback; 247 spu->mfc_callback = spufs_mfc_callback;
248 spu->dma_callback = spufs_dma_callback;
249 mb(); 248 mb();
250 spu_unmap_mappings(ctx); 249 spu_unmap_mappings(ctx);
251 spu_restore(&ctx->csa, spu); 250 spu_restore(&ctx->csa, spu);
@@ -433,7 +432,6 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
433 spu->wbox_callback = NULL; 432 spu->wbox_callback = NULL;
434 spu->stop_callback = NULL; 433 spu->stop_callback = NULL;
435 spu->mfc_callback = NULL; 434 spu->mfc_callback = NULL;
436 spu->dma_callback = NULL;
437 spu_associate_mm(spu, NULL); 435 spu_associate_mm(spu, NULL);
438 spu->pid = 0; 436 spu->pid = 0;
439 spu->tgid = 0; 437 spu->tgid = 0;
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index afdddbc8cf68..eaab1b239d02 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -222,6 +222,7 @@ void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx);
222 222
223/* fault handling */ 223/* fault handling */
224int spufs_handle_class1(struct spu_context *ctx); 224int spufs_handle_class1(struct spu_context *ctx);
225int spufs_handle_class0(struct spu_context *ctx);
225 226
226/* affinity */ 227/* affinity */
227struct spu *affinity_check(struct spu_context *ctx); 228struct spu *affinity_check(struct spu_context *ctx);
diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c
index c9600e8e0e16..1a7d7a0f66fc 100644
--- a/arch/powerpc/platforms/cell/spufs/switch.c
+++ b/arch/powerpc/platforms/cell/spufs/switch.c
@@ -2077,10 +2077,6 @@ int spu_save(struct spu_state *prev, struct spu *spu)
2077 int rc; 2077 int rc;
2078 2078
2079 acquire_spu_lock(spu); /* Step 1. */ 2079 acquire_spu_lock(spu); /* Step 1. */
2080 prev->dar = spu->dar;
2081 prev->dsisr = spu->dsisr;
2082 spu->dar = 0;
2083 spu->dsisr = 0;
2084 rc = __do_spu_save(prev, spu); /* Steps 2-53. */ 2080 rc = __do_spu_save(prev, spu); /* Steps 2-53. */
2085 release_spu_lock(spu); 2081 release_spu_lock(spu);
2086 if (rc != 0 && rc != 2 && rc != 6) { 2082 if (rc != 0 && rc != 2 && rc != 6) {
@@ -2107,9 +2103,6 @@ int spu_restore(struct spu_state *new, struct spu *spu)
2107 acquire_spu_lock(spu); 2103 acquire_spu_lock(spu);
2108 harvest(NULL, spu); 2104 harvest(NULL, spu);
2109 spu->slb_replace = 0; 2105 spu->slb_replace = 0;
2110 new->dar = 0;
2111 new->dsisr = 0;
2112 spu->class_0_pending = 0;
2113 rc = __do_spu_restore(new, spu); 2106 rc = __do_spu_restore(new, spu);
2114 release_spu_lock(spu); 2107 release_spu_lock(spu);
2115 if (rc) { 2108 if (rc) {
diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h
index 816e3dc1f66f..277460476aec 100644
--- a/include/asm-powerpc/spu.h
+++ b/include/asm-powerpc/spu.h
@@ -146,7 +146,6 @@ struct spu {
146 void (* ibox_callback)(struct spu *spu); 146 void (* ibox_callback)(struct spu *spu);
147 void (* stop_callback)(struct spu *spu); 147 void (* stop_callback)(struct spu *spu);
148 void (* mfc_callback)(struct spu *spu); 148 void (* mfc_callback)(struct spu *spu);
149 void (* dma_callback)(struct spu *spu, int type);
150 149
151 char irq_c0[8]; 150 char irq_c0[8];
152 char irq_c1[8]; 151 char irq_c1[8];
@@ -197,8 +196,6 @@ struct cbe_spu_info {
197extern struct cbe_spu_info cbe_spu_info[]; 196extern struct cbe_spu_info cbe_spu_info[];
198 197
199void spu_init_channels(struct spu *spu); 198void spu_init_channels(struct spu *spu);
200int spu_irq_class_0_bottom(struct spu *spu);
201int spu_irq_class_1_bottom(struct spu *spu);
202void spu_irq_setaffinity(struct spu *spu, int cpu); 199void spu_irq_setaffinity(struct spu *spu, int cpu);
203 200
204void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa, 201void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
diff --git a/include/asm-powerpc/spu_csa.h b/include/asm-powerpc/spu_csa.h
index 34da5c685170..0ab6bff86078 100644
--- a/include/asm-powerpc/spu_csa.h
+++ b/include/asm-powerpc/spu_csa.h
@@ -254,7 +254,7 @@ struct spu_state {
254 u64 spu_chnldata_RW[32]; 254 u64 spu_chnldata_RW[32];
255 u32 spu_mailbox_data[4]; 255 u32 spu_mailbox_data[4];
256 u32 pu_mailbox_data[1]; 256 u32 pu_mailbox_data[1];
257 u64 dar, dsisr; 257 u64 dar, dsisr, class_0_pending;
258 unsigned long suspend_time; 258 unsigned long suspend_time;
259 spinlock_t register_lock; 259 spinlock_t register_lock;
260}; 260};