From f83262408293795e5186e9d1bf66d525b24fdb12 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 12 Aug 2014 14:50:54 +0100 Subject: [PATCH 1/2] HMP: Do not fork-boost tasks coming from PIDs <= 2 System services are generally started by init, whilst kernel threads are started by kthreadd. We do not want to give those tasks a head start, as this costs power for very little benefit. We do however wish to do that for tasks which the user launches. Further, some tasks allocate per-cpu timers directly after launch which can lead to those tasks being always scheduled on a big CPU when there is no computational need to do so. Not promoting services to big CPUs on launch will prevent that unless a service allocates their per-cpu resources after a period of intense computation, which is not a common pattern. Signed-off-by: Chris Redpath Signed-off-by: Jon Medhurst --- include/linux/sched.h | 8 ++++++++ kernel/sched/core.c | 6 +++--- kernel/sched/fair.c | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 0e2a546cdade..b36dd2de437d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -946,6 +946,14 @@ struct sched_avg { u32 usage_avg_sum; }; +#ifdef CONFIG_SCHED_HMP +/* + * We want to avoid boosting any processes forked from init (PID 1) + * and kthreadd (assumed to be PID 2). + */ +#define hmp_task_should_forkboost(task) ((task->parent && task->parent->pid > 2)) +#endif + #ifdef CONFIG_SCHEDSTATS struct sched_statistics { u64 wait_start; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5f242330ef85..65aaa1c78ca1 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1629,9 +1629,9 @@ static void __sched_fork(struct task_struct *p) #ifdef CONFIG_SCHED_HMP /* keep LOAD_AVG_MAX in sync with fair.c if load avg series is changed */ #define LOAD_AVG_MAX 47742 - if (p->mm) { - p->se.avg.hmp_last_up_migration = 0; - p->se.avg.hmp_last_down_migration = 0; + p->se.avg.hmp_last_up_migration = 0; + p->se.avg.hmp_last_down_migration = 0; + if (hmp_task_should_forkboost(p)) { p->se.avg.load_avg_ratio = 1023; p->se.avg.load_avg_contrib = (1023 * scale_load_down(p->se.load.weight)); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 161da1ab3995..74a5adfefeb7 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4358,7 +4358,7 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags) #ifdef CONFIG_SCHED_HMP /* always put non-kernel forking tasks on a big domain */ - if (p->mm && (sd_flag & SD_BALANCE_FORK)) { + if (unlikely(sd_flag & SD_BALANCE_FORK) && hmp_task_should_forkboost(p)) { new_cpu = hmp_select_faster_cpu(p, prev_cpu); if (new_cpu != NR_CPUS) { hmp_next_up_delay(&p->se, new_cpu); From e482d95c1d1888f34cc3f7e6778806cfda6174ff Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 12 Aug 2014 14:50:55 +0100 Subject: [PATCH 2/2] hmp: Restrict ILB events if no CPU has > 1 task Frequently in HMP, the big CPUs are only active with one task per CPU and there may be idle CPUs in the big cluster. This patch avoids triggering an idle balance in situations where none of the active CPUs in the current HMP domain have > 1 tasks running. When packing is enabled, only enforce this behaviour when we are not in the smallest domain - there we idle balance whenever a CPU is over the up_threshold regardless of tasks in case one needs to be moved. Signed-off-by: Chris Redpath Signed-off-by: Jon Medhurst --- kernel/sched/fair.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 74a5adfefeb7..fd57f0be5b4e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6510,16 +6510,16 @@ static int nohz_test_cpu(int cpu) * Decide if the tasks on the busy CPUs in the * littlest domain would benefit from an idle balance */ -static int hmp_packing_ilb_needed(int cpu) +static int hmp_packing_ilb_needed(int cpu, int ilb_needed) { struct hmp_domain *hmp; - /* always allow ilb on non-slowest domain */ + /* allow previous decision on non-slowest domain */ if (!hmp_cpu_is_slowest(cpu)) - return 1; + return ilb_needed; /* if disabled, use normal ILB behaviour */ if (!hmp_packing_enabled) - return 1; + return ilb_needed; hmp = hmp_cpu_domain(cpu); for_each_cpu_and(cpu, &hmp->cpus, nohz.idle_cpus_mask) { @@ -6531,19 +6531,34 @@ static int hmp_packing_ilb_needed(int cpu) } #endif +DEFINE_PER_CPU(cpumask_var_t, ilb_tmpmask); + static inline int find_new_ilb(int call_cpu) { int ilb = cpumask_first(nohz.idle_cpus_mask); #ifdef CONFIG_SCHED_HMP - int ilb_needed = 1; + int ilb_needed = 0; + int cpu; + struct cpumask* tmp = per_cpu(ilb_tmpmask, smp_processor_id()); /* restrict nohz balancing to occur in the same hmp domain */ ilb = cpumask_first_and(nohz.idle_cpus_mask, &((struct hmp_domain *)hmp_cpu_domain(call_cpu))->cpus); + /* check to see if it's necessary within this domain */ + cpumask_andnot(tmp, + &((struct hmp_domain *)hmp_cpu_domain(call_cpu))->cpus, + nohz.idle_cpus_mask); + for_each_cpu(cpu, tmp) { + if (cpu_rq(cpu)->nr_running > 1) { + ilb_needed = 1; + break; + } + } + #ifdef CONFIG_SCHED_HMP_LITTLE_PACKING if (ilb < nr_cpu_ids) - ilb_needed = hmp_packing_ilb_needed(ilb); + ilb_needed = hmp_packing_ilb_needed(ilb, ilb_needed); #endif if (ilb_needed && ilb < nr_cpu_ids && idle_cpu(ilb))