cregit-Linux how code gets into the kernel

Release 4.14 arch/powerpc/platforms/cell/spufs/run.c

// SPDX-License-Identifier: GPL-2.0

#define DEBUG

#include <linux/wait.h>
#include <linux/ptrace.h>

#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/io.h>
#include <asm/unistd.h>

#include "spufs.h"

/* interrupt-level stop callback function. */

void spufs_stop_callback(struct spu *spu, int irq) { struct spu_context *ctx = spu->ctx; /* * 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(); wake_up_all(&ctx->stop_wq); } }

Contributors

PersonTokensPropCommitsCommitProp
Luke Browning4343.00%133.33%
Jeremy Kerr3030.00%133.33%
Arnd Bergmann2727.00%133.33%
Total100100.00%3100.00%


int spu_stopped(struct spu_context *ctx, u32 *stat) { u64 dsisr; u32 stopped; stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; 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; dsisr = ctx->csa.class_1_dsisr; if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) return 1; if (ctx->csa.class_0_pending) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Luke Browning6656.41%457.14%
Arnd Bergmann4437.61%114.29%
Jeremy Kerr65.13%114.29%
Bob Nelson10.85%114.29%
Total117100.00%7100.00%


static int spu_setup_isolated(struct spu_context *ctx) { int ret; u64 __iomem *mfc_cntl; u64 sr1; u32 status; unsigned long timeout; const u32 status_loading = SPU_STATUS_RUNNING | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; ret = -ENODEV; if (!isolated_loader) goto out; /* * 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); /* start the loader */ ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); ctx->ops->signal2_write(ctx, (unsigned long)isolated_loader & 0xffffffff); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); 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); out: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Jeremy Kerr34794.29%250.00%
Christoph Hellwig174.62%125.00%
Harvey Harrison41.09%125.00%
Total368100.00%4100.00%


static int spu_run_init(struct spu_context *ctx, u32 *npc) { unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; int ret; spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); /* * 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 { unsigned long privcntl; if (test_thread_flag(TIF_SINGLESTEP)) privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP; else privcntl = SPU_PRIVCNTL_MODE_NORMAL; ctx->ops->privcntl_write(ctx, privcntl); ctx->ops->npc_write(ctx, *npc); } ctx->ops->runcntl_write(ctx, runcntl); if (ctx->flags & SPU_CREATE_NOSCHED) { spuctx_switch_state(ctx, SPU_UTIL_USER); } else { if (ctx->state == SPU_STATE_SAVED) { ret = spu_activate(ctx, 0); if (ret) return ret; } else { spuctx_switch_state(ctx, SPU_UTIL_USER); } } set_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Luke Browning10640.00%323.08%
Jeremy Kerr8933.58%323.08%
Arnd Bergmann3513.21%215.38%
Andre Detsch145.28%17.69%
Benjamin Herrenschmidt93.40%17.69%
Mark Nutter72.64%17.69%
Christoph Hellwig51.89%215.38%
Total265100.00%13100.00%


static int spu_run_fini(struct spu_context *ctx, u32 *npc, u32 *status) { int ret = 0; spu_del_from_rq(ctx); *status = ctx->ops->status_read(ctx); *npc = ctx->ops->npc_read(ctx); spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status); spu_release(ctx); if (signal_pending(current)) ret = -ERESTARTSYS; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Arnd Bergmann6866.67%116.67%
Jeremy Kerr2221.57%350.00%
Andre Detsch76.86%116.67%
Luke Browning54.90%116.67%
Total102100.00%6100.00%

/* * 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. */
static int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, unsigned int *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; }

Contributors

PersonTokensPropCommitsCommitProp
Arnd Bergmann8897.78%133.33%
Harvey Harrison11.11%133.33%
Sebastian Siewior11.11%133.33%
Total90100.00%3100.00%


static int 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); /* write result, jump over indirect pointer */ memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret)); ctx->ops->npc_write(ctx, npc); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Arnd Bergmann20184.10%116.67%
Jeremy Kerr218.79%233.33%
Akinobu Mita156.28%116.67%
Sebastian Siewior10.42%116.67%
Rashmica Gupta10.42%116.67%
Total239100.00%6100.00%


long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) { int ret; struct spu *spu; 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; } spu = ctx->spu; if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))) { if (!(status & SPU_STATUS_STOPPED_BY_STOP)) { spu_switch_notify(spu, ctx); 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); if ((status & SPU_STATUS_STOPPED_BY_STOP) && (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) ctx->stats.libassist++; if ((ret == 0) || ((ret == -ERESTARTSYS) && ((status & SPU_STATUS_STOPPED_BY_HALT) || (status & SPU_STATUS_SINGLE_STEP) || ((status & SPU_STATUS_STOPPED_BY_STOP) && (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) ret = status; /* 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; else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { force_sig(SIGTRAP, current); ret = -ERESTARTSYS; } out: *event = ctx->event_return; out_unlock: mutex_unlock(&ctx->run_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Arnd Bergmann18040.27%523.81%
Christoph Hellwig7015.66%628.57%
Masato Noguchi6614.77%14.76%
Bob Nelson4710.51%14.76%
Jeremy Kerr4610.29%419.05%
Luke Browning224.92%14.76%
Benjamin Herrenschmidt81.79%14.76%
Andre Detsch71.57%14.76%
Michael Ellerman10.22%14.76%
Total447100.00%21100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Arnd Bergmann66037.61%613.33%
Jeremy Kerr56732.31%1226.67%
Luke Browning24213.79%715.56%
Christoph Hellwig925.24%817.78%
Masato Noguchi663.76%12.22%
Bob Nelson482.74%12.22%
Andre Detsch281.60%12.22%
Benjamin Herrenschmidt170.97%12.22%
Akinobu Mita150.85%12.22%
Mark Nutter70.40%12.22%
Harvey Harrison50.28%12.22%
Dave Jones30.17%12.22%
Sebastian Siewior20.11%12.22%
Greg Kroah-Hartman10.06%12.22%
Rashmica Gupta10.06%12.22%
Michael Ellerman10.06%12.22%
Total1755100.00%45100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.