sched: Consider spare cpu capacity at task wake-up
authorMorten Rasmussen <morten.rasmussen@arm.com>
Mon, 6 Jul 2015 14:01:10 +0000 (15:01 +0100)
committerLeo Yan <leo.yan@linaro.org>
Tue, 10 May 2016 08:49:49 +0000 (16:49 +0800)
find_idlest_group() selects the wake-up target group purely
based on group load which leads to suboptimal choices in low load
scenarios. An idle group with reduced capacity (due to RT tasks or
different cpu type) isn't necessarily a better target than a lightly
loaded group with higher capacity.

The patch adds spare capacity as an additional group selection
parameter. The target group is now selected based on the following
criteria:

1. Return the group with the cpu with most spare capacity and this
capacity is significant if such group exists. Significant spare capacity
is currently at least 20% to spare.

2. Return the group with the lowest load, unless it is the local group
in which case NULL is returned and the search is continued at the next
(lower) level.

cc: Ingo Molnar <mingo@redhat.com>
cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Morten Rasmussen <morten.rasmussen@arm.com>
kernel/sched/fair.c

index 5e112bba1b5a67d99a62c27609506c3139c918c3..bebc8367edee1b935c4dae13a5a7a18253675005 100644 (file)
@@ -4788,9 +4788,10 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
                  int this_cpu, int sd_flag)
 {
        struct sched_group *idlest = NULL, *group = sd->groups;
-       struct sched_group *fit_group = NULL;
+       struct sched_group *fit_group = NULL, *spare_group = NULL;
        unsigned long min_load = ULONG_MAX, this_load = 0;
        unsigned long fit_capacity = ULONG_MAX;
+       unsigned long max_spare_capacity = capacity_margin - SCHED_LOAD_SCALE;
        int load_idx = sd->forkexec_idx;
        int imbalance = 100 + (sd->imbalance_pct-100)/2;
 
@@ -4798,7 +4799,7 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
                load_idx = sd->wake_idx;
 
        do {
-               unsigned long load, avg_load;
+               unsigned long load, avg_load, spare_capacity;
                int local_group;
                int i;
 
@@ -4830,6 +4831,16 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
                                fit_capacity = capacity_of(i);
                                fit_group = group;
                        }
+
+                       /*
+                        * Look for group which has most spare capacity on a
+                        * single cpu.
+                        */
+                       spare_capacity = capacity_of(i) - cpu_util(i);
+                       if (spare_capacity > max_spare_capacity) {
+                               max_spare_capacity = spare_capacity;
+                               spare_group = group;
+                       }
                }
 
                /* Adjust by relative CPU capacity of the group */
@@ -4846,6 +4857,9 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
        if (fit_group)
                return fit_group;
 
+       if (spare_group)
+               return spare_group;
+
        if (!idlest || 100*this_load < imbalance*min_load)
                return NULL;
        return idlest;