[POWERPC] spufs: add support for nonschedulable contexts
[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 #include <asm/unistd.h>
6
7 #include "spufs.h"
8
9 /* interrupt-level stop callback function. */
10 void spufs_stop_callback(struct spu *spu)
11 {
12         struct spu_context *ctx = spu->ctx;
13
14         wake_up_all(&ctx->stop_wq);
15 }
16
17 void spufs_dma_callback(struct spu *spu, int type)
18 {
19         struct spu_context *ctx = spu->ctx;
20
21         if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
22                 ctx->event_return |= type;
23                 wake_up_all(&ctx->stop_wq);
24         } else {
25                 switch (type) {
26                 case SPE_EVENT_DMA_ALIGNMENT:
27                 case SPE_EVENT_INVALID_DMA:
28                         force_sig(SIGBUS, /* info, */ current);
29                         break;
30                 case SPE_EVENT_SPE_ERROR:
31                         force_sig(SIGILL, /* info */ current);
32                         break;
33                 }
34         }
35 }
36
37 static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
38 {
39         struct spu *spu;
40         u64 pte_fault;
41
42         *stat = ctx->ops->status_read(ctx);
43         if (ctx->state != SPU_STATE_RUNNABLE)
44                 return 1;
45         spu = ctx->spu;
46         pte_fault = spu->dsisr &
47             (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
48         return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
49 }
50
51 static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
52 {
53         int ret;
54         unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
55
56         if ((ret = spu_acquire_runnable(ctx)) != 0)
57                 return ret;
58
59         if (ctx->flags & SPU_CREATE_ISOLATE)
60                 runcntl |= SPU_RUNCNTL_ISOLATE;
61         else
62                 ctx->ops->npc_write(ctx, *npc);
63
64         ctx->ops->runcntl_write(ctx, runcntl);
65         return 0;
66 }
67
68 static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
69                                u32 * status)
70 {
71         int ret = 0;
72
73         *status = ctx->ops->status_read(ctx);
74         *npc = ctx->ops->npc_read(ctx);
75         spu_release(ctx);
76
77         if (signal_pending(current))
78                 ret = -ERESTARTSYS;
79         if (unlikely(current->ptrace & PT_PTRACED)) {
80                 if ((*status & SPU_STATUS_STOPPED_BY_STOP)
81                     && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
82                         force_sig(SIGTRAP, current);
83                         ret = -ERESTARTSYS;
84                 }
85         }
86         return ret;
87 }
88
89 static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
90                                          u32 *status)
91 {
92         int ret;
93
94         if ((ret = spu_run_fini(ctx, npc, status)) != 0)
95                 return ret;
96         if (*status & (SPU_STATUS_STOPPED_BY_STOP |
97                        SPU_STATUS_STOPPED_BY_HALT)) {
98                 return *status;
99         }
100         if ((ret = spu_run_init(ctx, npc)) != 0)
101                 return ret;
102         return 0;
103 }
104
105 /*
106  * SPU syscall restarting is tricky because we violate the basic
107  * assumption that the signal handler is running on the interrupted
108  * thread. Here instead, the handler runs on PowerPC user space code,
109  * while the syscall was called from the SPU.
110  * This means we can only do a very rough approximation of POSIX
111  * signal semantics.
112  */
113 int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
114                           unsigned int *npc)
115 {
116         int ret;
117
118         switch (*spu_ret) {
119         case -ERESTARTSYS:
120         case -ERESTARTNOINTR:
121                 /*
122                  * Enter the regular syscall restarting for
123                  * sys_spu_run, then restart the SPU syscall
124                  * callback.
125                  */
126                 *npc -= 8;
127                 ret = -ERESTARTSYS;
128                 break;
129         case -ERESTARTNOHAND:
130         case -ERESTART_RESTARTBLOCK:
131                 /*
132                  * Restart block is too hard for now, just return -EINTR
133                  * to the SPU.
134                  * ERESTARTNOHAND comes from sys_pause, we also return
135                  * -EINTR from there.
136                  * Assume that we need to be restarted ourselves though.
137                  */
138                 *spu_ret = -EINTR;
139                 ret = -ERESTARTSYS;
140                 break;
141         default:
142                 printk(KERN_WARNING "%s: unexpected return code %ld\n",
143                         __FUNCTION__, *spu_ret);
144                 ret = 0;
145         }
146         return ret;
147 }
148
149 int spu_process_callback(struct spu_context *ctx)
150 {
151         struct spu_syscall_block s;
152         u32 ls_pointer, npc;
153         char *ls;
154         long spu_ret;
155         int ret;
156
157         /* get syscall block from local store */
158         npc = ctx->ops->npc_read(ctx);
159         ls = ctx->ops->get_ls(ctx);
160         ls_pointer = *(u32*)(ls + npc);
161         if (ls_pointer > (LS_SIZE - sizeof(s)))
162                 return -EFAULT;
163         memcpy(&s, ls + ls_pointer, sizeof (s));
164
165         /* do actual syscall without pinning the spu */
166         ret = 0;
167         spu_ret = -ENOSYS;
168         npc += 4;
169
170         if (s.nr_ret < __NR_syscalls) {
171                 spu_release(ctx);
172                 /* do actual system call from here */
173                 spu_ret = spu_sys_callback(&s);
174                 if (spu_ret <= -ERESTARTSYS) {
175                         ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
176                 }
177                 spu_acquire(ctx);
178                 if (ret == -ERESTARTSYS)
179                         return ret;
180         }
181
182         /* write result, jump over indirect pointer */
183         memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
184         ctx->ops->npc_write(ctx, npc);
185         ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
186         return ret;
187 }
188
189 static inline int spu_process_events(struct spu_context *ctx)
190 {
191         struct spu *spu = ctx->spu;
192         u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
193         int ret = 0;
194
195         if (spu->dsisr & pte_fault)
196                 ret = spu_irq_class_1_bottom(spu);
197         if (spu->class_0_pending)
198                 ret = spu_irq_class_0_bottom(spu);
199         if (!ret && signal_pending(current))
200                 ret = -ERESTARTSYS;
201         return ret;
202 }
203
204 long spufs_run_spu(struct file *file, struct spu_context *ctx,
205                    u32 *npc, u32 *event)
206 {
207         int ret;
208         u32 status;
209
210         if (down_interruptible(&ctx->run_sema))
211                 return -ERESTARTSYS;
212
213         ctx->event_return = 0;
214         ret = spu_run_init(ctx, npc);
215         if (ret)
216                 goto out;
217
218         do {
219                 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
220                 if (unlikely(ret))
221                         break;
222                 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
223                     (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
224                         ret = spu_process_callback(ctx);
225                         if (ret)
226                                 break;
227                         status &= ~SPU_STATUS_STOPPED_BY_STOP;
228                 }
229                 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
230                         ret = spu_reacquire_runnable(ctx, npc, &status);
231                         if (ret)
232                                 goto out;
233                         continue;
234                 }
235                 ret = spu_process_events(ctx);
236
237         } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
238                                       SPU_STATUS_STOPPED_BY_HALT)));
239
240         ctx->ops->runcntl_stop(ctx);
241         ret = spu_run_fini(ctx, npc, &status);
242         if (!ret)
243                 ret = status;
244         spu_yield(ctx);
245
246 out:
247         *event = ctx->event_return;
248         up(&ctx->run_sema);
249         return ret;
250 }
251