In the old code the energy aware flow compares energy between two different CPUs and select the most power efficiency CPU for waken up task. But there have several cases the old code is hardly to handle:
- In old code it compares the energy between two candidate CPUs, and one of them must be the previous CPU. For some big tasks the previous CPU cannot provide required capacity, but the previous CPU is still selected by having less energy. So from performance perspective this case is wrongly to take previous CPU as candidate;
- It's possible the previous CPU is not the most power efficiency CPU in the cluster (e.g. in the same cluster another CPU can have lower OPP for the waken task); if always to compare with previous CPU energy, then the most power efficiency CPU will be missed. This case can happen when scheduler tries to figure out the task should stay on big cluster's low OPP or migrate the task to LITTLE cluster's high OPP, so if previous CPU is big core with high OPP then we should consider other CPUs in big cluster.
- If system has more then two clusters, the old code cannot handle this complex case.
This patch is to select candidate CPUs by cluster basis, it tries to select candidate CPUs from every schedule group (corresponding to every cluster on ARM big.LITTLE platform). Finally scheduler can select most power efficiency CPU among these candidate CPUs.
Signed-off-by: Leo Yan leo.yan@linaro.org --- kernel/sched/fair.c | 82 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 35 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 47f6365..487fbe5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5285,6 +5285,9 @@ static inline bool energy_aware(void) }
struct energy_env { + cpumask_t search_cpus; + int target_cpu; + struct sched_group *sg_top; struct sched_group *sg_cap; int cap_idx; @@ -6350,15 +6353,14 @@ static int energy_aware_select_candidate_cpu(struct task_struct *p, return cpu; }
-static inline int find_nrg_efficient_target(struct task_struct *p, - struct sched_domain *sd) +static inline void find_nrg_efficient_target(struct task_struct *p, + struct sched_domain *sd, + struct energy_env *eenv) { - struct sched_group *sg, *sg_target; - int target_max_cap = INT_MAX; - int target_cpu = task_cpu(p), cpu; + struct sched_group *sg; + int cpu;
sg = sd->groups; - sg_target = sg;
/* * Find group with sufficient capacity. We only get here if no cpu is @@ -6376,19 +6378,14 @@ static inline int find_nrg_efficient_target(struct task_struct *p, * Ideally we should query the energy model for the right * answer but it easily ends up in an exhaustive search. */ - if (capacity_of(max_cap_cpu) < target_max_cap && - task_fits_max(p, max_cap_cpu)) { - sg_target = sg; - target_max_cap = capacity_of(max_cap_cpu); + if (task_fits_max(p, max_cap_cpu)) { + cpu = energy_aware_select_candidate_cpu(p, sg); + if (cpu != -1) + cpumask_set_cpu(cpu, &eenv->search_cpus); } } while (sg = sg->next, sg != sd->groups);
- - cpu = energy_aware_select_candidate_cpu(p, sg_target); - if (cpu != -1) - target_cpu = cpu; - - return target_cpu; + return; }
/* @@ -6420,6 +6417,8 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync struct sched_domain *sd; int target_cpu = prev_cpu, tmp_target; bool boosted, prefer_idle; + struct energy_env eenv; + int cpu;
schedstat_inc(p, se.statistics.nr_wakeups_secb_attempts); schedstat_inc(this_rq(), eas_stats.secb_attempts); @@ -6447,6 +6446,8 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync if (!sd) goto unlock;
+ cpumask_clear(&eenv.search_cpus); + /* Find a cpu with sufficient capacity */ if (boosted || prefer_idle) { tmp_target = find_idlest_target(p, boosted, prefer_idle); @@ -6458,35 +6459,46 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync schedstat_inc(this_rq(), eas_stats.secb_idle_bt); goto unlock; } + + cpumask_set_cpu(target_cpu, &eenv.search_cpus); + cpumask_set_cpu(prev_cpu, &eenv.search_cpus); } else { - target_cpu = find_nrg_efficient_target(p, sd); + find_nrg_efficient_target(p, sd, &eenv); }
- if (target_cpu != prev_cpu) { - struct energy_env eenv = { - .util_delta = task_util(p), - .src_cpu = prev_cpu, - .dst_cpu = target_cpu, - .task = p, - }; + eenv.target_cpu = -1;
- /* Not enough spare capacity on previous cpu */ - if (cpu_overutilized(prev_cpu)) { - schedstat_inc(p, se.statistics.nr_wakeups_secb_insuff_cap); - schedstat_inc(this_rq(), eas_stats.secb_insuff_cap); - goto unlock; + for_each_cpu(cpu, &eenv.search_cpus) { + + if (eenv.target_cpu == -1) { + eenv.target_cpu = cpu; + continue; }
- if (energy_diff(&eenv) >= 0) { - schedstat_inc(p, se.statistics.nr_wakeups_secb_no_nrg_sav); - schedstat_inc(this_rq(), eas_stats.secb_no_nrg_sav); - target_cpu = prev_cpu; - goto unlock; + if (unlikely(!task_util(p))) { + if (capacity_orig_of(cpu) < capacity_orig_of(eenv.target_cpu)) + eenv.target_cpu = cpu; + + continue; }
+ eenv.util_delta = task_util(p); + eenv.src_cpu = eenv.target_cpu; + eenv.dst_cpu = cpu; + eenv.task = p; + + if (energy_diff(&eenv) < 0) + eenv.target_cpu = cpu; + } + + if (eenv.target_cpu == -1) { + schedstat_inc(p, se.statistics.nr_wakeups_secb_no_nrg_sav); + schedstat_inc(this_rq(), eas_stats.secb_no_nrg_sav); + target_cpu = prev_cpu; + } else { schedstat_inc(p, se.statistics.nr_wakeups_secb_nrg_sav); schedstat_inc(this_rq(), eas_stats.secb_nrg_sav); - goto unlock; + target_cpu = eenv.target_cpu; }
schedstat_inc(p, se.statistics.nr_wakeups_secb_count); -- 1.9.1