]> err.no Git - linux-2.6/blob - arch/powerpc/platforms/cell/spufs/run.c
[POWERPC] spufs: make spu page faults not block scheduling
[linux-2.6] / arch / powerpc / platforms / cell / spufs / run.c
1 #define DEBUG
2
3 #include <linux/wait.h>
4 #include <linux/ptrace.h>
5
6 #include <asm/spu.h>
7 #include <asm/spu_priv1.h>
8 #include <asm/io.h>
9 #include <asm/unistd.h>
10
11 #include "spufs.h"
12
13 /* interrupt-level stop callback function. */
14 void spufs_stop_callback(struct spu *spu)
15 {
16         struct spu_context *ctx = spu->ctx;
17
18         wake_up_all(&ctx->stop_wq);
19 }
20
21 static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
22 {
23         struct spu *spu;
24         u64 pte_fault;
25
26         *stat = ctx->ops->status_read(ctx);
27         if (ctx->state != SPU_STATE_RUNNABLE)
28                 return 1;
29         spu = ctx->spu;
30         pte_fault = spu->dsisr &
31             (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
32         return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
33 }
34
35 static int spu_setup_isolated(struct spu_context *ctx)
36 {
37         int ret;
38         u64 __iomem *mfc_cntl;
39         u64 sr1;
40         u32 status;
41         unsigned long timeout;
42         const u32 status_loading = SPU_STATUS_RUNNING
43                 | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS;
44
45         ret = -ENODEV;
46         if (!isolated_loader)
47                 goto out;
48
49         /*
50          * We need to exclude userspace access to the context.
51          *
52          * To protect against memory access we invalidate all ptes
53          * and make sure the pagefault handlers block on the mutex.
54          */
55         spu_unmap_mappings(ctx);
56
57         mfc_cntl = &ctx->spu->priv2->mfc_control_RW;
58
59         /* purge the MFC DMA queue to ensure no spurious accesses before we
60          * enter kernel mode */
61         timeout = jiffies + HZ;
62         out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST);
63         while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK)
64                         != MFC_CNTL_PURGE_DMA_COMPLETE) {
65                 if (time_after(jiffies, timeout)) {
66                         printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n",
67                                         __FUNCTION__);
68                         ret = -EIO;
69                         goto out;
70                 }
71                 cond_resched();
72         }
73
74         /* put the SPE in kernel mode to allow access to the loader */
75         sr1 = spu_mfc_sr1_get(ctx->spu);
76         sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK;
77         spu_mfc_sr1_set(ctx->spu, sr1);
78
79         /* start the loader */
80         ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32);
81         ctx->ops->signal2_write(ctx,
82                         (unsigned long)isolated_loader & 0xffffffff);
83
84         ctx->ops->runcntl_write(ctx,
85                         SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
86
87         ret = 0;
88         timeout = jiffies + HZ;
89         while (((status = ctx->ops->status_read(ctx)) & status_loading) ==
90                                 status_loading) {
91                 if (time_after(jiffies, timeout)) {
92                         printk(KERN_ERR "%s: timeout waiting for loader\n",
93                                         __FUNCTION__);
94                         ret = -EIO;
95                         goto out_drop_priv;
96                 }
97                 cond_resched();
98         }
99
100         if (!(status & SPU_STATUS_RUNNING)) {
101                 /* If isolated LOAD has failed: run SPU, we will get a stop-and
102                  * signal later. */
103                 pr_debug("%s: isolated LOAD failed\n", __FUNCTION__);
104                 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
105                 ret = -EACCES;
106                 goto out_drop_priv;
107         }
108
109         if (!(status & SPU_STATUS_ISOLATED_STATE)) {
110                 /* This isn't allowed by the CBEA, but check anyway */
111                 pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__);
112                 ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
113                 ret = -EINVAL;
114                 goto out_drop_priv;
115         }
116
117 out_drop_priv:
118         /* Finished accessing the loader. Drop kernel mode */
119         sr1 |= MFC_STATE1_PROBLEM_STATE_MASK;
120         spu_mfc_sr1_set(ctx->spu, sr1);
121
122 out:
123         return ret;
124 }
125
126 static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
127 {
128         int ret;
129         unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
130
131         ret = spu_acquire_runnable(ctx, 0);
132         if (ret)
133                 return ret;
134
135         if (ctx->flags & SPU_CREATE_ISOLATE) {
136                 if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) {
137                         ret = spu_setup_isolated(ctx);
138                         if (ret)
139                                 spu_release(ctx);
140                 }
141
142                 /* if userspace has set the runcntrl register (eg, to issue an
143                  * isolated exit), we need to re-set it here */
144                 runcntl = ctx->ops->runcntl_read(ctx) &
145                         (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
146                 if (runcntl == 0)
147                         runcntl = SPU_RUNCNTL_RUNNABLE;
148         } else {
149                 spu_start_tick(ctx);
150                 ctx->ops->npc_write(ctx, *npc);
151         }
152
153         ctx->ops->runcntl_write(ctx, runcntl);
154         return ret;
155 }
156
157 static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
158                                u32 * status)
159 {
160         int ret = 0;
161
162         spu_stop_tick(ctx);
163         *status = ctx->ops->status_read(ctx);
164         *npc = ctx->ops->npc_read(ctx);
165         spu_release(ctx);
166
167         if (signal_pending(current))
168                 ret = -ERESTARTSYS;
169
170         return ret;
171 }
172
173 static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
174                                          u32 *status)
175 {
176         int ret;
177
178         if ((ret = spu_run_fini(ctx, npc, status)) != 0)
179                 return ret;
180         if (*status & (SPU_STATUS_STOPPED_BY_STOP |
181                        SPU_STATUS_STOPPED_BY_HALT)) {
182                 return *status;
183         }
184         if ((ret = spu_run_init(ctx, npc)) != 0)
185                 return ret;
186         return 0;
187 }
188
189 /*
190  * SPU syscall restarting is tricky because we violate the basic
191  * assumption that the signal handler is running on the interrupted
192  * thread. Here instead, the handler runs on PowerPC user space code,
193  * while the syscall was called from the SPU.
194  * This means we can only do a very rough approximation of POSIX
195  * signal semantics.
196  */
197 int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
198                           unsigned int *npc)
199 {
200         int ret;
201
202         switch (*spu_ret) {
203         case -ERESTARTSYS:
204         case -ERESTARTNOINTR:
205                 /*
206                  * Enter the regular syscall restarting for
207                  * sys_spu_run, then restart the SPU syscall
208                  * callback.
209                  */
210                 *npc -= 8;
211                 ret = -ERESTARTSYS;
212                 break;
213         case -ERESTARTNOHAND:
214         case -ERESTART_RESTARTBLOCK:
215                 /*
216                  * Restart block is too hard for now, just return -EINTR
217                  * to the SPU.
218                  * ERESTARTNOHAND comes from sys_pause, we also return
219                  * -EINTR from there.
220                  * Assume that we need to be restarted ourselves though.
221                  */
222                 *spu_ret = -EINTR;
223                 ret = -ERESTARTSYS;
224                 break;
225         default:
226                 printk(KERN_WARNING "%s: unexpected return code %ld\n",
227                         __FUNCTION__, *spu_ret);
228                 ret = 0;
229         }
230         return ret;
231 }
232
233 int spu_process_callback(struct spu_context *ctx)
234 {
235         struct spu_syscall_block s;
236         u32 ls_pointer, npc;
237         char *ls;
238         long spu_ret;
239         int ret;
240
241         /* get syscall block from local store */
242         npc = ctx->ops->npc_read(ctx);
243         ls = ctx->ops->get_ls(ctx);
244         ls_pointer = *(u32*)(ls + npc);
245         if (ls_pointer > (LS_SIZE - sizeof(s)))
246                 return -EFAULT;
247         memcpy(&s, ls + ls_pointer, sizeof (s));
248
249         /* do actual syscall without pinning the spu */
250         ret = 0;
251         spu_ret = -ENOSYS;
252         npc += 4;
253
254         if (s.nr_ret < __NR_syscalls) {
255                 spu_release(ctx);
256                 /* do actual system call from here */
257                 spu_ret = spu_sys_callback(&s);
258                 if (spu_ret <= -ERESTARTSYS) {
259                         ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
260                 }
261                 spu_acquire(ctx);
262                 if (ret == -ERESTARTSYS)
263                         return ret;
264         }
265
266         /* write result, jump over indirect pointer */
267         memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
268         ctx->ops->npc_write(ctx, npc);
269         ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
270         return ret;
271 }
272
273 static inline int spu_process_events(struct spu_context *ctx)
274 {
275         struct spu *spu = ctx->spu;
276         int ret = 0;
277
278         if (spu->class_0_pending)
279                 ret = spu_irq_class_0_bottom(spu);
280         if (!ret && signal_pending(current))
281                 ret = -ERESTARTSYS;
282         return ret;
283 }
284
285 long spufs_run_spu(struct file *file, struct spu_context *ctx,
286                    u32 *npc, u32 *event)
287 {
288         int ret;
289         u32 status;
290
291         if (down_interruptible(&ctx->run_sema))
292                 return -ERESTARTSYS;
293
294         ctx->ops->master_start(ctx);
295         ctx->event_return = 0;
296         ret = spu_run_init(ctx, npc);
297         if (ret)
298                 goto out;
299
300         do {
301                 ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
302                 if (unlikely(ret))
303                         break;
304                 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
305                     (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
306                         ret = spu_process_callback(ctx);
307                         if (ret)
308                                 break;
309                         status &= ~SPU_STATUS_STOPPED_BY_STOP;
310                 }
311                 ret = spufs_handle_class1(ctx);
312                 if (ret)
313                         break;
314
315                 if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
316                         ret = spu_reacquire_runnable(ctx, npc, &status);
317                         if (ret) {
318                                 spu_stop_tick(ctx);
319                                 goto out2;
320                         }
321                         continue;
322                 }
323                 ret = spu_process_events(ctx);
324
325         } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
326                                       SPU_STATUS_STOPPED_BY_HALT)));
327
328         ctx->ops->master_stop(ctx);
329         ret = spu_run_fini(ctx, npc, &status);
330         spu_yield(ctx);
331
332 out2:
333         if ((ret == 0) ||
334             ((ret == -ERESTARTSYS) &&
335              ((status & SPU_STATUS_STOPPED_BY_HALT) ||
336               ((status & SPU_STATUS_STOPPED_BY_STOP) &&
337                (status >> SPU_STOP_STATUS_SHIFT != 0x2104)))))
338                 ret = status;
339
340         if ((status & SPU_STATUS_STOPPED_BY_STOP)
341             && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
342                 force_sig(SIGTRAP, current);
343                 ret = -ERESTARTSYS;
344         }
345
346 out:
347         *event = ctx->event_return;
348         up(&ctx->run_sema);
349         return ret;
350 }