diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2012-01-25 05:50:51 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2012-01-26 13:38:09 -0500 |
commit | 4ca9b72b71f10147bd21969c1805f5b2c4ca7b7b (patch) | |
tree | 05d6072a28029ffca57ba6c7d30036698348b59a /kernel/sched | |
parent | 87f71ae2dd7471c1b4c94100be1f218e91dc64c3 (diff) |
sched: Fix rq->nr_uninterruptible update race
KOSAKI Motohiro noticed the following race:
> CPU0 CPU1
> --------------------------------------------------------
> deactivate_task()
> task->state = TASK_UNINTERRUPTIBLE;
> activate_task()
> rq->nr_uninterruptible--;
>
> schedule()
> deactivate_task()
> rq->nr_uninterruptible++;
>
Kosaki-San's scenario is possible when CPU0 runs
__sched_setscheduler() against CPU1's current @task.
__sched_setscheduler() does a dequeue/enqueue in order to move
the task to its new queue (position) to reflect the newly provided
scheduling parameters. However it should be completely invariant to
nr_uninterruptible accounting, sched_setscheduler() doesn't affect
readyness to run, merely policy on when to run.
So convert the inappropriate activate/deactivate_task usage to
enqueue/dequeue_task, which avoids the nr_uninterruptible accounting.
Also convert the two other sites: __migrate_task() and
normalize_task() that still use activate/deactivate_task. These sites
aren't really a problem since __migrate_task() will only be called on
non-running task (and therefore are immume to the described problem)
and normalize_task() isn't ever used on regular systems.
Also remove the comments from activate/deactivate_task since they're
misleading at best.
Reported-by: KOSAKI Motohiro <kosaki.motohiro@gmail.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1327486224.2614.45.camel@laptop
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/sched')
-rw-r--r-- | kernel/sched/core.c | 18 |
1 files changed, 6 insertions, 12 deletions
diff --git a/kernel/sched/core.c b/kernel/sched/core.c index df00cb09263e..e067df1fd01a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c | |||
@@ -723,9 +723,6 @@ static void dequeue_task(struct rq *rq, struct task_struct *p, int flags) | |||
723 | p->sched_class->dequeue_task(rq, p, flags); | 723 | p->sched_class->dequeue_task(rq, p, flags); |
724 | } | 724 | } |
725 | 725 | ||
726 | /* | ||
727 | * activate_task - move a task to the runqueue. | ||
728 | */ | ||
729 | void activate_task(struct rq *rq, struct task_struct *p, int flags) | 726 | void activate_task(struct rq *rq, struct task_struct *p, int flags) |
730 | { | 727 | { |
731 | if (task_contributes_to_load(p)) | 728 | if (task_contributes_to_load(p)) |
@@ -734,9 +731,6 @@ void activate_task(struct rq *rq, struct task_struct *p, int flags) | |||
734 | enqueue_task(rq, p, flags); | 731 | enqueue_task(rq, p, flags); |
735 | } | 732 | } |
736 | 733 | ||
737 | /* | ||
738 | * deactivate_task - remove a task from the runqueue. | ||
739 | */ | ||
740 | void deactivate_task(struct rq *rq, struct task_struct *p, int flags) | 734 | void deactivate_task(struct rq *rq, struct task_struct *p, int flags) |
741 | { | 735 | { |
742 | if (task_contributes_to_load(p)) | 736 | if (task_contributes_to_load(p)) |
@@ -4134,7 +4128,7 @@ recheck: | |||
4134 | on_rq = p->on_rq; | 4128 | on_rq = p->on_rq; |
4135 | running = task_current(rq, p); | 4129 | running = task_current(rq, p); |
4136 | if (on_rq) | 4130 | if (on_rq) |
4137 | deactivate_task(rq, p, 0); | 4131 | dequeue_task(rq, p, 0); |
4138 | if (running) | 4132 | if (running) |
4139 | p->sched_class->put_prev_task(rq, p); | 4133 | p->sched_class->put_prev_task(rq, p); |
4140 | 4134 | ||
@@ -4147,7 +4141,7 @@ recheck: | |||
4147 | if (running) | 4141 | if (running) |
4148 | p->sched_class->set_curr_task(rq); | 4142 | p->sched_class->set_curr_task(rq); |
4149 | if (on_rq) | 4143 | if (on_rq) |
4150 | activate_task(rq, p, 0); | 4144 | enqueue_task(rq, p, 0); |
4151 | 4145 | ||
4152 | check_class_changed(rq, p, prev_class, oldprio); | 4146 | check_class_changed(rq, p, prev_class, oldprio); |
4153 | task_rq_unlock(rq, p, &flags); | 4147 | task_rq_unlock(rq, p, &flags); |
@@ -4998,9 +4992,9 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) | |||
4998 | * placed properly. | 4992 | * placed properly. |
4999 | */ | 4993 | */ |
5000 | if (p->on_rq) { | 4994 | if (p->on_rq) { |
5001 | deactivate_task(rq_src, p, 0); | 4995 | dequeue_task(rq_src, p, 0); |
5002 | set_task_cpu(p, dest_cpu); | 4996 | set_task_cpu(p, dest_cpu); |
5003 | activate_task(rq_dest, p, 0); | 4997 | enqueue_task(rq_dest, p, 0); |
5004 | check_preempt_curr(rq_dest, p, 0); | 4998 | check_preempt_curr(rq_dest, p, 0); |
5005 | } | 4999 | } |
5006 | done: | 5000 | done: |
@@ -7032,10 +7026,10 @@ static void normalize_task(struct rq *rq, struct task_struct *p) | |||
7032 | 7026 | ||
7033 | on_rq = p->on_rq; | 7027 | on_rq = p->on_rq; |
7034 | if (on_rq) | 7028 | if (on_rq) |
7035 | deactivate_task(rq, p, 0); | 7029 | dequeue_task(rq, p, 0); |
7036 | __setscheduler(rq, p, SCHED_NORMAL, 0); | 7030 | __setscheduler(rq, p, SCHED_NORMAL, 0); |
7037 | if (on_rq) { | 7031 | if (on_rq) { |
7038 | activate_task(rq, p, 0); | 7032 | enqueue_task(rq, p, 0); |
7039 | resched_task(rq->curr); | 7033 | resched_task(rq->curr); |
7040 | } | 7034 | } |
7041 | 7035 | ||