Commit 9a78f9c3 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull scheduler fix from Thomas Gleixner:
 "Fix a long standing state race in finish_task_switch()"

* 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  sched/core: Fix TASK_DEAD race in finish_task_switch()
parents 7cbbab00 95913d97
...@@ -2517,11 +2517,11 @@ static struct rq *finish_task_switch(struct task_struct *prev) ...@@ -2517,11 +2517,11 @@ static struct rq *finish_task_switch(struct task_struct *prev)
* If a task dies, then it sets TASK_DEAD in tsk->state and calls * If a task dies, then it sets TASK_DEAD in tsk->state and calls
* schedule one last time. The schedule call will never return, and * schedule one last time. The schedule call will never return, and
* the scheduled task must drop that reference. * the scheduled task must drop that reference.
* The test for TASK_DEAD must occur while the runqueue locks are *
* still held, otherwise prev could be scheduled on another cpu, die * We must observe prev->state before clearing prev->on_cpu (in
* there before we look at prev->state, and then the reference would * finish_lock_switch), otherwise a concurrent wakeup can get prev
* be dropped twice. * running on another CPU and we could rave with its RUNNING -> DEAD
* Manfred Spraul <manfred@colorfullife.com> * transition, resulting in a double drop.
*/ */
prev_state = prev->state; prev_state = prev->state;
vtime_task_switch(prev); vtime_task_switch(prev);
......
...@@ -1078,9 +1078,10 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) ...@@ -1078,9 +1078,10 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev)
* After ->on_cpu is cleared, the task can be moved to a different CPU. * After ->on_cpu is cleared, the task can be moved to a different CPU.
* We must ensure this doesn't happen until the switch is completely * We must ensure this doesn't happen until the switch is completely
* finished. * finished.
*
* Pairs with the control dependency and rmb in try_to_wake_up().
*/ */
smp_wmb(); smp_store_release(&prev->on_cpu, 0);
prev->on_cpu = 0;
#endif #endif
#ifdef CONFIG_DEBUG_SPINLOCK #ifdef CONFIG_DEBUG_SPINLOCK
/* this is a valid case when another task releases the spinlock */ /* this is a valid case when another task releases the spinlock */
......
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