PELT initial task load and wake_up_new_task()
In init_entity_runnable_average() the last_update_time is initialized to zero. The task is given max load and utilization as a pessimistic initial estimate.
But if in wake_up_new_task() the task is placed on a CPU other than where it was created, __update_load_avg() will be called via set_task_cpu() -> migrate_task_rq_fair() -> remove_entity_load_avg().
Since last_update_time is zero the delta will be huge and the task's load will be entirely decayed away before it is enqueued at the destination CPU.
If last_update_time is initialized to cfs_rq_clock_task() the load will not go away, but it will also then be subtracted from the original CPU in remove_entity_load_avg() if the task is placed on a different CPU, which is bad since it was never added there before.
Thinking about this more it seemed questionable to treat the assignment of a task to a new CPU in wake_up_new_task() as a migration given that the task has never executed previously. Would it make sense to call __set_task_cpu() there instead of set_task_cpu()?
thanks,
Steve
Hi Steve,
Quoted 11 lines · Steve Muckle
In
init_entity_runnable_average()thelast_update_timeis initialized to zero. The task is given max load and utilization as a pessimistic initial estimate.But if in
wake_up_new_task()the task is placed on a CPU other than where it was created,__update_load_avg()will be called viaset_task_cpu()->migrate_task_rq_fair()->remove_entity_load_avg().Since
last_update_timeis zero the delta will be huge and the task's load will be entirely decayed away before it is enqueued at the destination CPU.
Since the new task's last_update_time is equal to 0, it will not be decayed.
Hi Yuyang,
Quoted 11 lines · Yuyang Du
Hi Steve,
Quoted 8 lines · Steve Muckle
In
init_entity_runnable_average()thelast_update_timeis initialized to zero. The task is given max load and utilization as a pessimistic initial estimate.But if in
wake_up_new_task()the task is placed on a CPU other than where it was created,__update_load_avg()will be called viaset_task_cpu()->migrate_task_rq_fair()->remove_entity_load_avg().
Quoted 5 lines
Quoted 3 lines
Since
last_update_timeis zero the delta will be huge and the task's load will be entirely decayed away before it is enqueued at the destination CPU.Since the new task's
last_update_timeis equal to 0, it will not be decayed.
Can you point me to the code for that logic? I don't see anything that prevents the decay when a newly woken task is placed on a different CPU via the call chain I mentioned above. My testing also shows the load being decayed to zero.
thanks
Steve
Quoted 18 lines · Steve Muckle
Hi Yuyang,
Quoted 15 lines · Yuyang Du
Hi Steve,
Quoted 11 lines · Steve Muckle
In
init_entity_runnable_average()thelast_update_timeis initialized to zero. The task is given max load and utilization as a pessimistic initial estimate.But if in
wake_up_new_task()the task is placed on a CPU other than where it was created,__update_load_avg()will be called viaset_task_cpu()->migrate_task_rq_fair()->remove_entity_load_avg().Since
last_update_timeis zero the delta will be huge and the task's load will be entirely decayed away before it is enqueued at the destination CPU.
Quoted 7 lines
Quoted 1 line
Since the new task's
last_update_timeis equal to 0, it will not be decayed.Can you point me to the code for that logic? I don't see anything that prevents the decay when a newly woken task is placed on a different CPU via the call chain I mentioned above. My testing also shows the load being decayed to zero.
You may search the last_update_time, and see it would be treated differently if it is 0. Hope this may be helpful.
Quoted 15 lines · Yuyang Du
Quoted 15 lines
Quoted 14 lines · Steve Muckle
Quoted 11 lines
In
init_entity_runnable_average()thelast_update_timeis initialized to zero. The task is given max load and utilization as a pessimistic initial estimate.But if in
wake_up_new_task()the task is placed on a CPU other than where it was created,__update_load_avg()will be called viaset_task_cpu()->migrate_task_rq_fair()->remove_entity_load_avg().Since
last_update_timeis zero the delta will be huge and the task's load will be entirely decayed away before it is enqueued at the destination CPU.Since the new task's
last_update_timeis equal to 0, it will not be decayed.
Quoted 7 lines
Quoted 5 lines
Can you point me to the code for that logic? I don't see anything that prevents the decay when a newly woken task is placed on a different CPU via the call chain I mentioned above. My testing also shows the load being decayed to zero.
You may search the
last_update_time, and see it would be treated differently if it is 0. Hope this may be helpful.
Are you referring to the test in enqueue_entity_load_avg()? If so that isn't called until after remove_entity_load_avg() in this scenario, which has no check on last_update_time.
thanks,
Steve
Quoted 21 lines · Steve Muckle
Quoted 20 lines · Yuyang Du
Quoted 20 lines
Quoted 9 lines · Steve Muckle
Quoted 8 lines
In
init_entity_runnable_average()thelast_update_timeis initialized to zero. The task is given max load and utilization as a pessimistic initial estimate.But if in
wake_up_new_task()the task is placed on a CPU other than where it was created,__update_load_avg()will be called viaset_task_cpu()->migrate_task_rq_fair()->remove_entity_load_avg().Quoted 5 lines
Quoted 3 lines
Since
last_update_timeis zero the delta will be huge and the task's load will be entirely decayed away before it is enqueued at the destination CPU.Since the new task's
last_update_timeis equal to 0, it will not be decayed.Can you point me to the code for that logic? I don't see anything that prevents the decay when a newly woken task is placed on a different CPU via the call chain I mentioned above. My testing also shows the load being decayed to zero.
Quoted 6 lines
Quoted 2 lines
You may search the
last_update_time, and see it would be treated differently if it is 0. Hope this may be helpful.Are you referring to the test in
enqueue_entity_load_avg()? If so that isn't called until afterremove_entity_load_avg()in this scenario, which has no check onlast_update_time.
Indeed it is. Sorry that I did not look at this carefully before.
I think it should still be regarded as migration. It looks better as such.
Hope the following patch should work.
---
Subject: [PATCH] sched: Fix new task's load avg removed from source CPU in
wake_up_new_task()
If a newly created task is selected to go to a different CPU in fork balance when it wakes up the first time, its load averages should not be removed from the source CPU since they are never added to it before.
Fix it in remove_entity_load_avg(): when entity's last_update_time is 0, simply return. This should precisely identify the case in question, because in normal migration, the last_update_time is set to 0 after remove_entity_load_avg().
--- kernel/sched/fair.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/sched/fair.c +6 -0
@@ -2909,6 +2909,12 @@ void remove_entity_load_avg(struct sched_entity *se)struct cfs_rq *cfs_rq =cfs_rq_of (se);u64 last_update_time; +/* +* Newly created task should not be removed from the source CPU before migration +*/ +if (se->avg.last_update_time ==0 ) +return ; +#ifndef CONFIG_64BIT u64 last_update_time_copy;
kbuild test robot
build botRe: [PATCH] sched: Fix new task's load avg removed from source CPU in
reproduce:
Hi Yuyang,
[auto build test WARNING on tip/sched/core] [also build test WARNING on v4.4-rc5 next-20151216]
url: https://github.com/0day-ci/linux/commits/Yuyang-Du/sched-Fix-new-task-s-load-avg-removed-from-source-CPU-in/20151216-154529 config: i386-randconfig-x000-12141102 (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=i386
All warnings (new ones prefixed by >>):
kernel/sched/fair.c: In function 'remove_entity_load_avg':
>> kernel/sched/fair.c:2919:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
u64 last_update_time_copy;
^vim +2919 kernel/sched/fair.c
9ee474f5 Paul Turner 2012-10-04 2903 /*
9d89c257 Yuyang Du 2015-07-15 2904 * Task first catches up with cfs_rq, and then subtract
9d89c257 Yuyang Du 2015-07-15 2905 * itself from the cfs_rq (task must be off the queue now).
9ee474f5 Paul Turner 2012-10-04 2906 */
9d89c257 Yuyang Du 2015-07-15 2907 void remove_entity_load_avg(struct sched_entity *se)
2dac754e Paul Turner 2012-10-04 2908 {
9d89c257 Yuyang Du 2015-07-15 2909 struct cfs_rq *cfs_rq = cfs_rq_of(se);
9d89c257 Yuyang Du 2015-07-15 2910 u64 last_update_time;
9d89c257 Yuyang Du 2015-07-15 2911
a13869ac Yuyang Du 2015-12-16 2912 /*
a13869ac Yuyang Du 2015-12-16 2913 * Newly created task should not be removed from the source CPU before migration
a13869ac Yuyang Du 2015-12-16 2914 */
a13869ac Yuyang Du 2015-12-16 2915 if (se->avg.last_update_time == 0)
a13869ac Yuyang Du 2015-12-16 2916 return;
a13869ac Yuyang Du 2015-12-16 2917
9d89c257 Yuyang Du 2015-07-15 2918 #ifndef CONFIG_64BIT
9d89c257 Yuyang Du 2015-07-15 @2919 u64 last_update_time_copy;
9ee474f5 Paul Turner 2012-10-04 2920
9d89c257 Yuyang Du 2015-07-15 2921 do {
9d89c257 Yuyang Du 2015-07-15 2922 last_update_time_copy = cfs_rq->load_last_update_time_copy;
9d89c257 Yuyang Du 2015-07-15 2923 smp_rmb();
9d89c257 Yuyang Du 2015-07-15 2924 last_update_time = cfs_rq->avg.last_update_time;
9d89c257 Yuyang Du 2015-07-15 2925 } while (last_update_time != last_update_time_copy);
9d89c257 Yuyang Du 2015-07-15 2926 #else
9d89c257 Yuyang Du 2015-07-15 2927 last_update_time = cfs_rq->avg.last_update_time;:::::: The code at line 2919 was first introduced by commit
:::::: 9d89c257dfb9c51a532d69397f6eed75e5168c35 sched/fair: Rewrite runnable load and utilization average tracking
:::::: TO: Yuyang Du <[email protected]>
:::::: CC: Ingo Molnar <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Quoted 1 line · Yuyang Du
Hope the following patch should work.
Thanks Yuyang. AFAICS it should work, though I believe the test on last_update_time could instead go at the top of migrate_task_rq_fair()? It'd save the fn call to remove_entity_load_avg() and two unnecessary assignments (as p->se.avg.last_update_time and p->se.exec_start = 0 for newly forked tasks). This worked for me.
thanks,
Steve
Hi Steve,
Quoted 8 lines · Steve Muckle
Quoted 1 line · Yuyang Du
Hope the following patch should work.
Thanks Yuyang. AFAICS it should work, though I believe the test on
last_update_timecould instead go at the top ofmigrate_task_rq_fair()? It'd save the fn call toremove_entity_load_avg()and two unnecessary assignments (asp->se.avg.last_update_timeandp->se.exec_start= 0 for newly forked tasks). This worked for me.
In a hindsight yesterday, this also occurred to me. But I think the fix is also applicable to a group entity that is being removed but never used. And such cases are not unlikely to happen.
To make the code less duplicate, I still do this in remove_entity_load_avg(). Make sense?
Sorry about the compile error.
---
Subject: [PATCH] sched: Fix new task's load avg removed from source CPU in
wake_up_new_task()
If a newly created task is selected to go to a different CPU in fork balance when it wakes up the first time, its load averages should not be removed from the source CPU since they are never added to it before. The same is also applicable to a never used group entity.
Fix it in remove_entity_load_avg(): when entity's last_update_time is 0, simply return. This should precisely identify the case in question, because in other migrations, the last_update_time is set to 0 after remove_entity_load_avg().
--- kernel/sched/fair.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
kernel/sched/fair.c +9 -1
@@ -2908,10 +2908,18 @@ void remove_entity_load_avg(struct sched_entity *se) {struct cfs_rq *cfs_rq =cfs_rq_of (se);u64 last_update_time; -#ifndef CONFIG_64BIT u64 last_update_time_copy; +#endif +/* +* Newly created task or never used group entity should not be removed +* from its (source) cfs_rq +*/ +if (se->avg.last_update_time ==0 ) +return ; + +#ifndef CONFIG_64BIT do { last_update_time_copy = cfs_rq->load_last_update_time_copy; smp_rmb();
Peter Zijlstra
maintainerQuoted 14 lines · Yuyang Du
kernel/sched/fair.c +2
-1@@ -2908,10 +2908,18 @@ void remove_entity_load_avg(struct sched_entity *se) {struct cfs_rq *cfs_rq =cfs_rq_of (se);u64 last_update_time; -#ifndef CONFIG_64BIT u64 last_update_time_copy; +#endif +/*
kernel/sched/fair.c void remove_entity_load_avg(struct sched_entity *se) · line 2925
Quoted 10 lines
kernel/sched/fair.c +7
-0+* Newly created task or never used group entity should not be removed +* from its (source) cfs_rq +*/ +if (se->avg.last_update_time ==0 ) +return ; + +#ifndef CONFIG_64BIT do { last_update_time_copy = cfs_rq->load_last_update_time_copy; smp_rmb();
So that ifdef stuff annoyed me a wee bit, so I did the below.
Initially I wanted to do a macro that we could use for both this and the vruntime, but that didn't end up particularly pretty either, so I scrapped that.
---
Subject: sched: Fix new task's load avg removed from source CPU in wake_up_new_task()
From: Yuyang Du <[email protected]>
Date: Thu, 17 Dec 2015 07:34:27 +0800
If a newly created task is selected to go to a different CPU in fork balance when it wakes up the first time, its load averages should not be removed from the source CPU since they are never added to it before. The same is also applicable to a never used group entity.
Fix it in remove_entity_load_avg(): when entity's last_update_time is 0, simply return. This should precisely identify the case in question, because in other migrations, the last_update_time is set to 0 after remove_entity_load_avg().
--- kernel/sched/fair.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-)
kernel/sched/fair.c +28 -10
@@ -2809,27 +2809,45 @@ dequeue_entity_load_avg(struct cfs_rq *cmax_t (s64, cfs_rq->runnable_load_sum - se->avg.load_sum,0 ); } -/* -* Task first catches up with cfs_rq, and then subtract -* itself from the cfs_rq (task must be off the queue now). -*/ -void remove_entity_load_avg (struct sched_entity *se) -{ -struct cfs_rq *cfs_rq =cfs_rq_of (se); -u64 last_update_time; -#ifndef CONFIG_64BIT +static inline u64 cfs_rq_last_update_time (struct cfs_rq *cfs_rq) +{u64 last_update_time_copy; +u64 last_update_time;do { last_update_time_copy = cfs_rq->load_last_update_time_copy;smp_rmb (); last_update_time = cfs_rq->avg.last_update_time; }while (last_update_time != last_update_time_copy); + +return last_update_time; +}#else - last_update_time = cfs_rq->avg.last_update_time; +static inline u64 cfs_rq_last_update_time (struct cfs_rq *cfs_rq) +{ +return cfs_rq->avg.last_update_time; +}#endif +/* +* Task first catches up with cfs_rq, and then subtract +* itself from the cfs_rq (task must be off the queue now). +*/ +void remove_entity_load_avg (struct sched_entity *se) +{ +struct cfs_rq *cfs_rq =cfs_rq_of (se); +u64 last_update_time; + +/* +* Newly created task or never used group entity should not be removed +* from its (source) cfs_rq +*/ +if (se->avg.last_update_time ==0 ) +return ; + + last_update_time =cfs_rq_last_update_time (cfs_rq); +__update_load_avg (last_update_time,cpu_of (rq_of (cfs_rq)), &se->avg,0 ,0 ,NULL );atomic_long_add (se->avg.load_avg, &cfs_rq->removed_load_avg);atomic_long_add (se->avg.util_avg, &cfs_rq->removed_util_avg);
Quoted 28 lines · Peter Zijlstra
Quoted 14 lines · Yuyang Du
kernel/sched/fair.c +2
-1@@ -2908,10 +2908,18 @@ void remove_entity_load_avg(struct sched_entity *se) {struct cfs_rq *cfs_rq =cfs_rq_of (se);u64 last_update_time; -#ifndef CONFIG_64BIT u64 last_update_time_copy; +#endif +/* kernel/sched/fair.c void remove_entity_load_avg(struct sched_entity *se) · line 2925
Quoted 10 lines
kernel/sched/fair.c +7
-0+* Newly created task or never used group entity should not be removed +* from its (source) cfs_rq +*/ +if (se->avg.last_update_time ==0 ) +return ; + +#ifndef CONFIG_64BIT do { last_update_time_copy = cfs_rq->load_last_update_time_copy; smp_rmb();So that ifdef stuff annoyed me a wee bit, so I did the below.
Quoted 3 lines
Initially I wanted to do a macro that we could use for both this and the vruntime, but that didn't end up particularly pretty either, so I scrapped that.
Oh yeah, looks good. :)
Quoted 88 lines
---
Subject: sched: Fix new task's load avg removed from source CPU inwake_up_new_task()
From: Yuyang Du <[email protected]>
Date: Thu, 17 Dec 2015 07:34:27 +0800If a newly created task is selected to go to a different CPU in fork balance when it wakes up the first time, its load averages should not be removed from the source CPU since they are never added to it before. The same is also applicable to a never used group entity.
Fix it in
remove_entity_load_avg(): when entity'slast_update_timeis 0, simply return. This should precisely identify the case in question, because in other migrations, thelast_update_timeis set to 0 afterremove_entity_load_avg().Reported-by Steve Muckle <[email protected]>Signed-off-by Yuyang Du <[email protected]>Cc Patrick Bellasi <[email protected]>Cc Ingo Molnar <[email protected]>Cc Vincent Guittot <[email protected]>Cc Juri Lelli <[email protected]>Cc Dietmar Eggemann <[email protected]>Cc Morten Rasmussen <[email protected]>[peterz: cfs_rq_last_update_time]Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- kernel/sched/fair.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-)kernel/sched/fair.c +28
-10@@ -2809,27 +2809,45 @@ dequeue_entity_load_avg(struct cfs_rq *cmax_t (s64, cfs_rq->runnable_load_sum - se->avg.load_sum,0 ); } -/* -* Task first catches up with cfs_rq, and then subtract -* itself from the cfs_rq (task must be off the queue now). -*/ -void remove_entity_load_avg (struct sched_entity *se) -{ -struct cfs_rq *cfs_rq =cfs_rq_of (se); -u64 last_update_time; -#ifndef CONFIG_64BIT +static inline u64 cfs_rq_last_update_time (struct cfs_rq *cfs_rq) +{u64 last_update_time_copy; +u64 last_update_time;do { last_update_time_copy = cfs_rq->load_last_update_time_copy;smp_rmb (); last_update_time = cfs_rq->avg.last_update_time; }while (last_update_time != last_update_time_copy); + +return last_update_time; +}#else - last_update_time = cfs_rq->avg.last_update_time; +static inline u64 cfs_rq_last_update_time (struct cfs_rq *cfs_rq) +{ +return cfs_rq->avg.last_update_time; +}#endif +/* +* Task first catches up with cfs_rq, and then subtract +* itself from the cfs_rq (task must be off the queue now). +*/ +void remove_entity_load_avg (struct sched_entity *se) +{ +struct cfs_rq *cfs_rq =cfs_rq_of (se); +u64 last_update_time; + +/* +* Newly created task or never used group entity should not be removed +* from its (source) cfs_rq +*/ +if (se->avg.last_update_time ==0 ) +return ; + + last_update_time =cfs_rq_last_update_time (cfs_rq); +__update_load_avg (last_update_time,cpu_of (rq_of (cfs_rq)), &se->avg,0 ,0 ,NULL );atomic_long_add (se->avg.load_avg, &cfs_rq->removed_load_avg);atomic_long_add (se->avg.util_avg, &cfs_rq->removed_util_avg);
tip-bot for Yuyang Du
merge bot[tip:sched/core] sched/fair: Fix new task' s load avg removed from source CPU in wake_up_new_task()
In reply to Yuyang Dusched/fair: Fix new task's load avg removed from source CPU in wake_up_new_task()
Commit-ID: 0905f04eb21fc1c2e690bed5d0418a061d56c225 Gitweb: http://git.kernel.org/tip/0905f04eb21fc1c2e690bed5d0418a061d56c225 Author: Yuyang Du <[email protected]> AuthorDate: Thu, 17 Dec 2015 07:34:27 +0800 Committer: Ingo Molnar <[email protected]> CommitDate: Wed, 6 Jan 2016 11:06:29 +0100
sched/fair: Fix new task's load avg removed from source CPU in wake_up_new_task()
If a newly created task is selected to go to a different CPU in fork balance when it wakes up the first time, its load averages should not be removed from the source CPU since they are never added to it before. The same is also applicable to a never used group entity.
Fix it in remove_entity_load_avg(): when entity's last_update_time is 0, simply return. This should precisely identify the case in question, because in other migrations, the last_update_time is set to 0 after remove_entity_load_avg().
--- kernel/sched/fair.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-)
kernel/sched/fair.c +28 -10
@@ -2900,27 +2900,45 @@ dequeue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)max_t (s64, cfs_rq->runnable_load_sum - se->avg.load_sum,0 ); } -/* -* Task first catches up with cfs_rq, and then subtract -* itself from the cfs_rq (task must be off the queue now). -*/ -void remove_entity_load_avg (struct sched_entity *se) -{ -struct cfs_rq *cfs_rq =cfs_rq_of (se); -u64 last_update_time; -#ifndef CONFIG_64BIT +static inline u64 cfs_rq_last_update_time (struct cfs_rq *cfs_rq) +{u64 last_update_time_copy; +u64 last_update_time;do { last_update_time_copy = cfs_rq->load_last_update_time_copy;smp_rmb (); last_update_time = cfs_rq->avg.last_update_time; }while (last_update_time != last_update_time_copy); + +return last_update_time; +}#else - last_update_time = cfs_rq->avg.last_update_time; +static inline u64 cfs_rq_last_update_time (struct cfs_rq *cfs_rq) +{ +return cfs_rq->avg.last_update_time; +}#endif +/* +* Task first catches up with cfs_rq, and then subtract +* itself from the cfs_rq (task must be off the queue now). +*/ +void remove_entity_load_avg (struct sched_entity *se) +{ +struct cfs_rq *cfs_rq =cfs_rq_of (se); +u64 last_update_time; + +/* +* Newly created task or never used group entity should not be removed +* from its (source) cfs_rq +*/ +if (se->avg.last_update_time ==0 ) +return ; + + last_update_time =cfs_rq_last_update_time (cfs_rq); +__update_load_avg (last_update_time,cpu_of (rq_of (cfs_rq)), &se->avg,0 ,0 ,NULL );atomic_long_add (se->avg.load_avg, &cfs_rq->removed_load_avg);atomic_long_add (se->avg.util_avg, &cfs_rq->removed_util_avg);