diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2011-01-17 11:03:27 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-01-26 06:33:22 -0500 |
commit | da7a735e51f9622eb3e1672594d4a41da01d7e4f (patch) | |
tree | 27623dcd39c52a80b79e0ee86ab426fc9c7e2b46 /kernel | |
parent | a8941d7ec81678fb69aea7183338175f112f3e0d (diff) |
sched: Fix switch_from_fair()
When a task is taken out of the fair class we must ensure the vruntime
is properly normalized because when we put it back in it will assume
to be normalized.
The case that goes wrong is when changing away from the fair class
while sleeping. Sleeping tasks have non-normalized vruntime in order
to make sleeper-fairness work. So treat the switch away from fair as a
wakeup and preserve the relative vruntime.
Also update sysrq-n to call the ->switch_{to,from} methods.
Reported-by: Onkalo Samu <samu.p.onkalo@nokia.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/sched.c | 25 | ||||
-rw-r--r-- | kernel/sched_fair.c | 42 | ||||
-rw-r--r-- | kernel/sched_idletask.c | 7 | ||||
-rw-r--r-- | kernel/sched_rt.c | 19 | ||||
-rw-r--r-- | kernel/sched_stoptask.c | 7 |
5 files changed, 66 insertions, 34 deletions
diff --git a/kernel/sched.c b/kernel/sched.c index 8b718b59b09f..78fa75394011 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -2057,14 +2057,14 @@ inline int task_curr(const struct task_struct *p) | |||
2057 | 2057 | ||
2058 | static inline void check_class_changed(struct rq *rq, struct task_struct *p, | 2058 | static inline void check_class_changed(struct rq *rq, struct task_struct *p, |
2059 | const struct sched_class *prev_class, | 2059 | const struct sched_class *prev_class, |
2060 | int oldprio, int running) | 2060 | int oldprio) |
2061 | { | 2061 | { |
2062 | if (prev_class != p->sched_class) { | 2062 | if (prev_class != p->sched_class) { |
2063 | if (prev_class->switched_from) | 2063 | if (prev_class->switched_from) |
2064 | prev_class->switched_from(rq, p, running); | 2064 | prev_class->switched_from(rq, p); |
2065 | p->sched_class->switched_to(rq, p, running); | 2065 | p->sched_class->switched_to(rq, p); |
2066 | } else | 2066 | } else if (oldprio != p->prio) |
2067 | p->sched_class->prio_changed(rq, p, oldprio, running); | 2067 | p->sched_class->prio_changed(rq, p, oldprio); |
2068 | } | 2068 | } |
2069 | 2069 | ||
2070 | static void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) | 2070 | static void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) |
@@ -2598,6 +2598,7 @@ static void __sched_fork(struct task_struct *p) | |||
2598 | p->se.sum_exec_runtime = 0; | 2598 | p->se.sum_exec_runtime = 0; |
2599 | p->se.prev_sum_exec_runtime = 0; | 2599 | p->se.prev_sum_exec_runtime = 0; |
2600 | p->se.nr_migrations = 0; | 2600 | p->se.nr_migrations = 0; |
2601 | p->se.vruntime = 0; | ||
2601 | 2602 | ||
2602 | #ifdef CONFIG_SCHEDSTATS | 2603 | #ifdef CONFIG_SCHEDSTATS |
2603 | memset(&p->se.statistics, 0, sizeof(p->se.statistics)); | 2604 | memset(&p->se.statistics, 0, sizeof(p->se.statistics)); |
@@ -4696,11 +4697,10 @@ void rt_mutex_setprio(struct task_struct *p, int prio) | |||
4696 | 4697 | ||
4697 | if (running) | 4698 | if (running) |
4698 | p->sched_class->set_curr_task(rq); | 4699 | p->sched_class->set_curr_task(rq); |
4699 | if (on_rq) { | 4700 | if (on_rq) |
4700 | enqueue_task(rq, p, oldprio < prio ? ENQUEUE_HEAD : 0); | 4701 | enqueue_task(rq, p, oldprio < prio ? ENQUEUE_HEAD : 0); |
4701 | 4702 | ||
4702 | check_class_changed(rq, p, prev_class, oldprio, running); | 4703 | check_class_changed(rq, p, prev_class, oldprio); |
4703 | } | ||
4704 | task_rq_unlock(rq, &flags); | 4704 | task_rq_unlock(rq, &flags); |
4705 | } | 4705 | } |
4706 | 4706 | ||
@@ -5028,11 +5028,10 @@ recheck: | |||
5028 | 5028 | ||
5029 | if (running) | 5029 | if (running) |
5030 | p->sched_class->set_curr_task(rq); | 5030 | p->sched_class->set_curr_task(rq); |
5031 | if (on_rq) { | 5031 | if (on_rq) |
5032 | activate_task(rq, p, 0); | 5032 | activate_task(rq, p, 0); |
5033 | 5033 | ||
5034 | check_class_changed(rq, p, prev_class, oldprio, running); | 5034 | check_class_changed(rq, p, prev_class, oldprio); |
5035 | } | ||
5036 | __task_rq_unlock(rq); | 5035 | __task_rq_unlock(rq); |
5037 | raw_spin_unlock_irqrestore(&p->pi_lock, flags); | 5036 | raw_spin_unlock_irqrestore(&p->pi_lock, flags); |
5038 | 5037 | ||
@@ -8237,6 +8236,8 @@ EXPORT_SYMBOL(__might_sleep); | |||
8237 | #ifdef CONFIG_MAGIC_SYSRQ | 8236 | #ifdef CONFIG_MAGIC_SYSRQ |
8238 | static void normalize_task(struct rq *rq, struct task_struct *p) | 8237 | static void normalize_task(struct rq *rq, struct task_struct *p) |
8239 | { | 8238 | { |
8239 | const struct sched_class *prev_class = p->sched_class; | ||
8240 | int old_prio = p->prio; | ||
8240 | int on_rq; | 8241 | int on_rq; |
8241 | 8242 | ||
8242 | on_rq = p->se.on_rq; | 8243 | on_rq = p->se.on_rq; |
@@ -8247,6 +8248,8 @@ static void normalize_task(struct rq *rq, struct task_struct *p) | |||
8247 | activate_task(rq, p, 0); | 8248 | activate_task(rq, p, 0); |
8248 | resched_task(rq->curr); | 8249 | resched_task(rq->curr); |
8249 | } | 8250 | } |
8251 | |||
8252 | check_class_changed(rq, p, prev_class, old_prio); | ||
8250 | } | 8253 | } |
8251 | 8254 | ||
8252 | void normalize_rt_tasks(void) | 8255 | void normalize_rt_tasks(void) |
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 4cbc9121094c..55040f3938d8 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c | |||
@@ -4078,33 +4078,62 @@ static void task_fork_fair(struct task_struct *p) | |||
4078 | * Priority of the task has changed. Check to see if we preempt | 4078 | * Priority of the task has changed. Check to see if we preempt |
4079 | * the current task. | 4079 | * the current task. |
4080 | */ | 4080 | */ |
4081 | static void prio_changed_fair(struct rq *rq, struct task_struct *p, | 4081 | static void |
4082 | int oldprio, int running) | 4082 | prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) |
4083 | { | 4083 | { |
4084 | if (!p->se.on_rq) | ||
4085 | return; | ||
4086 | |||
4084 | /* | 4087 | /* |
4085 | * Reschedule if we are currently running on this runqueue and | 4088 | * Reschedule if we are currently running on this runqueue and |
4086 | * our priority decreased, or if we are not currently running on | 4089 | * our priority decreased, or if we are not currently running on |
4087 | * this runqueue and our priority is higher than the current's | 4090 | * this runqueue and our priority is higher than the current's |
4088 | */ | 4091 | */ |
4089 | if (running) { | 4092 | if (rq->curr == p) { |
4090 | if (p->prio > oldprio) | 4093 | if (p->prio > oldprio) |
4091 | resched_task(rq->curr); | 4094 | resched_task(rq->curr); |
4092 | } else | 4095 | } else |
4093 | check_preempt_curr(rq, p, 0); | 4096 | check_preempt_curr(rq, p, 0); |
4094 | } | 4097 | } |
4095 | 4098 | ||
4099 | static void switched_from_fair(struct rq *rq, struct task_struct *p) | ||
4100 | { | ||
4101 | struct sched_entity *se = &p->se; | ||
4102 | struct cfs_rq *cfs_rq = cfs_rq_of(se); | ||
4103 | |||
4104 | /* | ||
4105 | * Ensure the task's vruntime is normalized, so that when its | ||
4106 | * switched back to the fair class the enqueue_entity(.flags=0) will | ||
4107 | * do the right thing. | ||
4108 | * | ||
4109 | * If it was on_rq, then the dequeue_entity(.flags=0) will already | ||
4110 | * have normalized the vruntime, if it was !on_rq, then only when | ||
4111 | * the task is sleeping will it still have non-normalized vruntime. | ||
4112 | */ | ||
4113 | if (!se->on_rq && p->state != TASK_RUNNING) { | ||
4114 | /* | ||
4115 | * Fix up our vruntime so that the current sleep doesn't | ||
4116 | * cause 'unlimited' sleep bonus. | ||
4117 | */ | ||
4118 | place_entity(cfs_rq, se, 0); | ||
4119 | se->vruntime -= cfs_rq->min_vruntime; | ||
4120 | } | ||
4121 | } | ||
4122 | |||
4096 | /* | 4123 | /* |
4097 | * We switched to the sched_fair class. | 4124 | * We switched to the sched_fair class. |
4098 | */ | 4125 | */ |
4099 | static void switched_to_fair(struct rq *rq, struct task_struct *p, | 4126 | static void switched_to_fair(struct rq *rq, struct task_struct *p) |
4100 | int running) | ||
4101 | { | 4127 | { |
4128 | if (!p->se.on_rq) | ||
4129 | return; | ||
4130 | |||
4102 | /* | 4131 | /* |
4103 | * We were most likely switched from sched_rt, so | 4132 | * We were most likely switched from sched_rt, so |
4104 | * kick off the schedule if running, otherwise just see | 4133 | * kick off the schedule if running, otherwise just see |
4105 | * if we can still preempt the current task. | 4134 | * if we can still preempt the current task. |
4106 | */ | 4135 | */ |
4107 | if (running) | 4136 | if (rq->curr == p) |
4108 | resched_task(rq->curr); | 4137 | resched_task(rq->curr); |
4109 | else | 4138 | else |
4110 | check_preempt_curr(rq, p, 0); | 4139 | check_preempt_curr(rq, p, 0); |
@@ -4190,6 +4219,7 @@ static const struct sched_class fair_sched_class = { | |||
4190 | .task_fork = task_fork_fair, | 4219 | .task_fork = task_fork_fair, |
4191 | 4220 | ||
4192 | .prio_changed = prio_changed_fair, | 4221 | .prio_changed = prio_changed_fair, |
4222 | .switched_from = switched_from_fair, | ||
4193 | .switched_to = switched_to_fair, | 4223 | .switched_to = switched_to_fair, |
4194 | 4224 | ||
4195 | .get_rr_interval = get_rr_interval_fair, | 4225 | .get_rr_interval = get_rr_interval_fair, |
diff --git a/kernel/sched_idletask.c b/kernel/sched_idletask.c index 41eb62a0808b..c82f26c1b7c3 100644 --- a/kernel/sched_idletask.c +++ b/kernel/sched_idletask.c | |||
@@ -52,14 +52,13 @@ static void set_curr_task_idle(struct rq *rq) | |||
52 | { | 52 | { |
53 | } | 53 | } |
54 | 54 | ||
55 | static void | 55 | static void switched_to_idle(struct rq *rq, struct task_struct *p) |
56 | switched_to_idle(struct rq *rq, struct task_struct *p, int running) | ||
57 | { | 56 | { |
58 | BUG(); | 57 | BUG(); |
59 | } | 58 | } |
60 | 59 | ||
61 | static void prio_changed_idle(struct rq *rq, struct task_struct *p, | 60 | static void |
62 | int oldprio, int running) | 61 | prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio) |
63 | { | 62 | { |
64 | BUG(); | 63 | BUG(); |
65 | } | 64 | } |
diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index c914ec747ca6..c381fdc18c64 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c | |||
@@ -1595,8 +1595,7 @@ static void rq_offline_rt(struct rq *rq) | |||
1595 | * When switch from the rt queue, we bring ourselves to a position | 1595 | * When switch from the rt queue, we bring ourselves to a position |
1596 | * that we might want to pull RT tasks from other runqueues. | 1596 | * that we might want to pull RT tasks from other runqueues. |
1597 | */ | 1597 | */ |
1598 | static void switched_from_rt(struct rq *rq, struct task_struct *p, | 1598 | static void switched_from_rt(struct rq *rq, struct task_struct *p) |
1599 | int running) | ||
1600 | { | 1599 | { |
1601 | /* | 1600 | /* |
1602 | * If there are other RT tasks then we will reschedule | 1601 | * If there are other RT tasks then we will reschedule |
@@ -1605,7 +1604,7 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p, | |||
1605 | * we may need to handle the pulling of RT tasks | 1604 | * we may need to handle the pulling of RT tasks |
1606 | * now. | 1605 | * now. |
1607 | */ | 1606 | */ |
1608 | if (!rq->rt.rt_nr_running) | 1607 | if (p->se.on_rq && !rq->rt.rt_nr_running) |
1609 | pull_rt_task(rq); | 1608 | pull_rt_task(rq); |
1610 | } | 1609 | } |
1611 | 1610 | ||
@@ -1624,8 +1623,7 @@ static inline void init_sched_rt_class(void) | |||
1624 | * with RT tasks. In this case we try to push them off to | 1623 | * with RT tasks. In this case we try to push them off to |
1625 | * other runqueues. | 1624 | * other runqueues. |
1626 | */ | 1625 | */ |
1627 | static void switched_to_rt(struct rq *rq, struct task_struct *p, | 1626 | static void switched_to_rt(struct rq *rq, struct task_struct *p) |
1628 | int running) | ||
1629 | { | 1627 | { |
1630 | int check_resched = 1; | 1628 | int check_resched = 1; |
1631 | 1629 | ||
@@ -1636,7 +1634,7 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p, | |||
1636 | * If that current running task is also an RT task | 1634 | * If that current running task is also an RT task |
1637 | * then see if we can move to another run queue. | 1635 | * then see if we can move to another run queue. |
1638 | */ | 1636 | */ |
1639 | if (!running) { | 1637 | if (p->se.on_rq && rq->curr != p) { |
1640 | #ifdef CONFIG_SMP | 1638 | #ifdef CONFIG_SMP |
1641 | if (rq->rt.overloaded && push_rt_task(rq) && | 1639 | if (rq->rt.overloaded && push_rt_task(rq) && |
1642 | /* Don't resched if we changed runqueues */ | 1640 | /* Don't resched if we changed runqueues */ |
@@ -1652,10 +1650,13 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p, | |||
1652 | * Priority of the task has changed. This may cause | 1650 | * Priority of the task has changed. This may cause |
1653 | * us to initiate a push or pull. | 1651 | * us to initiate a push or pull. |
1654 | */ | 1652 | */ |
1655 | static void prio_changed_rt(struct rq *rq, struct task_struct *p, | 1653 | static void |
1656 | int oldprio, int running) | 1654 | prio_changed_rt(struct rq *rq, struct task_struct *p, int oldprio) |
1657 | { | 1655 | { |
1658 | if (running) { | 1656 | if (!p->se.on_rq) |
1657 | return; | ||
1658 | |||
1659 | if (rq->curr == p) { | ||
1659 | #ifdef CONFIG_SMP | 1660 | #ifdef CONFIG_SMP |
1660 | /* | 1661 | /* |
1661 | * If our priority decreases while running, we | 1662 | * If our priority decreases while running, we |
diff --git a/kernel/sched_stoptask.c b/kernel/sched_stoptask.c index 2bf6b47058c1..84ec9bcf82d9 100644 --- a/kernel/sched_stoptask.c +++ b/kernel/sched_stoptask.c | |||
@@ -59,14 +59,13 @@ static void set_curr_task_stop(struct rq *rq) | |||
59 | { | 59 | { |
60 | } | 60 | } |
61 | 61 | ||
62 | static void switched_to_stop(struct rq *rq, struct task_struct *p, | 62 | static void switched_to_stop(struct rq *rq, struct task_struct *p) |
63 | int running) | ||
64 | { | 63 | { |
65 | BUG(); /* its impossible to change to this class */ | 64 | BUG(); /* its impossible to change to this class */ |
66 | } | 65 | } |
67 | 66 | ||
68 | static void prio_changed_stop(struct rq *rq, struct task_struct *p, | 67 | static void |
69 | int oldprio, int running) | 68 | prio_changed_stop(struct rq *rq, struct task_struct *p, int oldprio) |
70 | { | 69 | { |
71 | BUG(); /* how!?, what priority? */ | 70 | BUG(); /* how!?, what priority? */ |
72 | } | 71 | } |