[Cbe-oss-dev] [PATCH] spusched: extend list_mutex coverage
Christoph Hellwig
hch at lst.de
Wed Jul 23 18:50:53 EST 2008
spu->ctx is changed under the list_mutex and state_mutex of the loaded
context. To guarantee that a context doesn't go away or get unloaded
we need to make sure to lock the state mutex without previously
unlocking the list_mutex when traversing the spu lists in find_victim
and spusched_tick.
That means we have to switch to trylock operations on state_mutex in these
two functions and opencode spu_schedule in spusched_tick. Both is
rather ugly, but I can't find a good way around it.
Signed-off-by: Christoph Hellwig <hch at lst.de>
Index: linux-2.6/arch/powerpc/platforms/cell/spufs/sched.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/sched.c 2008-07-23 10:18:04.000000000 +0200
+++ linux-2.6/arch/powerpc/platforms/cell/spufs/sched.c 2008-07-23 10:45:22.000000000 +0200
@@ -633,7 +633,6 @@ static struct spu *find_victim(struct sp
(!victim || tmp->prio > victim->prio))
victim = spu->ctx;
}
- mutex_unlock(&cbe_spu_info[node].list_mutex);
if (victim) {
/*
@@ -647,28 +646,15 @@ static struct spu *find_victim(struct sp
* look at another context or give up after X retries.
*/
if (!mutex_trylock(&victim->state_mutex)) {
- victim = NULL;
- goto restart;
- }
-
- spu = victim->spu;
- if (!spu || victim->prio <= ctx->prio) {
- /*
- * This race can happen because we've dropped
- * the active list mutex. Not a problem, just
- * restart the search.
- */
- mutex_unlock(&victim->state_mutex);
+ mutex_unlock(&cbe_spu_info[node].list_mutex);
victim = NULL;
goto restart;
}
spu_context_trace(__spu_deactivate__unload, ctx, spu);
- mutex_lock(&cbe_spu_info[node].list_mutex);
cbe_spu_info[node].nr_active--;
spu_unbind_context(spu, victim);
- mutex_unlock(&cbe_spu_info[node].list_mutex);
victim->stats.invol_ctx_switch++;
spu->stats.invol_ctx_switch++;
@@ -676,9 +662,10 @@ static struct spu *find_victim(struct sp
spu_add_to_rq(victim);
mutex_unlock(&victim->state_mutex);
-
+ mutex_unlock(&cbe_spu_info[node].list_mutex);
return spu;
}
+ mutex_unlock(&cbe_spu_info[node].list_mutex);
}
return NULL;
@@ -715,16 +702,21 @@ static void spu_schedule(struct spu *spu
spu_release(ctx);
}
-static void spu_unschedule(struct spu *spu, struct spu_context *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--;
+ cbe_spu_info[spu->node].nr_active--;
spu->alloc_state = SPU_FREE;
spu_unbind_context(spu, ctx);
ctx->stats.invol_ctx_switch++;
spu->stats.invol_ctx_switch++;
+}
+
+static void spu_unschedule(struct spu *spu, struct spu_context *ctx)
+{
+ int node = spu->node;
+
+ mutex_lock(&cbe_spu_info[node].list_mutex);
+ __spu_unschedule(spu, ctx);
mutex_unlock(&cbe_spu_info[node].list_mutex);
}
@@ -875,8 +867,11 @@ static noinline void spusched_tick(struc
struct spu_context *new = NULL;
struct spu *spu = NULL;
- if (spu_acquire(ctx))
- BUG(); /* a kernel thread never has signals pending */
+ /*
+ * If we can't lock the context wait for the next scheduler tick.
+ */
+ if (!mutex_trylock(&ctx->state_mutex))
+ return;
if (ctx->state != SPU_STATE_RUNNABLE)
goto out;
@@ -892,9 +887,16 @@ static noinline void spusched_tick(struc
spu_context_trace(spusched_tick__preempt, ctx, spu);
- new = grab_runnable_context(ctx->prio + 1, spu->node);
+ /*
+ * Try to find a new context on the runqueue that we can lock
+ * without sleeping.
+ */
+ do {
+ new = grab_runnable_context(ctx->prio + 1, spu->node);
+ } while (new && !mutex_trylock(&new->state_mutex));
+
if (new) {
- spu_unschedule(spu, ctx);
+ __spu_unschedule(spu, ctx);
if (test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags))
spu_add_to_rq(ctx);
} else {
@@ -905,8 +907,24 @@ static noinline void spusched_tick(struc
out:
spu_release(ctx);
- if (new)
- spu_schedule(spu, new);
+ /*
+ * This is an opencoded spu_schedule accounting for the fact
+ * that we have the list_lock already taken.
+ *
+ * Not pretty.
+ */
+ if (new) {
+ int node = spu->node;
+
+ BUG_ON(spu->ctx);
+
+ spu_set_timeslice(new);
+ spu_bind_context(spu, new);
+ cbe_spu_info[node].nr_active++;
+ spu->alloc_state = SPU_USED;
+ wake_up_all(&new->run_wq);
+ spu_release(new);
+ }
}
/**
@@ -973,11 +991,8 @@ static int spusched_thread(void *unused)
cbe_list) {
struct spu_context *ctx = spu->ctx;
- if (ctx) {
- mutex_unlock(mtx);
+ if (ctx)
spusched_tick(ctx);
- mutex_lock(mtx);
- }
}
mutex_unlock(mtx);
}
More information about the cbe-oss-dev
mailing list