[Cbe-oss-dev] PATCH [4/7] decouple spu scheduler from spufs_spu_run [asynchronous scheduling]
Luke Browning
lukebr at linux.vnet.ibm.com
Thu Nov 1 11:07:18 EST 2007
This patch changes spufs_spu_run so that the context is queued
directly to the scheduler and the controlling thread advances
directly to spufs_wait() for spe errors and exceptions.
Isolated (No-sched) contexts are treated the same as before.
Signed-off-by: Luke Browning <lukebr at linux.vnet.ibm.com>
---
Index: linux-2.6.22/arch/powerpc/platforms/cell/spufs/run.c
===================================================================
--- linux-2.6.22.orig/arch/powerpc/platforms/cell/spufs/run.c
+++ linux-2.6.22/arch/powerpc/platforms/cell/spufs/run.c
@@ -39,19 +39,27 @@ void spufs_stop_callback(struct spu *spu
static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
{
- struct spu *spu;
- u64 pte_fault;
+ u64 dsisr;
+ u32 stopped;
*stat = ctx->ops->status_read(ctx);
- spu = ctx->spu;
- if (ctx->state != SPU_STATE_RUNNABLE ||
- test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
+ if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
return 1;
- pte_fault = ctx->csa.dsisr &
- (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
- return (!(*stat & SPU_STATUS_RUNNING) || pte_fault || ctx->csa.class_0_pending) ?
- 1 : 0;
+
+ stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP |
+ SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP;
+ if (*stat & stopped)
+ return 1;
+
+ dsisr = ctx->csa.dsisr;
+ if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))
+ return 1;
+
+ if (ctx->csa.class_0_pending)
+ return 1;
+
+ return 0;
}
static int spu_setup_isolated(struct spu_context *ctx)
@@ -147,24 +155,27 @@ out:
static int spu_run_init(struct spu_context *ctx, u32 * npc)
{
- unsigned long runcntl;
+ unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
int ret;
spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM);
- if (ctx->flags & SPU_CREATE_ISOLATE) {
- /*
- * Force activation of spu. Isolated state assumes that
- * special loader context is loaded and running on spu.
- */
+ /*
+ * 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) {
- spu_set_timeslice(ctx);
-
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)
@@ -172,17 +183,18 @@ static int spu_run_init(struct spu_conte
}
/*
- * If userspace has set the runcntrl register (eg, to
- * issue an isolated exit), we need to re-set it here
+ * 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;
+ }
+ if (ctx->flags & SPU_CREATE_NOSCHED) {
spuctx_switch_state(ctx, SPUCTX_UTIL_USER);
ctx->ops->runcntl_write(ctx, runcntl);
-
} else {
unsigned long privcntl;
@@ -190,20 +202,17 @@ static int spu_run_init(struct spu_conte
privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP;
else
privcntl = SPU_PRIVCNTL_MODE_NORMAL;
- runcntl = SPU_RUNCNTL_RUNNABLE;
ctx->ops->npc_write(ctx, *npc);
ctx->ops->privcntl_write(ctx, privcntl);
+ spuctx_switch_state(ctx, SPUCTX_UTIL_USER);
+ ctx->ops->runcntl_write(ctx, runcntl);
if (ctx->state == SPU_STATE_SAVED) {
- spu_set_timeslice(ctx);
ret = spu_activate(ctx, 0);
if (ret)
return ret;
}
-
- spuctx_switch_state(ctx, SPUCTX_UTIL_USER);
- ctx->ops->runcntl_write(ctx, runcntl);
}
return 0;
@@ -222,9 +231,6 @@ static int spu_run_fini(struct spu_conte
spuctx_switch_state(ctx, SPUCTX_UTIL_LOADED);
spu_release(ctx);
- if (signal_pending(current))
- ret = -ERESTARTSYS;
-
return ret;
}
@@ -353,9 +359,9 @@ static inline int spu_handle_class0_even
ret = spu_handle_class0(ctx);
}
}
- if (!ret && signal_pending(current))
- ret = -ERESTARTSYS;
+
ctx->csa.class_0_pending = 0;
+
return ret;
}
@@ -383,8 +389,8 @@ long spufs_run_spu(struct file *file, st
}
do {
- ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
spu = ctx->spu;
+ ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
if (unlikely(ret))
break;
@@ -398,6 +404,7 @@ long spufs_run_spu(struct file *file, st
continue;
}
}
+
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
(status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
ret = spu_process_callback(ctx);
@@ -405,21 +412,31 @@ long spufs_run_spu(struct file *file, st
break;
status &= ~SPU_STATUS_STOPPED_BY_STOP;
}
+
ret = spu_handle_class1(ctx);
if (ret)
break;
+ ret = spu_handle_class0_events(ctx);
+ if (ret)
+ break;
+
+ if (status & (SPU_STATUS_STOPPED_BY_STOP |
+ SPU_STATUS_STOPPED_BY_HALT |
+ SPU_STATUS_SINGLE_STEP))
+ break;
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
ret = spu_reacquire_runnable(ctx, npc, &status);
if (ret)
goto out2;
- continue;
}
- ret = spu_handle_class0_events(ctx);
-
- } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
- SPU_STATUS_STOPPED_BY_HALT |
- SPU_STATUS_SINGLE_STEP)));
+ } while (1);
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
(((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100) &&
Index: linux-2.6.22/arch/powerpc/platforms/cell/spufs/sched.c
===================================================================
--- linux-2.6.22.orig/arch/powerpc/platforms/cell/spufs/sched.c
+++ linux-2.6.22/arch/powerpc/platforms/cell/spufs/sched.c
@@ -487,6 +487,13 @@ static void __spu_add_to_rq(struct spu_c
}
}
+static void spu_add_to_rq(struct spu_context *ctx)
+{
+ spin_lock(&spu_prio->runq_lock);
+ __spu_add_to_rq(ctx);
+ spin_unlock(&spu_prio->runq_lock);
+}
+
static void __spu_del_from_rq(struct spu_context *ctx)
{
int prio = ctx->prio;
@@ -505,6 +512,13 @@ static void spu_prio_wait(struct spu_con
{
DEFINE_WAIT(wait);
+ /*
+ * The caller must explicitly wait for a context to be loaded
+ * if the nosched flag is set. If NOSCHED is not set, the caller
+ * queues the context and waits for an spu event or error.
+ */
+ BUG_ON(!(ctx->flags & SPU_CREATE_NOSCHED));
+
spin_lock(&spu_prio->runq_lock);
prepare_to_wait_exclusive(&ctx->stop_wq, &wait, TASK_INTERRUPTIBLE);
if (!signal_pending(current)) {
@@ -604,6 +618,7 @@ static struct spu *find_victim(struct sp
struct spu_context *tmp = spu->ctx;
if (tmp && tmp->prio > ctx->prio &&
+ !(tmp->flags & SPU_CREATE_NOSCHED) &&
(!victim || tmp->prio > victim->prio))
victim = spu->ctx;
}
@@ -645,12 +660,8 @@ static struct spu *find_victim(struct sp
victim->stats.invol_ctx_switch++;
spu->stats.invol_ctx_switch++;
mutex_unlock(&victim->state_mutex);
- /*
- * We need to break out of the wait loop in spu_run
- * manually to ensure this context gets put on the
- * runqueue again ASAP.
- */
- wake_up(&victim->stop_wq);
+
+ spu_add_to_rq(victim);
return spu;
}
}
@@ -658,6 +669,48 @@ static struct spu *find_victim(struct sp
return NULL;
}
+static void __spu_schedule(struct spu *spu, struct spu_context *ctx)
+{
+ int node = spu->node;
+ int success = 0;
+
+ spu_set_timeslice(ctx);
+
+ mutex_lock(&cbe_spu_info[node].list_mutex);
+ if (spu->ctx == NULL) {
+ spu_bind_context(spu, ctx);
+ cbe_spu_info[node].nr_active++;
+ spu->alloc_state = SPU_USED;
+ success = 1;
+ }
+ mutex_unlock(&cbe_spu_info[node].list_mutex);
+
+ if (success)
+ wake_up_all(&ctx->run_wq);
+ else
+ __spu_add_to_rq(ctx);
+}
+
+static void spu_schedule(struct spu *spu, struct spu_context *ctx)
+{
+ spu_acquire(ctx);
+ __spu_schedule(spu, ctx);
+ spu_release(ctx);
+}
+
+static void spu_unschedule(struct spu *spu, struct spu_context *ctx)
+{
+ int node = spu->node;
+
+ mutex_lock(&cbe_spu_info[node].list_mutex);
+ cbe_spu_info[node].nr_active--;
+ spu->alloc_state = SPU_FREE;
+ spu_unbind_context(spu, ctx);
+ ctx->stats.invol_ctx_switch++;
+ spu->stats.invol_ctx_switch++;
+ mutex_unlock(&cbe_spu_info[node].list_mutex);
+}
+
/**
* spu_activate - find a free spu for a context and execute it
* @ctx: spu context to schedule
@@ -669,40 +722,42 @@ static struct spu *find_victim(struct sp
*/
int spu_activate(struct spu_context *ctx, unsigned long flags)
{
- do {
- struct spu *spu;
+ struct spu *spu;
- /*
- * If there are multiple threads waiting for a single context
- * only one actually binds the context while the others will
- * only be able to acquire the state_mutex once the context
- * already is in runnable state.
- */
- if (ctx->spu)
- return 0;
-
- spu = spu_get_idle(ctx);
- /*
- * If this is a realtime thread we try to get it running by
- * preempting a lower priority thread.
- */
- if (!spu && rt_prio(ctx->prio))
- spu = find_victim(ctx);
- if (spu) {
- int node = spu->node;
+ /*
+ * If there are multiple threads waiting for a single context
+ * only one actually binds the context while the others will
+ * only be able to acquire the state_mutex once the context
+ * already is in runnable state.
+ */
+ if (ctx->spu)
+ return 0;
- mutex_lock(&cbe_spu_info[node].list_mutex);
- spu_bind_context(spu, ctx);
- cbe_spu_info[node].nr_active++;
- mutex_unlock(&cbe_spu_info[node].list_mutex);
- wake_up_all(&ctx->run_wq);
- return 0;
- }
+spu_activate_top:
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spu = spu_get_idle(ctx);
+ /*
+ * If this is a realtime thread we try to get it running by
+ * preempting a lower priority thread.
+ */
+ if (!spu && rt_prio(ctx->prio))
+ spu = find_victim(ctx);
+ if (spu) {
+ __spu_schedule(spu, ctx);
+ return 0;
+ }
+
+ if (ctx->flags & SPU_CREATE_NOSCHED) {
spu_prio_wait(ctx);
- } while (!signal_pending(current));
+ goto spu_activate_top;
+ }
- return -ERESTARTSYS;
+ spu_add_to_rq(ctx);
+
+ return 0;
}
/**
@@ -744,21 +799,17 @@ static int __spu_deactivate(struct spu_c
if (spu) {
new = grab_runnable_context(max_prio, spu->node);
if (new || force) {
- int node = spu->node;
-
- mutex_lock(&cbe_spu_info[node].list_mutex);
- spu_unbind_context(spu, ctx);
- spu->alloc_state = SPU_FREE;
- cbe_spu_info[node].nr_active--;
- mutex_unlock(&cbe_spu_info[node].list_mutex);
-
- ctx->stats.vol_ctx_switch++;
- spu->stats.vol_ctx_switch++;
-
- if (new)
- wake_up(&new->stop_wq);
+ spu_unschedule(spu, ctx);
+ if (new) {
+ if (new->flags & SPU_CREATE_NOSCHED)
+ wake_up(&new->stop_wq);
+ else {
+ spu_release(ctx);
+ spu_schedule(spu, new);
+ spu_acquire(ctx);
+ }
+ }
}
-
}
return new != NULL;
@@ -814,21 +865,14 @@ static noinline void spusched_tick(struc
new = grab_runnable_context(ctx->prio + 1, spu->node);
if (new) {
- spu_unbind_context(spu, ctx);
- ctx->stats.invol_ctx_switch++;
- spu->stats.invol_ctx_switch++;
- spu->alloc_state = SPU_FREE;
- cbe_spu_info[spu->node].nr_active--;
- wake_up(&new->stop_wq);
- /*
- * We need to break out of the wait loop in
- * spu_run manually to ensure this context
- * gets put on the runqueue again ASAP.
- */
- wake_up(&ctx->stop_wq);
+ spu_unschedule(spu, ctx);
+ spu_add_to_rq(ctx);
+ } else {
+ spu_set_timeslice(ctx);
}
- spu_set_timeslice(ctx);
mutex_unlock(&ctx->state_mutex);
+ if (new)
+ spu_schedule(spu, new);
} else {
ctx->time_slice++;
}
Index: linux-2.6.22/arch/powerpc/platforms/cell/spufs/file.c
===================================================================
--- linux-2.6.22.orig/arch/powerpc/platforms/cell/spufs/file.c
+++ linux-2.6.22/arch/powerpc/platforms/cell/spufs/file.c
@@ -572,6 +572,9 @@ void spufs_ibox_callback(struct spu *spu
{
struct spu_context *ctx = spu->ctx;
+ if (!ctx)
+ return;
+
wake_up_all(&ctx->ibox_wq);
kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
}
@@ -708,6 +711,9 @@ void spufs_wbox_callback(struct spu *spu
{
struct spu_context *ctx = spu->ctx;
+ if (!ctx)
+ return;
+
wake_up_all(&ctx->wbox_wq);
kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
}
@@ -1320,6 +1326,9 @@ void spufs_mfc_callback(struct spu *spu)
{
struct spu_context *ctx = spu->ctx;
+ if (!ctx)
+ return;
+
wake_up_all(&ctx->mfc_wq);
pr_debug("%s %s\n", __FUNCTION__, spu->name);
More information about the cbe-oss-dev
mailing list