From 5eef46a913e088355ee050c3618f92c2f6e84de9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 3 Feb 2004 18:30:23 -0800 Subject: [PATCH] [PATCH] Fix race in sched_exit() From: Martin Schwidefsky 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(). --- kernel/sched.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/sched.c b/kernel/sched.c index 141e0c348758a..3000cdf6121b8 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -819,6 +819,7 @@ void wake_up_forked_process(task_t * p) void sched_exit(task_t * p) { unsigned long flags; + runqueue_t *rq; local_irq_save(flags); if (p->first_time_slice) { @@ -831,10 +832,12 @@ void sched_exit(task_t * p) * If the child was a (relative-) CPU hog then decrease * the sleep_avg of the parent as well. */ + rq = task_rq_lock(p->parent, &flags); if (p->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); + task_rq_unlock(rq, &flags); } /** -- 2.53.0