summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
authorWen Yi <wyi@nvidia.com>2012-06-12 12:01:11 -0700
committerSimone Willett <swillett@nvidia.com>2012-07-01 09:15:23 -0700
commit69f4a0f9adb0d8f82b920ceffcc40f7bd770c2e2 (patch)
tree282ae29b0961e6b99b02e1235f32bac2a25039e6 /arch/arm
parent1802afb2ad9ee1b6c1e11207f3fcdd3a56a1e0f0 (diff)
ARM: tegra: power: Use runnable threads average for hotplug
Sample scheduler runnable threads average in auto-hotplug work function and use it to determine the auto-hotplug target for number of on-line cores. Use cpu up delay as sampling period, and enforce down delay by checking last cpu configuration change time stamp. Bug 958978 Change-Id: I4280a11d39914687e6ffaa6f38df594d10aedaa9 Signed-off-by: Alex Frid <afrid@nvidia.com> (cherry picked from commit 507e2ef5e4f09b23de2e924003dba259d3c8bc3c) Reviewed-on: http://git-master/r/111638 Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com> Tested-by: Sai Gurrappadi <sgurrappadi@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Peter Boonstoppel <pboonstoppel@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-tegra/cpu-tegra3.c47
1 files changed, 39 insertions, 8 deletions
diff --git a/arch/arm/mach-tegra/cpu-tegra3.c b/arch/arm/mach-tegra/cpu-tegra3.c
index 5b9ac4f770a2..01b954625e54 100644
--- a/arch/arm/mach-tegra/cpu-tegra3.c
+++ b/arch/arm/mach-tegra/cpu-tegra3.c
@@ -73,6 +73,8 @@ static struct clk *cpu_clk;
static struct clk *cpu_g_clk;
static struct clk *cpu_lp_clk;
+static unsigned long last_change_time;
+
static struct {
cputime64_t time_up_total;
u64 last_update;
@@ -186,6 +188,14 @@ enum {
TEGRA_CPU_SPEED_SKEWED,
};
+#define NR_FSHIFT 2
+static unsigned int nr_run_thresholds[] = {
+/* 1, 2, 3, 4 - on-line cpus target */
+ 5, 9, 13, UINT_MAX /* avg run threads * 4 (e.g., 9 = 2.25 threads) */
+};
+static unsigned int nr_run_hysteresis = 2; /* 0.5 thread */
+static unsigned int nr_run_last;
+
static noinline int tegra_cpu_speed_balance(void)
{
unsigned long highest_speed = tegra_cpu_highest_speed();
@@ -194,17 +204,36 @@ static noinline int tegra_cpu_speed_balance(void)
unsigned int nr_cpus = num_online_cpus();
unsigned int max_cpus = pm_qos_request(PM_QOS_MAX_ONLINE_CPUS) ? : 4;
unsigned int min_cpus = pm_qos_request(PM_QOS_MIN_ONLINE_CPUS);
+ unsigned int avg_nr_run = avg_nr_running();
+ unsigned int nr_run;
+
+ /* Evaluate:
+ * - distribution of freq targets for already on-lined CPUs
+ * - average number of runnable threads
+ * - effective MIPS available within EDP frequency limits,
+ * and return:
+ * TEGRA_CPU_SPEED_BALANCED to bring one more CPU core on-line
+ * TEGRA_CPU_SPEED_BIASED to keep CPU core composition unchanged
+ * TEGRA_CPU_SPEED_SKEWED to remove CPU core off-line
+ */
+ for (nr_run = 1; nr_run < ARRAY_SIZE(nr_run_thresholds); nr_run++) {
+ unsigned int nr_threshold = nr_run_thresholds[nr_run - 1];
+ if (nr_run_last <= nr_run)
+ nr_threshold += nr_run_hysteresis;
+ if (avg_nr_run <= (nr_threshold << (FSHIFT - NR_FSHIFT)))
+ break;
+ }
+ nr_run_last = nr_run;
- /* balanced: freq targets for all CPUs are above 50% of highest speed
- biased: freq target for at least one CPU is below 50% threshold
- skewed: freq targets for at least 2 CPUs are below 25% threshold */
if (((tegra_count_slow_cpus(skewed_speed) >= 2) ||
+ (nr_run < nr_cpus) ||
tegra_cpu_edp_favor_down(nr_cpus, mp_overhead) ||
(highest_speed <= idle_bottom_freq) || (nr_cpus > max_cpus)) &&
(nr_cpus > min_cpus))
return TEGRA_CPU_SPEED_SKEWED;
if (((tegra_count_slow_cpus(balanced_speed) >= 1) ||
+ (nr_run <= nr_cpus) ||
(!tegra_cpu_edp_favor_up(nr_cpus, mp_overhead)) ||
(highest_speed <= idle_bottom_freq) || (nr_cpus == max_cpus)) &&
(nr_cpus >= min_cpus))
@@ -218,7 +247,6 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
bool up = false;
unsigned int cpu = nr_cpu_ids;
unsigned long now = jiffies;
- static unsigned long last_change_time;
mutex_lock(tegra3_cpu_lock);
@@ -231,7 +259,8 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
if (cpu < nr_cpu_ids) {
up = false;
} else if (!is_lp_cluster() && !no_lp &&
- !pm_qos_request(PM_QOS_MIN_ONLINE_CPUS)) {
+ !pm_qos_request(PM_QOS_MIN_ONLINE_CPUS) &&
+ ((now - last_change_time) >= down_delay)) {
if(!clk_set_parent(cpu_clk, cpu_lp_clk)) {
hp_stats_update(CONFIG_NR_CPUS, true);
hp_stats_update(0, false);
@@ -241,11 +270,12 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
}
}
queue_delayed_work(
- hotplug_wq, &hotplug_work, down_delay);
+ hotplug_wq, &hotplug_work, up2gn_delay);
break;
case TEGRA_HP_UP:
if (is_lp_cluster() && !no_lp) {
if(!clk_set_parent(cpu_clk, cpu_g_clk)) {
+ last_change_time = now;
hp_stats_update(CONFIG_NR_CPUS, false);
hp_stats_update(0, true);
/* catch-up with governor target speed */
@@ -307,6 +337,7 @@ static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
tegra_update_cpu_speed(speed);
if (!clk_set_parent(cpu_clk, cpu_g_clk)) {
+ last_change_time = jiffies;
hp_stats_update(CONFIG_NR_CPUS, false);
hp_stats_update(0, true);
}
@@ -372,7 +403,7 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend)
} else if (cpu_freq <= bottom_freq) {
hp_state = TEGRA_HP_DOWN;
queue_delayed_work(
- hotplug_wq, &hotplug_work, down_delay);
+ hotplug_wq, &hotplug_work, up_delay);
}
break;
case TEGRA_HP_DOWN:
@@ -388,7 +419,7 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend)
if (cpu_freq <= bottom_freq) {
hp_state = TEGRA_HP_DOWN;
queue_delayed_work(
- hotplug_wq, &hotplug_work, down_delay);
+ hotplug_wq, &hotplug_work, up_delay);
} else if (cpu_freq <= top_freq) {
hp_state = TEGRA_HP_IDLE;
}