diff options
author | Arnd Bergmann <arnd@arndb.de> | 2006-10-04 11:26:14 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-10-04 19:21:01 -0400 |
commit | 9add11daeee2f6d69f6b86237f197824332a4a3b (patch) | |
tree | 526daebd5cac297c288503fe4dafecd02c0f492e | |
parent | 28347bce8a837258e737873a55d31f2f424a6ea6 (diff) |
[POWERPC] spufs: implement error event delivery to user space
This tries to fix spufs so we have an interface closer to what is
specified in the man page for events returned in the third argument of
spu_run.
Fortunately, libspe has never been using the returned contents of that
register, as they were the same as the return code of spu_run (duh!).
Unlike the specification that we never implemented correctly, we now
require a SPU_CREATE_EVENTS_ENABLED flag passed to spu_create, in
order to get the new behavior. When this flag is not passed, spu_run
will simply ignore the third argument now.
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/platforms/cell/spu_base.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/inode.c | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/run.c | 48 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/syscalls.c | 7 | ||||
-rw-r--r-- | include/asm-powerpc/spu.h | 14 |
7 files changed, 68 insertions, 24 deletions
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 5931973845b1..bfbd0455d8a7 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c | |||
@@ -46,21 +46,21 @@ EXPORT_SYMBOL_GPL(spu_priv1_ops); | |||
46 | static int __spu_trap_invalid_dma(struct spu *spu) | 46 | static int __spu_trap_invalid_dma(struct spu *spu) |
47 | { | 47 | { |
48 | pr_debug("%s\n", __FUNCTION__); | 48 | pr_debug("%s\n", __FUNCTION__); |
49 | force_sig(SIGBUS, /* info, */ current); | 49 | spu->dma_callback(spu, SPE_EVENT_INVALID_DMA); |
50 | return 0; | 50 | return 0; |
51 | } | 51 | } |
52 | 52 | ||
53 | static int __spu_trap_dma_align(struct spu *spu) | 53 | static int __spu_trap_dma_align(struct spu *spu) |
54 | { | 54 | { |
55 | pr_debug("%s\n", __FUNCTION__); | 55 | pr_debug("%s\n", __FUNCTION__); |
56 | force_sig(SIGBUS, /* info, */ current); | 56 | spu->dma_callback(spu, SPE_EVENT_DMA_ALIGNMENT); |
57 | return 0; | 57 | return 0; |
58 | } | 58 | } |
59 | 59 | ||
60 | static int __spu_trap_error(struct spu *spu) | 60 | static int __spu_trap_error(struct spu *spu) |
61 | { | 61 | { |
62 | pr_debug("%s\n", __FUNCTION__); | 62 | pr_debug("%s\n", __FUNCTION__); |
63 | force_sig(SIGILL, /* info, */ current); | 63 | spu->dma_callback(spu, SPE_EVENT_SPE_ERROR); |
64 | return 0; | 64 | return 0; |
65 | } | 65 | } |
66 | 66 | ||
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 3950ddccb2c8..8cc615ff3637 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c | |||
@@ -224,7 +224,8 @@ struct file_operations spufs_context_fops = { | |||
224 | }; | 224 | }; |
225 | 225 | ||
226 | static int | 226 | static int |
227 | spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | 227 | spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, |
228 | int mode) | ||
228 | { | 229 | { |
229 | int ret; | 230 | int ret; |
230 | struct inode *inode; | 231 | struct inode *inode; |
@@ -244,6 +245,8 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
244 | if (!ctx) | 245 | if (!ctx) |
245 | goto out_iput; | 246 | goto out_iput; |
246 | 247 | ||
248 | ctx->flags = flags; | ||
249 | |||
247 | inode->i_op = &spufs_dir_inode_operations; | 250 | inode->i_op = &spufs_dir_inode_operations; |
248 | inode->i_fop = &simple_dir_operations; | 251 | inode->i_fop = &simple_dir_operations; |
249 | ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); | 252 | ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); |
@@ -304,7 +307,7 @@ long spufs_create_thread(struct nameidata *nd, | |||
304 | goto out; | 307 | goto out; |
305 | 308 | ||
306 | /* all flags are reserved */ | 309 | /* all flags are reserved */ |
307 | if (flags) | 310 | if (flags & (~SPU_CREATE_FLAG_ALL)) |
308 | goto out; | 311 | goto out; |
309 | 312 | ||
310 | dentry = lookup_create(nd, 1); | 313 | dentry = lookup_create(nd, 1); |
@@ -317,7 +320,7 @@ long spufs_create_thread(struct nameidata *nd, | |||
317 | goto out_dput; | 320 | goto out_dput; |
318 | 321 | ||
319 | mode &= ~current->fs->umask; | 322 | mode &= ~current->fs->umask; |
320 | ret = spufs_mkdir(nd->dentry->d_inode, dentry, mode & S_IRWXUGO); | 323 | ret = spufs_mkdir(nd->dentry->d_inode, dentry, flags, mode & S_IRWXUGO); |
321 | if (ret) | 324 | if (ret) |
322 | goto out_dput; | 325 | goto out_dput; |
323 | 326 | ||
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 483c8b76232c..63df8cf4ba16 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c | |||
@@ -14,6 +14,26 @@ void spufs_stop_callback(struct spu *spu) | |||
14 | wake_up_all(&ctx->stop_wq); | 14 | wake_up_all(&ctx->stop_wq); |
15 | } | 15 | } |
16 | 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 | |||
17 | static inline int spu_stopped(struct spu_context *ctx, u32 * stat) | 37 | static inline int spu_stopped(struct spu_context *ctx, u32 * stat) |
18 | { | 38 | { |
19 | struct spu *spu; | 39 | struct spu *spu; |
@@ -28,8 +48,7 @@ static inline int spu_stopped(struct spu_context *ctx, u32 * stat) | |||
28 | return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0; | 48 | return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0; |
29 | } | 49 | } |
30 | 50 | ||
31 | static inline int spu_run_init(struct spu_context *ctx, u32 * npc, | 51 | static inline int spu_run_init(struct spu_context *ctx, u32 * npc) |
32 | u32 * status) | ||
33 | { | 52 | { |
34 | int ret; | 53 | int ret; |
35 | 54 | ||
@@ -72,7 +91,7 @@ static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc, | |||
72 | SPU_STATUS_STOPPED_BY_HALT)) { | 91 | SPU_STATUS_STOPPED_BY_HALT)) { |
73 | return *status; | 92 | return *status; |
74 | } | 93 | } |
75 | if ((ret = spu_run_init(ctx, npc, status)) != 0) | 94 | if ((ret = spu_run_init(ctx, npc)) != 0) |
76 | return ret; | 95 | return ret; |
77 | return 0; | 96 | return 0; |
78 | } | 97 | } |
@@ -177,46 +196,49 @@ static inline int spu_process_events(struct spu_context *ctx) | |||
177 | } | 196 | } |
178 | 197 | ||
179 | long spufs_run_spu(struct file *file, struct spu_context *ctx, | 198 | long spufs_run_spu(struct file *file, struct spu_context *ctx, |
180 | u32 * npc, u32 * status) | 199 | u32 *npc, u32 *event) |
181 | { | 200 | { |
182 | int ret; | 201 | int ret; |
202 | u32 status; | ||
183 | 203 | ||
184 | if (down_interruptible(&ctx->run_sema)) | 204 | if (down_interruptible(&ctx->run_sema)) |
185 | return -ERESTARTSYS; | 205 | return -ERESTARTSYS; |
186 | 206 | ||
187 | ret = spu_run_init(ctx, npc, status); | 207 | ctx->event_return = 0; |
208 | ret = spu_run_init(ctx, npc); | ||
188 | if (ret) | 209 | if (ret) |
189 | goto out; | 210 | goto out; |
190 | 211 | ||
191 | do { | 212 | do { |
192 | ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status)); | 213 | ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); |
193 | if (unlikely(ret)) | 214 | if (unlikely(ret)) |
194 | break; | 215 | break; |
195 | if ((*status & SPU_STATUS_STOPPED_BY_STOP) && | 216 | if ((status & SPU_STATUS_STOPPED_BY_STOP) && |
196 | (*status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { | 217 | (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { |
197 | ret = spu_process_callback(ctx); | 218 | ret = spu_process_callback(ctx); |
198 | if (ret) | 219 | if (ret) |
199 | break; | 220 | break; |
200 | *status &= ~SPU_STATUS_STOPPED_BY_STOP; | 221 | status &= ~SPU_STATUS_STOPPED_BY_STOP; |
201 | } | 222 | } |
202 | if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { | 223 | if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { |
203 | ret = spu_reacquire_runnable(ctx, npc, status); | 224 | ret = spu_reacquire_runnable(ctx, npc, &status); |
204 | if (ret) | 225 | if (ret) |
205 | goto out; | 226 | goto out; |
206 | continue; | 227 | continue; |
207 | } | 228 | } |
208 | ret = spu_process_events(ctx); | 229 | ret = spu_process_events(ctx); |
209 | 230 | ||
210 | } while (!ret && !(*status & (SPU_STATUS_STOPPED_BY_STOP | | 231 | } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | |
211 | SPU_STATUS_STOPPED_BY_HALT))); | 232 | SPU_STATUS_STOPPED_BY_HALT))); |
212 | 233 | ||
213 | ctx->ops->runcntl_stop(ctx); | 234 | ctx->ops->runcntl_stop(ctx); |
214 | ret = spu_run_fini(ctx, npc, status); | 235 | ret = spu_run_fini(ctx, npc, &status); |
215 | if (!ret) | 236 | if (!ret) |
216 | ret = *status; | 237 | ret = status; |
217 | spu_yield(ctx); | 238 | spu_yield(ctx); |
218 | 239 | ||
219 | out: | 240 | out: |
241 | *event = ctx->event_return; | ||
220 | up(&ctx->run_sema); | 242 | up(&ctx->run_sema); |
221 | return ret; | 243 | return ret; |
222 | } | 244 | } |
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index bd4e2c3d5d08..a824b6051164 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c | |||
@@ -81,7 +81,6 @@ static inline void bind_context(struct spu *spu, struct spu_context *ctx) | |||
81 | spu->number, spu->node); | 81 | spu->number, spu->node); |
82 | spu->ctx = ctx; | 82 | spu->ctx = ctx; |
83 | spu->flags = 0; | 83 | spu->flags = 0; |
84 | ctx->flags = 0; | ||
85 | ctx->spu = spu; | 84 | ctx->spu = spu; |
86 | ctx->ops = &spu_hw_ops; | 85 | ctx->ops = &spu_hw_ops; |
87 | spu->pid = current->pid; | 86 | spu->pid = current->pid; |
@@ -92,6 +91,7 @@ static inline void bind_context(struct spu *spu, struct spu_context *ctx) | |||
92 | spu->wbox_callback = spufs_wbox_callback; | 91 | spu->wbox_callback = spufs_wbox_callback; |
93 | spu->stop_callback = spufs_stop_callback; | 92 | spu->stop_callback = spufs_stop_callback; |
94 | spu->mfc_callback = spufs_mfc_callback; | 93 | spu->mfc_callback = spufs_mfc_callback; |
94 | spu->dma_callback = spufs_dma_callback; | ||
95 | mb(); | 95 | mb(); |
96 | spu_unmap_mappings(ctx); | 96 | spu_unmap_mappings(ctx); |
97 | spu_restore(&ctx->csa, spu); | 97 | spu_restore(&ctx->csa, spu); |
@@ -111,12 +111,12 @@ static inline void unbind_context(struct spu *spu, struct spu_context *ctx) | |||
111 | spu->wbox_callback = NULL; | 111 | spu->wbox_callback = NULL; |
112 | spu->stop_callback = NULL; | 112 | spu->stop_callback = NULL; |
113 | spu->mfc_callback = NULL; | 113 | spu->mfc_callback = NULL; |
114 | spu->dma_callback = NULL; | ||
114 | spu->mm = NULL; | 115 | spu->mm = NULL; |
115 | spu->pid = 0; | 116 | spu->pid = 0; |
116 | spu->prio = MAX_PRIO; | 117 | spu->prio = MAX_PRIO; |
117 | ctx->ops = &spu_backing_ops; | 118 | ctx->ops = &spu_backing_ops; |
118 | ctx->spu = NULL; | 119 | ctx->spu = NULL; |
119 | ctx->flags = 0; | ||
120 | spu->flags = 0; | 120 | spu->flags = 0; |
121 | spu->ctx = NULL; | 121 | spu->ctx = NULL; |
122 | } | 122 | } |
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 4485738e2102..2fb6a0099112 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h | |||
@@ -66,7 +66,8 @@ struct spu_context { | |||
66 | u32 tagwait; | 66 | u32 tagwait; |
67 | struct spu_context_ops *ops; | 67 | struct spu_context_ops *ops; |
68 | struct work_struct reap_work; | 68 | struct work_struct reap_work; |
69 | u64 flags; | 69 | unsigned long flags; |
70 | unsigned long event_return; | ||
70 | }; | 71 | }; |
71 | 72 | ||
72 | struct mfc_dma_command { | 73 | struct mfc_dma_command { |
@@ -183,5 +184,6 @@ void spufs_ibox_callback(struct spu *spu); | |||
183 | void spufs_wbox_callback(struct spu *spu); | 184 | void spufs_wbox_callback(struct spu *spu); |
184 | void spufs_stop_callback(struct spu *spu); | 185 | void spufs_stop_callback(struct spu *spu); |
185 | void spufs_mfc_callback(struct spu *spu); | 186 | void spufs_mfc_callback(struct spu *spu); |
187 | void spufs_dma_callback(struct spu *spu, int type); | ||
186 | 188 | ||
187 | #endif | 189 | #endif |
diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c index e6565a949ddc..ef33a0ca2036 100644 --- a/arch/powerpc/platforms/cell/spufs/syscalls.c +++ b/arch/powerpc/platforms/cell/spufs/syscalls.c | |||
@@ -38,7 +38,7 @@ static long do_spu_run(struct file *filp, | |||
38 | u32 npc, status; | 38 | u32 npc, status; |
39 | 39 | ||
40 | ret = -EFAULT; | 40 | ret = -EFAULT; |
41 | if (get_user(npc, unpc) || get_user(status, ustatus)) | 41 | if (get_user(npc, unpc)) |
42 | goto out; | 42 | goto out; |
43 | 43 | ||
44 | /* check if this file was created by spu_create */ | 44 | /* check if this file was created by spu_create */ |
@@ -49,7 +49,10 @@ static long do_spu_run(struct file *filp, | |||
49 | i = SPUFS_I(filp->f_dentry->d_inode); | 49 | i = SPUFS_I(filp->f_dentry->d_inode); |
50 | ret = spufs_run_spu(filp, i->i_ctx, &npc, &status); | 50 | ret = spufs_run_spu(filp, i->i_ctx, &npc, &status); |
51 | 51 | ||
52 | if (put_user(npc, unpc) || put_user(status, ustatus)) | 52 | if (put_user(npc, unpc)) |
53 | ret = -EFAULT; | ||
54 | |||
55 | if (ustatus && put_user(status, ustatus)) | ||
53 | ret = -EFAULT; | 56 | ret = -EFAULT; |
54 | out: | 57 | out: |
55 | return ret; | 58 | return ret; |
diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index f6c0a95e8209..87cc21e21946 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h | |||
@@ -138,6 +138,7 @@ struct spu { | |||
138 | void (* ibox_callback)(struct spu *spu); | 138 | void (* ibox_callback)(struct spu *spu); |
139 | void (* stop_callback)(struct spu *spu); | 139 | void (* stop_callback)(struct spu *spu); |
140 | void (* mfc_callback)(struct spu *spu); | 140 | void (* mfc_callback)(struct spu *spu); |
141 | void (* dma_callback)(struct spu *spu, int type); | ||
141 | 142 | ||
142 | char irq_c0[8]; | 143 | char irq_c0[8]; |
143 | char irq_c1[8]; | 144 | char irq_c1[8]; |
@@ -169,6 +170,19 @@ extern struct spufs_calls { | |||
169 | struct module *owner; | 170 | struct module *owner; |
170 | } spufs_calls; | 171 | } spufs_calls; |
171 | 172 | ||
173 | /* return status from spu_run, same as in libspe */ | ||
174 | #define SPE_EVENT_DMA_ALIGNMENT 0x0008 /*A DMA alignment error */ | ||
175 | #define SPE_EVENT_SPE_ERROR 0x0010 /*An illegal instruction error*/ | ||
176 | #define SPE_EVENT_SPE_DATA_SEGMENT 0x0020 /*A DMA segmentation error */ | ||
177 | #define SPE_EVENT_SPE_DATA_STORAGE 0x0040 /*A DMA storage error */ | ||
178 | #define SPE_EVENT_INVALID_DMA 0x0800 /* Invalid MFC DMA */ | ||
179 | |||
180 | /* | ||
181 | * Flags for sys_spu_create. | ||
182 | */ | ||
183 | #define SPU_CREATE_EVENTS_ENABLED 0x0001 | ||
184 | #define SPU_CREATE_FLAG_ALL 0x0001 /* mask of all valid flags */ | ||
185 | |||
172 | #ifdef CONFIG_SPU_FS_MODULE | 186 | #ifdef CONFIG_SPU_FS_MODULE |
173 | int register_spu_syscalls(struct spufs_calls *calls); | 187 | int register_spu_syscalls(struct spufs_calls *calls); |
174 | void unregister_spu_syscalls(struct spufs_calls *calls); | 188 | void unregister_spu_syscalls(struct spufs_calls *calls); |