/** * spu_acquire_exclusive - lock spu contex and protect against userspace access * @ctx: spu contex to lock * * Note: * Returns 0 and with the context locked on success * Returns negative error and with the context _unlocked_ on failure. */ int spu_acquire_exclusive(struct spu_context *ctx) { int ret = -EINVAL; spu_acquire(ctx); /* * Context is about to be freed, so we can't acquire it anymore. */ if (!ctx->owner) goto out_unlock; if (ctx->state == SPU_STATE_SAVED) { ret = spu_activate(ctx, 0); if (ret) goto out_unlock; } else { /* * 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); } return 0; out_unlock: spu_release(ctx); return ret; }
int spufs_handle_class1(struct spu_context *ctx) { u64 ea, dsisr, access; unsigned long flags; unsigned flt = 0; int ret; ea = ctx->csa.class_1_dar; dsisr = ctx->csa.class_1_dsisr; if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) return 0; spuctx_switch_state(ctx, SPU_UTIL_IOWAIT); pr_debug("ctx %p: ea %016llx, dsisr %016llx state %d\n", ctx, ea, dsisr, ctx->state); ctx->stats.hash_flt++; if (ctx->state == SPU_STATE_RUNNABLE) ctx->spu->stats.hash_flt++; spu_release(ctx); access = (_PAGE_PRESENT | _PAGE_USER); access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL; local_irq_save(flags); ret = hash_page(ea, access, 0x300); local_irq_restore(flags); if (ret) ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); mutex_lock(&ctx->state_mutex); ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0; if (!ret) { if (flt & VM_FAULT_MAJOR) ctx->stats.maj_flt++; else ctx->stats.min_flt++; if (ctx->state == SPU_STATE_RUNNABLE) { if (flt & VM_FAULT_MAJOR) ctx->spu->stats.maj_flt++; else ctx->spu->stats.min_flt++; } if (ctx->spu) ctx->ops->restart_dma(ctx); } else spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); return ret; }
/* give up the mm reference when the context is about to be destroyed */ void spu_forget(struct spu_context *ctx) { struct mm_struct *mm; spu_acquire_saved(ctx); mm = ctx->owner; ctx->owner = NULL; mmput(mm); spu_release(ctx); }
/** * spu_release_saved - unlock spu context and return it to the runqueue * @ctx: context to unlock */ void spu_release_saved(struct spu_context *ctx) { BUG_ON(ctx->state != SPU_STATE_SAVED); if (test_and_clear_bit(SPU_SCHED_WAS_ACTIVE, &ctx->sched_flags) && test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags)) spu_activate(ctx, 0); spu_release(ctx); }
/* give up the mm reference when the context is about to be destroyed */ void spu_forget(struct spu_context *ctx) { struct mm_struct *mm; /* * This is basically an open-coded spu_acquire_saved, except that * we don't acquire the state mutex interruptible, and we don't * want this context to be rescheduled on release. */ mutex_lock(&ctx->state_mutex); if (ctx->state != SPU_STATE_SAVED) spu_deactivate(ctx); mm = ctx->owner; ctx->owner = NULL; mmput(mm); spu_release(ctx); }
/** * spu_acquire_runnable - lock spu contex and make sure it is in runnable state * @ctx: spu contex to lock * * Note: * Returns 0 and with the context locked on success * Returns negative error and with the context _unlocked_ on failure. */ int spu_acquire_runnable(struct spu_context *ctx, unsigned long flags) { int ret = -EINVAL; spu_acquire(ctx); if (ctx->state == SPU_STATE_SAVED) { /* * Context is about to be freed, so we can't acquire it anymore. */ if (!ctx->owner) goto out_unlock; ret = spu_activate(ctx, flags); if (ret) goto out_unlock; } return 0; out_unlock: spu_release(ctx); return ret; }
/* * bottom half handler for page faults, we can't do this from * interrupt context, since we might need to sleep. * we also need to give up the mutex so we can get scheduled * out while waiting for the backing store. * * TODO: try calling hash_page from the interrupt handler first * in order to speed up the easy case. */ int spufs_handle_class1(struct spu_context *ctx) { u64 ea, dsisr, access; unsigned long flags; unsigned flt = 0; int ret; /* * dar and dsisr get passed from the registers * to the spu_context, to this function, but not * back to the spu if it gets scheduled again. * * if we don't handle the fault for a saved context * in time, we can still expect to get the same fault * the immediately after the context restore. */ ea = ctx->csa.class_1_dar; dsisr = ctx->csa.class_1_dsisr; if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) return 0; spuctx_switch_state(ctx, SPU_UTIL_IOWAIT); pr_debug("ctx %p: ea %016llx, dsisr %016llx state %d\n", ctx, ea, dsisr, ctx->state); ctx->stats.hash_flt++; if (ctx->state == SPU_STATE_RUNNABLE) ctx->spu->stats.hash_flt++; /* we must not hold the lock when entering spu_handle_mm_fault */ spu_release(ctx); access = (_PAGE_PRESENT | _PAGE_USER); access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL; local_irq_save(flags); ret = hash_page(ea, access, 0x300); local_irq_restore(flags); /* hashing failed, so try the actual fault handler */ if (ret) ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); /* * 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); /* * Clear dsisr under ctxt lock after handling the fault, so that * time slicing will not preempt the context while the page fault * handler is running. Context switch code removes mappings. */ ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0; /* * If we handled the fault successfully and are in runnable * state, restart the DMA. * In case of unhandled error report the problem to user space. */ if (!ret) { if (flt & VM_FAULT_MAJOR) ctx->stats.maj_flt++; else ctx->stats.min_flt++; if (ctx->state == SPU_STATE_RUNNABLE) { if (flt & VM_FAULT_MAJOR) ctx->spu->stats.maj_flt++; else ctx->spu->stats.min_flt++; } if (ctx->spu) ctx->ops->restart_dma(ctx); } else spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); return ret; }