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