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