[Cbe-oss-dev] [PATCH] spusched: avoid false context switches in spu tick code

Luke Browning lukebr at linux.vnet.ibm.com
Fri Apr 6 03:16:49 EST 2007


This patch adds the run queue lock to the spu time slicing code 
so that it does not perform false context switches.  It is needed
to protect the runqueue prio bitmap.  Also, once it decides to
schedule a ctx on the runqueue, it removes it from the runqueue,
so that it is not visible to other spus. This is needed to avoid 
thundering herd problems when high priority ctxts are added to 
the runqueue. 

Signed-off-by: Luke Browning <lukebrowning at us.ibm.com>

Index: linux-2.6.21-rc4/arch/powerpc/platforms/cell/spufs/sched.c
===================================================================
--- linux-2.6.21-rc4.orig/arch/powerpc/platforms/cell/spufs/sched.c
2007-04-05 13:44:13.000000000 -0300
+++ linux-2.6.21-rc4/arch/powerpc/platforms/cell/spufs/sched.c
2007-04-05 14:02:43.000000000 -0300
@@ -69,68 +69,6 @@
 	return 1;
 }
 
-void spu_start_tick(struct spu_context *ctx)
-{
-	if (ctx->policy == SCHED_RR) {
-		/*
-		 * Make sure the exiting bit is cleared.
-		 */
-		clear_bit(SPU_SCHED_EXITING, &ctx->sched_flags);
-		mb();
-		queue_delayed_work(spu_sched_wq, &ctx->sched_work, SPU_TIMESLICE);
-	}
-}
-
-void spu_stop_tick(struct spu_context *ctx)
-{
-	if (ctx->policy == SCHED_RR) {
-		/*
-		 * While the work can be rearming normally setting this flag
-		 * makes sure it does not rearm itself anymore.
-		 */
-		set_bit(SPU_SCHED_EXITING, &ctx->sched_flags);
-		mb();
-		cancel_delayed_work(&ctx->sched_work);
-	}
-}
-
-void spu_sched_tick(struct work_struct *work)
-{
-	struct spu_context *ctx =
-		container_of(work, struct spu_context, sched_work.work);
-	struct spu *spu;
-	int preempted = 0;
-
-	/*
-	 * If this context is being stopped avoid rescheduling from the
-	 * scheduler tick because we would block on the state_mutex.
-	 * The caller will yield the spu later on anyway.
-	 */
-	if (test_bit(SPU_SCHED_EXITING, &ctx->sched_flags))
-		return;
-
-	mutex_lock(&ctx->state_mutex);
-	spu = ctx->spu;
-	if (spu) {
-		int best = sched_find_first_bit(spu_prio->bitmap);
-		if (best <= ctx->prio) {
-			spu_deactivate(ctx);
-			preempted = 1;
-		}
-	}
-	mutex_unlock(&ctx->state_mutex);
-
-	if (preempted) {
-		/*
-		 * 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);
-	} else
-		spu_start_tick(ctx);
-}
-
 /**
  * spu_add_to_active_list - add spu to active list
  * @spu:	spu to add to the active list
@@ -512,6 +450,87 @@
 	}
 }
 
+void spu_start_tick(struct spu_context *ctx)
+{
+	if (ctx->policy == SCHED_RR) {
+		/*
+		 * Make sure the exiting bit is cleared.
+		 */
+		clear_bit(SPU_SCHED_EXITING, &ctx->sched_flags);
+		mb();
+		queue_delayed_work(spu_sched_wq, &ctx->sched_work, SPU_TIMESLICE);
+	}
+}
+
+void spu_stop_tick(struct spu_context *ctx)
+{
+	if (ctx->policy == SCHED_RR) {
+		/*
+		 * While the work can be rearming normally setting this flag
+		 * makes sure it does not rearm itself anymore.
+		 */
+		set_bit(SPU_SCHED_EXITING, &ctx->sched_flags);
+		mb();
+		cancel_delayed_work(&ctx->sched_work);
+	}
+}
+
+void spu_sched_tick(struct work_struct *work)
+{
+	struct spu_context *ctx =
+		container_of(work, struct spu_context, sched_work.work);
+	struct spu_context *new = NULL;
+	int preempted = 0;
+	struct spu *spu;
+
+	/*
+	 * If this context is being stopped avoid rescheduling from the
+	 * scheduler tick because we would block on the state_mutex.
+	 * The caller will yield the spu later on anyway.
+	 */
+	if (test_bit(SPU_SCHED_EXITING, &ctx->sched_flags))
+		return;
+
+	mutex_lock(&ctx->state_mutex);
+	spu = ctx->spu;
+	if (spu) {
+		int best;
+
+		spin_lock(&spu_prio->runq_lock);
+		best = sched_find_first_bit(spu_prio->bitmap);
+		if (best <= ctx->prio) {
+			struct list_head *rq = &spu_prio->runq[best];
+
+			/* 
+			 * Remove target ctxt from the runqueue, so that 
+			 * other spus don't try to yield their spus to it 
+			 * also.  We are going to give it our spu.  
+			 */
+			BUG_ON(list_empty(rq));
+			preempted = 1;
+			new = list_entry(rq->next, struct spu_context, rq);
+			__spu_del_from_rq(new);
+		}
+		spin_unlock(&spu_prio->runq_lock);
+
+		if (preempted)
+			spu_unbind_context(spu, ctx);
+	}
+	mutex_unlock(&ctx->state_mutex);
+
+	if (preempted) {
+		/*
+		 * Free the spu.  Wakeup the target ctx.  And then, 
+		 * wakeup our context, so that it puts itself back 
+		 * on the runqueue.
+		 */
+		spu_free(spu);
+		wake_up(&new->stop_wq);
+		wake_up(&ctx->stop_wq);
+	} else
+		spu_start_tick(ctx);
+}
+
 int __init spu_sched_init(void)
 {
 	int i;





More information about the cbe-oss-dev mailing list