Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[pandora-kernel.git] / arch / powerpc / platforms / cell / spufs / run.c
1 #include <linux/wait.h>
2 #include <linux/ptrace.h>
3
4 #include <asm/spu.h>
5
6 #include "spufs.h"
7
8 /* interrupt-level stop callback function. */
9 void spufs_stop_callback(struct spu *spu)
10 {
11         struct spu_context *ctx = spu->ctx;
12
13         wake_up_all(&ctx->stop_wq);
14 }
15
16 static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
17 {
18         struct spu *spu;
19         u64 pte_fault;
20
21         *stat = ctx->ops->status_read(ctx);
22         if (ctx->state != SPU_STATE_RUNNABLE)
23                 return 1;
24         spu = ctx->spu;
25         pte_fault = spu->dsisr &
26             (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
27         return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
28 }
29
30 static inline int spu_run_init(struct spu_context *ctx, u32 * npc,
31                                u32 * status)
32 {
33         int ret;
34
35         if ((ret = spu_acquire_runnable(ctx)) != 0)
36                 return ret;
37         ctx->ops->npc_write(ctx, *npc);
38         ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
39         return 0;
40 }
41
42 static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
43                                u32 * status)
44 {
45         int ret = 0;
46
47         *status = ctx->ops->status_read(ctx);
48         *npc = ctx->ops->npc_read(ctx);
49         spu_release(ctx);
50
51         if (signal_pending(current))
52                 ret = -ERESTARTSYS;
53         if (unlikely(current->ptrace & PT_PTRACED)) {
54                 if ((*status & SPU_STATUS_STOPPED_BY_STOP)
55                     && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
56                         force_sig(SIGTRAP, current);
57                         ret = -ERESTARTSYS;
58                 }
59         }
60         return ret;
61 }
62
63 static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
64                                          u32 *status)
65 {
66         int ret;
67
68         if ((ret = spu_run_fini(ctx, npc, status)) != 0)
69                 return ret;
70         if (*status & (SPU_STATUS_STOPPED_BY_STOP |
71                        SPU_STATUS_STOPPED_BY_HALT)) {
72                 return *status;
73         }
74         if ((ret = spu_run_init(ctx, npc, status)) != 0)
75                 return ret;
76         return 0;
77 }
78
79 static inline int spu_process_events(struct spu_context *ctx)
80 {
81         struct spu *spu = ctx->spu;
82         u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
83         int ret = 0;
84
85         if (spu->dsisr & pte_fault)
86                 ret = spu_irq_class_1_bottom(spu);
87         if (spu->class_0_pending)
88                 ret = spu_irq_class_0_bottom(spu);
89         if (!ret && signal_pending(current))
90                 ret = -ERESTARTSYS;
91         return ret;
92 }
93
94 long spufs_run_spu(struct file *file, struct spu_context *ctx,
95                    u32 * npc, u32 * status)
96 {
97         int ret;
98
99         if (down_interruptible(&ctx->run_sema))
100                 return -ERESTARTSYS;
101
102         ret = spu_run_init(ctx, npc, status);
103         if (ret)
104                 goto out;
105
106         do {
107                 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status));
108                 if (unlikely(ret))
109                         break;
110                 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
111                         ret = spu_reacquire_runnable(ctx, npc, status);
112                         if (ret)
113                                 goto out;
114                         continue;
115                 }
116                 ret = spu_process_events(ctx);
117
118         } while (!ret && !(*status & (SPU_STATUS_STOPPED_BY_STOP |
119                                       SPU_STATUS_STOPPED_BY_HALT)));
120
121         ctx->ops->runcntl_stop(ctx);
122         ret = spu_run_fini(ctx, npc, status);
123         if (!ret)
124                 ret = *status;
125         spu_yield(ctx);
126
127 out:
128         up(&ctx->run_sema);
129         return ret;
130 }
131