diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/sched/fair.c | 57 |
1 files changed, 47 insertions, 10 deletions
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 460e7cfef2d6..2cb70c88d18a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3051,6 +3051,11 @@ static inline int power_cost(struct task_struct *p, int cpu) return SCHED_CAPACITY_SCALE; } +static unsigned int power_cost_at_freq(int cpu, unsigned int freq) +{ + return 1; +} + static inline int mostly_idle_cpu(int cpu) { return 0; @@ -8025,6 +8030,10 @@ static int idle_balance(struct rq *this_rq) struct sched_domain *sd; int pulled_task = 0; u64 curr_cost = 0; + int i, cost; + int min_power = INT_MAX; + int balance_cpu = -1; + struct rq *balance_rq = NULL; idle_enter_fair(this_rq); @@ -8045,30 +8054,58 @@ static int idle_balance(struct rq *this_rq) goto out; } + /* + * If this CPU is not the most power-efficient idle CPU in the + * lowest level domain, run load balance on behalf of that + * most power-efficient idle CPU. + */ + rcu_read_lock(); + sd = rcu_dereference_check_sched_domain(this_rq->sd); + if (sd && sysctl_sched_enable_power_aware) { + for_each_cpu(i, sched_domain_span(sd)) { + if (i == this_cpu || idle_cpu(i)) { + cost = power_cost_at_freq(i, 0); + if (cost < min_power) { + min_power = cost; + balance_cpu = i; + } + } + } + BUG_ON(balance_cpu == -1); + + } else { + balance_cpu = this_cpu; + } + rcu_read_unlock(); + balance_rq = cpu_rq(balance_cpu); + raw_spin_unlock(&this_rq->lock); - update_blocked_averages(this_cpu); + update_blocked_averages(balance_cpu); rcu_read_lock(); - for_each_domain(this_cpu, sd) { + for_each_domain(balance_cpu, sd) { int continue_balancing = 1; u64 t0, domain_cost; if (!(sd->flags & SD_LOAD_BALANCE)) continue; - if (this_rq->avg_idle < curr_cost + sd->max_newidle_lb_cost) { + if (balance_rq->avg_idle < curr_cost + sd->max_newidle_lb_cost) { update_next_balance(sd, 0, &next_balance); break; } if (sd->flags & SD_BALANCE_NEWIDLE) { - t0 = sched_clock_cpu(this_cpu); + t0 = sched_clock_cpu(balance_cpu); - pulled_task = load_balance(this_cpu, this_rq, - sd, CPU_NEWLY_IDLE, + pulled_task = load_balance(balance_cpu, balance_rq, + sd, + (this_cpu == balance_cpu ? + CPU_NEWLY_IDLE : + CPU_IDLE), &continue_balancing); - domain_cost = sched_clock_cpu(this_cpu) - t0; + domain_cost = sched_clock_cpu(balance_cpu) - t0; if (domain_cost > sd->max_newidle_lb_cost) sd->max_newidle_lb_cost = domain_cost; @@ -8079,9 +8116,9 @@ static int idle_balance(struct rq *this_rq) /* * Stop searching for tasks to pull if there are - * now runnable tasks on this rq. + * now runnable tasks on the balance rq. */ - if (pulled_task || this_rq->nr_running > 0) + if (pulled_task || balance_rq->nr_running > 0) break; } rcu_read_unlock(); @@ -8108,7 +8145,7 @@ out: if (this_rq->nr_running != this_rq->cfs.h_nr_running) pulled_task = -1; - if (pulled_task) { + if (pulled_task && balance_cpu == this_cpu) { idle_exit_fair(this_rq); this_rq->idle_stamp = 0; } |
