/* * It should be impossible to preempt a context while an exception * is being processed, since the context switch code is specially * coded to deal with interrupts ... But, just in case, sanity check * the context pointer. It is OK to return doing nothing since * the exception will be regenerated when the context is resumed.
*/ if (ctx) { /* Copy exception arguments into module specific structure */ switch(irq) { case 0 :
ctx->csa.class_0_pending = spu->class_0_pending;
ctx->csa.class_0_dar = spu->class_0_dar; break; case 1 :
ctx->csa.class_1_dsisr = spu->class_1_dsisr;
ctx->csa.class_1_dar = spu->class_1_dar; break; case 2 : break;
}
/* ensure that the exception status has hit memory before a
* thread waiting on the context's stop queue is woken */
smp_wmb();
top:
*stat = ctx->ops->status_read(ctx); if (*stat & stopped) { /* * If the spu hasn't finished stopping, we need to * re-read the register to get the stopped value.
*/ if (*stat & SPU_STATUS_RUNNING) goto top; return 1;
}
if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) return 1;
/* * We need to exclude userspace access to the context. * * To protect against memory access we invalidate all ptes * and make sure the pagefault handlers block on the mutex.
*/
spu_unmap_mappings(ctx);
mfc_cntl = &ctx->spu->priv2->mfc_control_RW;
/* purge the MFC DMA queue to ensure no spurious accesses before we
* enter kernel mode */
timeout = jiffies + HZ;
out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK)
!= MFC_CNTL_PURGE_DMA_COMPLETE) { if (time_after(jiffies, timeout)) {
printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n",
__func__);
ret = -EIO; goto out;
}
cond_resched();
}
/* clear purge status */
out_be64(mfc_cntl, 0);
/* put the SPE in kernel mode to allow access to the loader */
sr1 = spu_mfc_sr1_get(ctx->spu);
sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK;
spu_mfc_sr1_set(ctx->spu, sr1);
ret = 0;
timeout = jiffies + HZ; while (((status = ctx->ops->status_read(ctx)) & status_loading) ==
status_loading) { if (time_after(jiffies, timeout)) {
printk(KERN_ERR "%s: timeout waiting for loader\n",
__func__);
ret = -EIO; goto out_drop_priv;
}
cond_resched();
}
if (!(status & SPU_STATUS_RUNNING)) { /* If isolated LOAD has failed: run SPU, we will get a stop-and
* signal later. */
pr_debug("%s: isolated LOAD failed\n", __func__);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
ret = -EACCES; goto out_drop_priv;
}
if (!(status & SPU_STATUS_ISOLATED_STATE)) { /* This isn't allowed by the CBEA, but check anyway */
pr_debug("%s: SPU fell out of isolated mode?\n", __func__);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
ret = -EINVAL; goto out_drop_priv;
}
out_drop_priv: /* Finished accessing the loader. Drop kernel mode */
sr1 |= MFC_STATE1_PROBLEM_STATE_MASK;
spu_mfc_sr1_set(ctx->spu, sr1);
/* * NOSCHED is synchronous scheduling with respect to the caller. * The caller waits for the context to be loaded.
*/ if (ctx->flags & SPU_CREATE_NOSCHED) { if (ctx->state == SPU_STATE_SAVED) {
ret = spu_activate(ctx, 0); if (ret) return ret;
}
}
/* * Apply special setup as required.
*/ if (ctx->flags & SPU_CREATE_ISOLATE) { if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) {
ret = spu_setup_isolated(ctx); if (ret) return ret;
}
/* * If userspace has set the runcntrl register (eg, to * issue an isolated exit), we need to re-set it here
*/
runcntl = ctx->ops->runcntl_read(ctx) &
(SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); if (runcntl == 0)
runcntl = SPU_RUNCNTL_RUNNABLE;
} else { unsignedlong privcntl;
if (test_thread_flag(TIF_SINGLESTEP))
privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP; else
privcntl = SPU_PRIVCNTL_MODE_NORMAL;
/* * SPU syscall restarting is tricky because we violate the basic * assumption that the signal handler is running on the interrupted * thread. Here instead, the handler runs on PowerPC user space code, * while the syscall was called from the SPU. * This means we can only do a very rough approximation of POSIX * signal semantics.
*/ staticint spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, unsignedint *npc)
{ int ret;
switch (*spu_ret) { case -ERESTARTSYS: case -ERESTARTNOINTR: /* * Enter the regular syscall restarting for * sys_spu_run, then restart the SPU syscall * callback.
*/
*npc -= 8;
ret = -ERESTARTSYS; break; case -ERESTARTNOHAND: case -ERESTART_RESTARTBLOCK: /* * Restart block is too hard for now, just return -EINTR * to the SPU. * ERESTARTNOHAND comes from sys_pause, we also return * -EINTR from there. * Assume that we need to be restarted ourselves though.
*/
*spu_ret = -EINTR;
ret = -ERESTARTSYS; break; default:
printk(KERN_WARNING "%s: unexpected return code %ld\n",
__func__, *spu_ret);
ret = 0;
} return ret;
}
staticint spu_process_callback(struct spu_context *ctx)
{ struct spu_syscall_block s;
u32 ls_pointer, npc; void __iomem *ls; long spu_ret; int ret;
/* get syscall block from local store */
npc = ctx->ops->npc_read(ctx) & ~3;
ls = (void __iomem *)ctx->ops->get_ls(ctx);
ls_pointer = in_be32(ls + npc); if (ls_pointer > (LS_SIZE - sizeof(s))) return -EFAULT;
memcpy_fromio(&s, ls + ls_pointer, sizeof(s));
/* do actual syscall without pinning the spu */
ret = 0;
spu_ret = -ENOSYS;
npc += 4;
if (s.nr_ret < NR_syscalls) {
spu_release(ctx); /* do actual system call from here */
spu_ret = spu_sys_callback(&s); if (spu_ret <= -ERESTARTSYS) {
ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
}
mutex_lock(&ctx->state_mutex); if (ret == -ERESTARTSYS) return ret;
}
/* need to re-get the ls, as it may have changed when we released the
* spu */
ls = (void __iomem *)ctx->ops->get_ls(ctx);
long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
{ int ret;
u32 status;
if (mutex_lock_interruptible(&ctx->run_mutex)) return -ERESTARTSYS;
ctx->event_return = 0;
ret = spu_acquire(ctx); if (ret) goto out_unlock;
spu_enable_spu(ctx);
spu_update_sched_info(ctx);
ret = spu_run_init(ctx, npc); if (ret) {
spu_release(ctx); goto out;
}
do {
ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); if (unlikely(ret)) { /* * This is nasty: we need the state_mutex for all the * bookkeeping even if the syscall was interrupted by * a signal. ewww.
*/
mutex_lock(&ctx->state_mutex); break;
} if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE,
&ctx->sched_flags))) { if (!(status & SPU_STATUS_STOPPED_BY_STOP)) continue;
}
spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
(status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
ret = spu_process_callback(ctx); if (ret) break;
status &= ~SPU_STATUS_STOPPED_BY_STOP;
}
ret = spufs_handle_class1(ctx); if (ret) break;
ret = spufs_handle_class0(ctx); if (ret) break;
if (signal_pending(current))
ret = -ERESTARTSYS;
} while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
SPU_STATUS_STOPPED_BY_HALT |
SPU_STATUS_SINGLE_STEP)));
spu_disable_spu(ctx);
ret = spu_run_fini(ctx, npc, &status);
spu_yield(ctx);
/* Note: we don't need to force_sig SIGTRAP on single-step * since we have TIF_SINGLESTEP set, thus the kernel will do * it upon return from the syscall anyway.
*/ if (unlikely(status & SPU_STATUS_SINGLE_STEP))
ret = -ERESTARTSYS;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.