diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-10-11 13:24:32 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-10-11 13:24:32 -0400 |
| commit | 9a78f9c3c68c4d57f602b4100e766945d3362f4d (patch) | |
| tree | d17f8e55f98819b58bd0a594afee211867d3a016 /kernel | |
| parent | 7cbbab00cbbd3d36288f4d3634eb45d66d78d218 (diff) | |
| parent | 95913d97914f44db2b81271c2e2ebd4d2ac2df83 (diff) | |
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()
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/sched/core.c | 10 | ||||
| -rw-r--r-- | kernel/sched/sched.h | 5 |
2 files changed, 8 insertions, 7 deletions
diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 615953141951..10a8faa1b0d4 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c | |||
| @@ -2517,11 +2517,11 @@ static struct rq *finish_task_switch(struct task_struct *prev) | |||
| 2517 | * If a task dies, then it sets TASK_DEAD in tsk->state and calls | 2517 | * If a task dies, then it sets TASK_DEAD in tsk->state and calls |
| 2518 | * schedule one last time. The schedule call will never return, and | 2518 | * schedule one last time. The schedule call will never return, and |
| 2519 | * the scheduled task must drop that reference. | 2519 | * the scheduled task must drop that reference. |
| 2520 | * The test for TASK_DEAD must occur while the runqueue locks are | 2520 | * |
| 2521 | * still held, otherwise prev could be scheduled on another cpu, die | 2521 | * We must observe prev->state before clearing prev->on_cpu (in |
| 2522 | * there before we look at prev->state, and then the reference would | 2522 | * finish_lock_switch), otherwise a concurrent wakeup can get prev |
| 2523 | * be dropped twice. | 2523 | * running on another CPU and we could rave with its RUNNING -> DEAD |
| 2524 | * Manfred Spraul <manfred@colorfullife.com> | 2524 | * transition, resulting in a double drop. |
| 2525 | */ | 2525 | */ |
| 2526 | prev_state = prev->state; | 2526 | prev_state = prev->state; |
| 2527 | vtime_task_switch(prev); | 2527 | vtime_task_switch(prev); |
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 68cda117574c..6d2a119c7ad9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h | |||
| @@ -1078,9 +1078,10 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) | |||
| 1078 | * After ->on_cpu is cleared, the task can be moved to a different CPU. | 1078 | * After ->on_cpu is cleared, the task can be moved to a different CPU. |
| 1079 | * We must ensure this doesn't happen until the switch is completely | 1079 | * We must ensure this doesn't happen until the switch is completely |
| 1080 | * finished. | 1080 | * finished. |
| 1081 | * | ||
| 1082 | * Pairs with the control dependency and rmb in try_to_wake_up(). | ||
| 1081 | */ | 1083 | */ |
| 1082 | smp_wmb(); | 1084 | smp_store_release(&prev->on_cpu, 0); |
| 1083 | prev->on_cpu = 0; | ||
| 1084 | #endif | 1085 | #endif |
| 1085 | #ifdef CONFIG_DEBUG_SPINLOCK | 1086 | #ifdef CONFIG_DEBUG_SPINLOCK |
| 1086 | /* this is a valid case when another task releases the spinlock */ | 1087 | /* this is a valid case when another task releases the spinlock */ |
