| From a7fc7fb22cc8632cea0d24c26bdde9881aed1a82 Mon Sep 17 00:00:00 2001 |
| From: Quentin Perret <quentin.perret@arm.com> |
| Date: Wed, 27 Feb 2019 11:21:24 +0000 |
| Subject: [PATCH] NOUPSTREAM: ANDROID: sched/fair: Bias EAS placement for |
| latency |
| |
| Add to find_energy_efficient_cpu() a latency sensitive case which mimics |
| what was done for prefer-idle in android-4.19 and before (see [1] for |
| reference). |
| |
| This isn't strictly equivalent to the legacy algorithm but comes real |
| close, and isn't very invasive. Overall, the idea is to select the |
| biggest idle CPU we can find for latency-sensitive boosted tasks, and |
| the smallest CPU where the can fit for latency-sensitive non-boosted |
| tasks. |
| |
| The main differences with the legacy behaviour are the following: |
| |
| 1. the policy for 'prefer idle' when there isn't a single idle CPU in |
| the system is simpler now. We just pick the CPU with the highest |
| spare capacity; |
| |
| 2. the cstate awareness is implemented by minimizing the exit latency |
| rather than the idle state index. This is how it is done in the slow |
| path (find_idlest_group_cpu()), it doesn't require us to keep hooks |
| into CPUIdle, and should actually be better because what we want is |
| a CPU that can wake up quickly; |
| |
| 3. non-latency-sensitive tasks just use the standard mainline |
| energy-aware wake-up path, which decides the placement using the |
| Energy Model; |
| |
| 4. the 'boosted' and 'latency_sensitive' attributes of a task come from |
| util_clamp (which now replaces schedtune). |
| |
| [1] https://android.googlesource.com/kernel/common.git/+/c27c56105dcaaae54ecc39ef33fbfac87a1486fc |
| |
| [CPNOTE: 30/06/21] Lee: Hoping for an upstream alternative (conversation died) |
| |
| Bug: 120440300 |
| Change-Id: Ia58516906e9cb5abe08385a8cd088097043d8703 |
| Signed-off-by: Quentin Perret <quentin.perret@arm.com> |
| --- |
| kernel/sched/fair.c | 38 ++++++++++++++++++++++++++++++++++++-- |
| 1 file changed, 36 insertions(+), 2 deletions(-) |
| |
| diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c |
| index d517efe9e85f33c151aae3295378482c7c577a3d..89fa0526883cf3c2c5ec78f332bdc209f8186586 100644 |
| --- a/kernel/sched/fair.c |
| +++ b/kernel/sched/fair.c |
| @@ -7778,11 +7778,16 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sy |
| unsigned long prev_delta = ULONG_MAX, best_delta = ULONG_MAX; |
| unsigned long p_util_min = uclamp_is_used() ? uclamp_eff_value(p, UCLAMP_MIN) : 0; |
| unsigned long p_util_max = uclamp_is_used() ? uclamp_eff_value(p, UCLAMP_MAX) : 1024; |
| + int max_spare_cap_cpu_ls = prev_cpu, best_idle_cpu = -1; |
| + unsigned long max_spare_cap_ls = 0, target_cap; |
| struct root_domain *rd = this_rq()->rd; |
| int cpu, best_energy_cpu, target = -1; |
| int prev_fits = -1, best_fits = -1; |
| unsigned long best_thermal_cap = 0; |
| unsigned long prev_thermal_cap = 0; |
| + bool boosted, latency_sensitive = false; |
| + unsigned int min_exit_lat = UINT_MAX; |
| + struct cpuidle_state *idle; |
| struct sched_domain *sd; |
| struct perf_domain *pd; |
| struct energy_env eenv; |
| @@ -7816,6 +7821,9 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sy |
| goto unlock; |
| |
| eenv_task_busy_time(&eenv, p, prev_cpu); |
| + latency_sensitive = uclamp_latency_sensitive(p); |
| + boosted = uclamp_boosted(p); |
| + target_cap = boosted ? 0 : ULONG_MAX; |
| |
| for (; pd; pd = pd->next) { |
| unsigned long util_min = p_util_min, util_max = p_util_max; |
| @@ -7881,7 +7889,7 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sy |
| |
| lsub_positive(&cpu_cap, util); |
| |
| - if (cpu == prev_cpu) { |
| + if (!latency_sensitive && cpu == prev_cpu) { |
| /* Always use prev_cpu as a candidate. */ |
| prev_spare_cap = cpu_cap; |
| prev_fits = fits; |
| @@ -7896,9 +7904,32 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sy |
| max_spare_cap_cpu = cpu; |
| max_fits = fits; |
| } |
| + |
| + if (!latency_sensitive) |
| + continue; |
| + |
| + if (idle_cpu(cpu)) { |
| + cpu_cap = arch_scale_cpu_capacity(cpu); |
| + if (boosted && cpu_cap < target_cap) |
| + continue; |
| + if (!boosted && cpu_cap > target_cap) |
| + continue; |
| + idle = idle_get_state(cpu_rq(cpu)); |
| + if (idle && idle->exit_latency > min_exit_lat && |
| + cpu_cap == target_cap) |
| + continue; |
| + |
| + if (idle) |
| + min_exit_lat = idle->exit_latency; |
| + target_cap = cpu_cap; |
| + best_idle_cpu = cpu; |
| + } else if (cpu_cap > max_spare_cap_ls) { |
| + max_spare_cap_ls = cpu_cap; |
| + max_spare_cap_cpu_ls = cpu; |
| + } |
| } |
| |
| - if (max_spare_cap_cpu < 0 && prev_spare_cap < 0) |
| + if (!latency_sensitive && max_spare_cap_cpu < 0 && prev_spare_cap == 0) |
| continue; |
| |
| eenv_pd_busy_time(&eenv, cpus, p); |
| @@ -7954,6 +7985,9 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sy |
| } |
| rcu_read_unlock(); |
| |
| + if (latency_sensitive) |
| + return best_idle_cpu >= 0 ? best_idle_cpu : max_spare_cap_cpu_ls; |
| + |
| if ((best_fits > prev_fits) || |
| ((best_fits > 0) && (best_delta < prev_delta)) || |
| ((best_fits < 0) && (best_thermal_cap > prev_thermal_cap))) |
| -- |
| 2.43.0.rc2.451.g8631bc7472-goog |
| |