Commit 5eef46a9 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix race in sched_exit()

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Fix a race on sleep_avg in sched_exit().

The symptom I saw on 64-bit s390 has been a fixpoint divide exception
because sleep_avg had a value > NS_MAX_SLEEP_AVG.  I tracked it down and
the problem is sched_exit which recalculates the parents sleep average
without taking the runqueue lock.  schedule() subtracts run_time from
sleep_avg of the previous process.  This can turn out negative and is
corrected shortly after the subtraction but that is already too late.
sched_exit() already read the negative value an miscalculated the parents
sleep_avg -> bang.

I fixed this by adding task_rq_lock/task_rq_unlock to sched_exit().
parent 7508df7c
...@@ -819,6 +819,7 @@ void wake_up_forked_process(task_t * p) ...@@ -819,6 +819,7 @@ void wake_up_forked_process(task_t * p)
void sched_exit(task_t * p) void sched_exit(task_t * p)
{ {
unsigned long flags; unsigned long flags;
runqueue_t *rq;
local_irq_save(flags); local_irq_save(flags);
if (p->first_time_slice) { if (p->first_time_slice) {
...@@ -831,10 +832,12 @@ void sched_exit(task_t * p) ...@@ -831,10 +832,12 @@ void sched_exit(task_t * p)
* If the child was a (relative-) CPU hog then decrease * If the child was a (relative-) CPU hog then decrease
* the sleep_avg of the parent as well. * the sleep_avg of the parent as well.
*/ */
rq = task_rq_lock(p->parent, &flags);
if (p->sleep_avg < p->parent->sleep_avg) if (p->sleep_avg < p->parent->sleep_avg)
p->parent->sleep_avg = p->parent->sleep_avg / p->parent->sleep_avg = p->parent->sleep_avg /
(EXIT_WEIGHT + 1) * EXIT_WEIGHT + p->sleep_avg / (EXIT_WEIGHT + 1) * EXIT_WEIGHT + p->sleep_avg /
(EXIT_WEIGHT + 1); (EXIT_WEIGHT + 1);
task_rq_unlock(rq, &flags);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment