[POWERPC] spufs scheduler: Fix wakeup races

Fix the race between checking for contexts on the runqueue and actually
waking them in spu_deactive and spu_yield.

The guts of spu_reschedule are split into a new helper called
grab_runnable_context which shows if there is a runnable thread below
a specified priority and if yes removes if from the runqueue and uses
it.  This function is used by the new __spu_deactivate hepler shared
by preemption and spu_yield to grab a new context before deactivating
a specified priority and if yes removes if from the runqueue and uses
it.  This function is used by the new __spu_deactivate hepler shared
by preemption and spu_yield to grab a new context before deactivating
the old one.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Christoph Hellwig 2007-06-04 23:26:51 +10:00 committed by Paul Mackerras
parent 47d3a5faa3
commit bb5db29aa0
1 changed files with 77 additions and 82 deletions

View File

@ -93,43 +93,6 @@ void spu_stop_tick(struct spu_context *ctx)
}
}
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
@ -273,34 +236,6 @@ static void spu_prio_wait(struct spu_context *ctx)
remove_wait_queue(&ctx->stop_wq, &wait);
}
/**
* spu_reschedule - try to find a runnable context for a spu
* @spu: spu available
*
* This function is called whenever a spu becomes idle. It looks for the
* most suitable runnable spu context and schedules it for execution.
*/
static void spu_reschedule(struct spu *spu)
{
int best;
spu_free(spu);
spin_lock(&spu_prio->runq_lock);
best = sched_find_first_bit(spu_prio->bitmap);
if (best < MAX_PRIO) {
struct list_head *rq = &spu_prio->runq[best];
struct spu_context *ctx;
BUG_ON(list_empty(rq));
ctx = list_entry(rq->next, struct spu_context, rq);
__spu_del_from_rq(ctx);
wake_up(&ctx->stop_wq);
}
spin_unlock(&spu_prio->runq_lock);
}
static struct spu *spu_get_idle(struct spu_context *ctx)
{
struct spu *spu = NULL;
@ -428,6 +363,51 @@ int spu_activate(struct spu_context *ctx, unsigned long flags)
return -ERESTARTSYS;
}
/**
* grab_runnable_context - try to find a runnable context
*
* Remove the highest priority context on the runqueue and return it
* to the caller. Returns %NULL if no runnable context was found.
*/
static struct spu_context *grab_runnable_context(int prio)
{
struct spu_context *ctx = NULL;
int best;
spin_lock(&spu_prio->runq_lock);
best = sched_find_first_bit(spu_prio->bitmap);
if (best < prio) {
struct list_head *rq = &spu_prio->runq[best];
BUG_ON(list_empty(rq));
ctx = list_entry(rq->next, struct spu_context, rq);
__spu_del_from_rq(ctx);
}
spin_unlock(&spu_prio->runq_lock);
return ctx;
}
static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio)
{
struct spu *spu = ctx->spu;
struct spu_context *new = NULL;
if (spu) {
new = grab_runnable_context(max_prio);
if (new || force) {
spu_unbind_context(spu, ctx);
spu_free(spu);
if (new)
wake_up(&new->stop_wq);
}
}
return new != NULL;
}
/**
* spu_deactivate - unbind a context from it's physical spu
* @ctx: spu context to unbind
@ -437,12 +417,7 @@ int spu_activate(struct spu_context *ctx, unsigned long flags)
*/
void spu_deactivate(struct spu_context *ctx)
{
struct spu *spu = ctx->spu;
if (spu) {
spu_unbind_context(spu, ctx);
spu_reschedule(spu);
}
__spu_deactivate(ctx, 1, MAX_PRIO);
}
/**
@ -455,18 +430,38 @@ void spu_deactivate(struct spu_context *ctx)
*/
void spu_yield(struct spu_context *ctx)
{
struct spu *spu;
mutex_lock(&ctx->state_mutex);
__spu_deactivate(ctx, 0, MAX_PRIO);
mutex_unlock(&ctx->state_mutex);
}
if (mutex_trylock(&ctx->state_mutex)) {
if ((spu = ctx->spu) != NULL) {
int best = sched_find_first_bit(spu_prio->bitmap);
if (best < MAX_PRIO) {
pr_debug("%s: yielding SPU %d NODE %d\n",
__FUNCTION__, spu->number, spu->node);
spu_deactivate(ctx);
}
}
mutex_unlock(&ctx->state_mutex);
void spu_sched_tick(struct work_struct *work)
{
struct spu_context *ctx =
container_of(work, struct spu_context, sched_work.work);
int preempted;
/*
* 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);
preempted = __spu_deactivate(ctx, 0, ctx->prio + 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);
}
}