Blob Blame History Raw
From 9ba19a3f643b7e9bfe1ca6842acfbbb8819b8159 Mon Sep 17 00:00:00 2001
From: Mel Gorman <mgorman@suse.de>
Date: Thu, 4 Jan 2018 11:28:56 +0000
Subject: [PATCH] cpufreq: intel_pstate: Temporarily boost P-state when exiting
 from idle

References: bnc#1066110, bnc#1066845
Patch-mainline: No, upstream favours power consumption over performance

cpufreq state is inherently per-cpu and while utilisation of a task
influences the p-state selected for a CPU, there is a lag when a task
migrates to a new CPU. The lag before true utilisation is known is related to
both PELT calculations and the frequency samples are taken. For tasks that
recently migrate or are naturally low utilisation, the higher p-states may
never be reached. This patch temporarily boosts p-state when exiting from
idle similar to how it's boosted if a task has blocked recently for IO.
It takes care to avoid boosting for the idle task entering idle and makes
sure the initial target p-state is based on the boosted utilisation.

Signed-off-by: Mel Gorman <mgorman@suse.de>
---
 drivers/cpufreq/intel_pstate.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index c10149c656a5..3a157a89eab3 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -222,6 +222,7 @@ struct global_params {
  * @update_util:	CPUFreq utility callback information
  * @update_util_set:	CPUFreq utility callback is set
  * @iowait_boost:	iowait-related boost fraction
+ * @idlewait_boost:	idle-related boost fraction
  * @last_update:	Time of the last update.
  * @pstate:		Stores P state limits for this CPU
  * @vid:		Stores VID limits for this CPU
@@ -277,6 +278,7 @@ struct cpudata {
 	bool valid_pss_table;
 #endif
 	unsigned int iowait_boost;
+	unsigned int idle_boost;
 	s16 epp_powersave;
 	s16 epp_policy;
 	s16 epp_default;
@@ -1633,14 +1635,24 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
 	busy_frac = div_fp(sample->mperf << cpu->aperf_mperf_shift,
 			   sample->tsc);
 
+	/* IO-wait boosting */
 	boost = cpu->iowait_boost;
 	cpu->iowait_boost >>= 1;
-
 	if (busy_frac < boost)
 		busy_frac = boost;
-
 	sample->busy_scaled = busy_frac * 100;
 
+	/* Exit from long idle boosting */
+	if (cpu->idle_boost &&
+	    pid_params.setpoint == CPUFREQ_SERVER_DEFAULT_SETPOINT) {
+		boost = max_t(int32_t, pid_params.setpoint, cpu->idle_boost);
+		cpu->idle_boost >>= 1;
+		if (busy_frac < boost && !is_idle_task(current)) {
+			busy_frac = boost;
+			sample->busy_scaled = boost * 100;
+		}
+	}
+
 	max_target = global.no_turbo || global.turbo_disabled ?
 			cpu->pstate.max_pstate : cpu->pstate.turbo_pstate;
 	max_target += max_target >> 2;
@@ -1789,11 +1801,16 @@ static void intel_pstate_update_util(struct update_util_data *data, u64 time,
 			return;
 
 		goto set_pstate;
-	} else if (cpu->iowait_boost) {
-		/* Clear iowait_boost if the CPU may have been idle. */
+	} else {
 		delta_ns = time - cpu->last_update;
-		if (delta_ns > TICK_NSEC)
-			cpu->iowait_boost = 0;
+		if (delta_ns > TICK_NSEC) {
+			/* Clear iowait_boost if the CPU may have been idle. */
+			if (cpu->iowait_boost)
+				cpu->iowait_boost = 0;
+
+			if (!is_idle_task(current))
+				cpu->idle_boost = int_tofp(1);
+		}
 	}
 	cpu->last_update = time;
 	delta_ns = time - cpu->sample.time;