[Cbe-oss-dev] PATCH [3/3] spusched: add asynchronous scheduling

Luke Browning lukebr at linux.vnet.ibm.com
Wed Oct 24 12:09:18 EST 2007


This patches adds asynchronous scheduling capabilities as a spu context 
is initialized and queued to the scheduler by the controlling thread.  
Thereafter, it is bound and started by the scheduler, which may happen
under the context of another thread.  
   
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	2007-10-23 20:07:19.000000000 -0300
+++ linux-2.6.22/arch/powerpc/platforms/cell/spufs/run.c	2007-10-23 20:18:52.000000000 -0300
@@ -21,18 +21,34 @@
 static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
 {
 	struct spu *spu;
-	u64 pte_fault;
+	u64 pte_fault, dsisr;
+	u32 stopped;
+
+	if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
+		return 1;
+
+	stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP |
+		SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP;
 
 	*stat = ctx->ops->status_read(ctx);
+	if (*stat & stopped)
+		return 1;
+
+	if (ctx->spu)
+		dsisr = ctx->spu->dsisr;
+	else
+		dsisr = ctx->csa.priv1.mfc_dsisr_RW;
+
+	pte_fault = dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
+	if (pte_fault)
+		return 1;
 
 	spu = ctx->spu;
-	if (ctx->state != SPU_STATE_RUNNABLE ||
-	    test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
+	if (!spu)
+		return 0;
+	if (spu->class_0_pending)
 		return 1;
-	pte_fault = spu->dsisr &
-	    (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
-	return (!(*stat & SPU_STATUS_RUNNING) || pte_fault || spu->class_0_pending) ?
-		1 : 0;
+	return 0;
 }
 
 static int spu_setup_isolated(struct spu_context *ctx)
@@ -128,24 +144,27 @@
 
 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)
@@ -153,17 +172,18 @@
 		}
 
 		/*
-		 * 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;
 
@@ -171,20 +191,17 @@
 			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;
@@ -349,8 +366,8 @@
 	}
 
 	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;
 
Index: linux-2.6.22/arch/powerpc/platforms/cell/spufs/sched.c
===================================================================
--- linux-2.6.22.orig/arch/powerpc/platforms/cell/spufs/sched.c	2007-10-23 20:06:50.000000000 -0300
+++ linux-2.6.22/arch/powerpc/platforms/cell/spufs/sched.c	2007-10-23 20:11:56.000000000 -0300
@@ -489,6 +489,13 @@
 	}
 }
 
+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;
@@ -507,6 +514,13 @@
 {
 	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)) {
@@ -606,6 +620,7 @@
 			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;
 		}
@@ -647,12 +662,8 @@
 			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;
 		}
 	}
@@ -660,6 +671,48 @@
 	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
@@ -671,40 +724,42 @@
  */
 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;
 }
 
 /**
@@ -746,21 +801,17 @@
 	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;
@@ -816,21 +867,14 @@
 
 		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++;
 	}





More information about the cbe-oss-dev mailing list