Skip to content

Commit da7a735

Browse files
Peter ZijlstraIngo Molnar
Peter Zijlstra
authored and
Ingo Molnar
committed
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>
1 parent a8941d7 commit da7a735

File tree

6 files changed

+69
-39
lines changed

6 files changed

+69
-39
lines changed

include/linux/sched.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -1084,12 +1084,10 @@ struct sched_class {
10841084
void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);
10851085
void (*task_fork) (struct task_struct *p);
10861086

1087-
void (*switched_from) (struct rq *this_rq, struct task_struct *task,
1088-
int running);
1089-
void (*switched_to) (struct rq *this_rq, struct task_struct *task,
1090-
int running);
1087+
void (*switched_from) (struct rq *this_rq, struct task_struct *task);
1088+
void (*switched_to) (struct rq *this_rq, struct task_struct *task);
10911089
void (*prio_changed) (struct rq *this_rq, struct task_struct *task,
1092-
int oldprio, int running);
1090+
int oldprio);
10931091

10941092
unsigned int (*get_rr_interval) (struct rq *rq,
10951093
struct task_struct *task);

kernel/sched.c

+14-11
Original file line numberDiff line numberDiff line change
@@ -2057,14 +2057,14 @@ inline int task_curr(const struct task_struct *p)
20572057

20582058
static inline void check_class_changed(struct rq *rq, struct task_struct *p,
20592059
const struct sched_class *prev_class,
2060-
int oldprio, int running)
2060+
int oldprio)
20612061
{
20622062
if (prev_class != p->sched_class) {
20632063
if (prev_class->switched_from)
2064-
prev_class->switched_from(rq, p, running);
2065-
p->sched_class->switched_to(rq, p, running);
2066-
} else
2067-
p->sched_class->prio_changed(rq, p, oldprio, running);
2064+
prev_class->switched_from(rq, p);
2065+
p->sched_class->switched_to(rq, p);
2066+
} else if (oldprio != p->prio)
2067+
p->sched_class->prio_changed(rq, p, oldprio);
20682068
}
20692069

20702070
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)
25982598
p->se.sum_exec_runtime = 0;
25992599
p->se.prev_sum_exec_runtime = 0;
26002600
p->se.nr_migrations = 0;
2601+
p->se.vruntime = 0;
26012602

26022603
#ifdef CONFIG_SCHEDSTATS
26032604
memset(&p->se.statistics, 0, sizeof(p->se.statistics));
@@ -4696,11 +4697,10 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
46964697

46974698
if (running)
46984699
p->sched_class->set_curr_task(rq);
4699-
if (on_rq) {
4700+
if (on_rq)
47004701
enqueue_task(rq, p, oldprio < prio ? ENQUEUE_HEAD : 0);
47014702

4702-
check_class_changed(rq, p, prev_class, oldprio, running);
4703-
}
4703+
check_class_changed(rq, p, prev_class, oldprio);
47044704
task_rq_unlock(rq, &flags);
47054705
}
47064706

@@ -5028,11 +5028,10 @@ static int __sched_setscheduler(struct task_struct *p, int policy,
50285028

50295029
if (running)
50305030
p->sched_class->set_curr_task(rq);
5031-
if (on_rq) {
5031+
if (on_rq)
50325032
activate_task(rq, p, 0);
50335033

5034-
check_class_changed(rq, p, prev_class, oldprio, running);
5035-
}
5034+
check_class_changed(rq, p, prev_class, oldprio);
50365035
__task_rq_unlock(rq);
50375036
raw_spin_unlock_irqrestore(&p->pi_lock, flags);
50385037

@@ -8237,6 +8236,8 @@ EXPORT_SYMBOL(__might_sleep);
82378236
#ifdef CONFIG_MAGIC_SYSRQ
82388237
static void normalize_task(struct rq *rq, struct task_struct *p)
82398238
{
8239+
const struct sched_class *prev_class = p->sched_class;
8240+
int old_prio = p->prio;
82408241
int on_rq;
82418242

82428243
on_rq = p->se.on_rq;
@@ -8247,6 +8248,8 @@ static void normalize_task(struct rq *rq, struct task_struct *p)
82478248
activate_task(rq, p, 0);
82488249
resched_task(rq->curr);
82498250
}
8251+
8252+
check_class_changed(rq, p, prev_class, old_prio);
82508253
}
82518254

82528255
void normalize_rt_tasks(void)

kernel/sched_fair.c

+36-6
Original file line numberDiff line numberDiff line change
@@ -4078,33 +4078,62 @@ static void task_fork_fair(struct task_struct *p)
40784078
* Priority of the task has changed. Check to see if we preempt
40794079
* the current task.
40804080
*/
4081-
static void prio_changed_fair(struct rq *rq, struct task_struct *p,
4082-
int oldprio, int running)
4081+
static void
4082+
prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio)
40834083
{
4084+
if (!p->se.on_rq)
4085+
return;
4086+
40844087
/*
40854088
* Reschedule if we are currently running on this runqueue and
40864089
* our priority decreased, or if we are not currently running on
40874090
* this runqueue and our priority is higher than the current's
40884091
*/
4089-
if (running) {
4092+
if (rq->curr == p) {
40904093
if (p->prio > oldprio)
40914094
resched_task(rq->curr);
40924095
} else
40934096
check_preempt_curr(rq, p, 0);
40944097
}
40954098

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+
40964123
/*
40974124
* We switched to the sched_fair class.
40984125
*/
4099-
static void switched_to_fair(struct rq *rq, struct task_struct *p,
4100-
int running)
4126+
static void switched_to_fair(struct rq *rq, struct task_struct *p)
41014127
{
4128+
if (!p->se.on_rq)
4129+
return;
4130+
41024131
/*
41034132
* We were most likely switched from sched_rt, so
41044133
* kick off the schedule if running, otherwise just see
41054134
* if we can still preempt the current task.
41064135
*/
4107-
if (running)
4136+
if (rq->curr == p)
41084137
resched_task(rq->curr);
41094138
else
41104139
check_preempt_curr(rq, p, 0);
@@ -4190,6 +4219,7 @@ static const struct sched_class fair_sched_class = {
41904219
.task_fork = task_fork_fair,
41914220

41924221
.prio_changed = prio_changed_fair,
4222+
.switched_from = switched_from_fair,
41934223
.switched_to = switched_to_fair,
41944224

41954225
.get_rr_interval = get_rr_interval_fair,

kernel/sched_idletask.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,13 @@ static void set_curr_task_idle(struct rq *rq)
5252
{
5353
}
5454

55-
static void
56-
switched_to_idle(struct rq *rq, struct task_struct *p, int running)
55+
static void switched_to_idle(struct rq *rq, struct task_struct *p)
5756
{
5857
BUG();
5958
}
6059

61-
static void prio_changed_idle(struct rq *rq, struct task_struct *p,
62-
int oldprio, int running)
60+
static void
61+
prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
6362
{
6463
BUG();
6564
}

kernel/sched_rt.c

+10-9
Original file line numberDiff line numberDiff line change
@@ -1595,8 +1595,7 @@ static void rq_offline_rt(struct rq *rq)
15951595
* When switch from the rt queue, we bring ourselves to a position
15961596
* that we might want to pull RT tasks from other runqueues.
15971597
*/
1598-
static void switched_from_rt(struct rq *rq, struct task_struct *p,
1599-
int running)
1598+
static void switched_from_rt(struct rq *rq, struct task_struct *p)
16001599
{
16011600
/*
16021601
* 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,
16051604
* we may need to handle the pulling of RT tasks
16061605
* now.
16071606
*/
1608-
if (!rq->rt.rt_nr_running)
1607+
if (p->se.on_rq && !rq->rt.rt_nr_running)
16091608
pull_rt_task(rq);
16101609
}
16111610

@@ -1624,8 +1623,7 @@ static inline void init_sched_rt_class(void)
16241623
* with RT tasks. In this case we try to push them off to
16251624
* other runqueues.
16261625
*/
1627-
static void switched_to_rt(struct rq *rq, struct task_struct *p,
1628-
int running)
1626+
static void switched_to_rt(struct rq *rq, struct task_struct *p)
16291627
{
16301628
int check_resched = 1;
16311629

@@ -1636,7 +1634,7 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p,
16361634
* If that current running task is also an RT task
16371635
* then see if we can move to another run queue.
16381636
*/
1639-
if (!running) {
1637+
if (p->se.on_rq && rq->curr != p) {
16401638
#ifdef CONFIG_SMP
16411639
if (rq->rt.overloaded && push_rt_task(rq) &&
16421640
/* Don't resched if we changed runqueues */
@@ -1652,10 +1650,13 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p,
16521650
* Priority of the task has changed. This may cause
16531651
* us to initiate a push or pull.
16541652
*/
1655-
static void prio_changed_rt(struct rq *rq, struct task_struct *p,
1656-
int oldprio, int running)
1653+
static void
1654+
prio_changed_rt(struct rq *rq, struct task_struct *p, int oldprio)
16571655
{
1658-
if (running) {
1656+
if (!p->se.on_rq)
1657+
return;
1658+
1659+
if (rq->curr == p) {
16591660
#ifdef CONFIG_SMP
16601661
/*
16611662
* If our priority decreases while running, we

kernel/sched_stoptask.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,13 @@ static void set_curr_task_stop(struct rq *rq)
5959
{
6060
}
6161

62-
static void switched_to_stop(struct rq *rq, struct task_struct *p,
63-
int running)
62+
static void switched_to_stop(struct rq *rq, struct task_struct *p)
6463
{
6564
BUG(); /* its impossible to change to this class */
6665
}
6766

68-
static void prio_changed_stop(struct rq *rq, struct task_struct *p,
69-
int oldprio, int running)
67+
static void
68+
prio_changed_stop(struct rq *rq, struct task_struct *p, int oldprio)
7069
{
7170
BUG(); /* how!?, what priority? */
7271
}

0 commit comments

Comments
 (0)