forked from Minki/linux
RT: fix push_rt_task() to handle dequeue_pushable properly
A panic was discovered by Chirag Jog where a BUG_ON sanity check in the new "pushable_task" logic would trigger a panic under certain circumstances: http://lkml.org/lkml/2008/9/25/189 Gilles Carry discovered that the root cause was attributed to the pushable_tasks list getting corrupted in the push_rt_task logic. This was the result of a dropped rq lock in double_lock_balance allowing a task in the process of being pushed to potentially migrate away, and thus corrupt the pushable_tasks() list. I traced back the problem as introduced by the pushable_tasks patch that went in recently. There is a "retry" path in push_rt_task() that actually had a compound conditional to decide whether to retry or exit. I missed the meaning behind the rationale for the virtual "if(!task) goto out;" portion of the compound statement and thus did not handle it properly. The new pushable_tasks logic actually creates three distinct conditions: 1) an untouched and unpushable task should be dequeued 2) a migrated task where more pushable tasks remain should be retried 3) a migrated task where no more pushable tasks exist should exit The original logic mushed (1) and (3) together, resulting in the system dequeuing a migrated task (against an unlocked foreign run-queue nonetheless). To fix this, we get rid of the notion of "paranoid" and we support the three unique conditions properly. The paranoid feature is no longer relevant with the new pushable logic (since pushable naturally limits the loop) anyway, so lets just remove it. Reported-By: Chirag Jog <chirag@linux.vnet.ibm.com> Found-by: Gilles Carry <gilles.carry@bull.net> Signed-off-by: Gregory Haskins <ghaskins@novell.com>
This commit is contained in:
parent
917b627d4d
commit
1563513d34
@ -1192,7 +1192,6 @@ static int push_rt_task(struct rq *rq)
|
|||||||
{
|
{
|
||||||
struct task_struct *next_task;
|
struct task_struct *next_task;
|
||||||
struct rq *lowest_rq;
|
struct rq *lowest_rq;
|
||||||
int paranoid = RT_MAX_TRIES;
|
|
||||||
|
|
||||||
if (!rq->rt.overloaded)
|
if (!rq->rt.overloaded)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1226,23 +1225,34 @@ static int push_rt_task(struct rq *rq)
|
|||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
/*
|
/*
|
||||||
* find lock_lowest_rq releases rq->lock
|
* find lock_lowest_rq releases rq->lock
|
||||||
* so it is possible that next_task has changed.
|
* so it is possible that next_task has migrated.
|
||||||
* If it has, then try again.
|
*
|
||||||
|
* We need to make sure that the task is still on the same
|
||||||
|
* run-queue and is also still the next task eligible for
|
||||||
|
* pushing.
|
||||||
*/
|
*/
|
||||||
task = pick_next_pushable_task(rq);
|
task = pick_next_pushable_task(rq);
|
||||||
if (unlikely(task != next_task) && task && paranoid--) {
|
if (task_cpu(next_task) == rq->cpu && task == next_task) {
|
||||||
put_task_struct(next_task);
|
/*
|
||||||
next_task = task;
|
* If we get here, the task hasnt moved at all, but
|
||||||
goto retry;
|
* it has failed to push. We will not try again,
|
||||||
|
* since the other cpus will pull from us when they
|
||||||
|
* are ready.
|
||||||
|
*/
|
||||||
|
dequeue_pushable_task(rq, next_task);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!task)
|
||||||
|
/* No more tasks, just exit */
|
||||||
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Once we have failed to push this task, we will not
|
* Something has shifted, try again.
|
||||||
* try again, since the other cpus will pull from us
|
|
||||||
* when they are ready
|
|
||||||
*/
|
*/
|
||||||
dequeue_pushable_task(rq, next_task);
|
put_task_struct(next_task);
|
||||||
goto out;
|
next_task = task;
|
||||||
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivate_task(rq, next_task, 0);
|
deactivate_task(rq, next_task, 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user