Mel Gorman 767090
From c3a14b0ca1837d870e8eca18cedcc1db15a36302 Mon Sep 17 00:00:00 2001
Mel Gorman 767090
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Mel Gorman 767090
Date: Fri, 28 Jul 2017 14:45:03 +0200
Mel Gorman 767090
Subject: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected
Mel Gorman 767090
Mel Gorman 767090
References: bnc#1073583 cpufreq reporting
Mel Gorman 767090
Patch-mainline: v4.13
Mel Gorman 767090
Git-commit: 4815d3c56d1e10449a44089a47544d9ba84fad0d
Mel Gorman 767090
Mel Gorman 767090
After commit f8475cef9008 "x86: use common aperfmperf_khz_on_cpu() to
Mel Gorman 767090
calculate KHz using APERF/MPERF" the scaling_cur_freq policy attribute
Mel Gorman 767090
in sysfs only behaves as expected on x86 with APERF/MPERF registers
Mel Gorman 767090
available when it is read from at least twice in a row.  The value
Mel Gorman 767090
returned by the first read may not be meaningful, because the
Mel Gorman 767090
computations in there use cached values from the previous iteration
Mel Gorman 767090
of aperfmperf_snapshot_khz() which may be stale.
Mel Gorman 767090
Mel Gorman 767090
To prevent that from happening, modify arch_freq_get_on_cpu() to
Mel Gorman 767090
call aperfmperf_snapshot_khz() twice, with a short delay between
Mel Gorman 767090
these calls, if the previous invocation of aperfmperf_snapshot_khz()
Mel Gorman 767090
was too far back in the past (specifically, more that 1s ago).
Mel Gorman 767090
Mel Gorman 767090
Also, as pointed out by Doug Smythies, aperf_delta is limited now
Mel Gorman 767090
and the multiplication of it by cpu_khz won't overflow, so simplify
Mel Gorman 767090
the s->khz computations too.
Mel Gorman 767090
Mel Gorman 767090
Fixes: f8475cef9008 "x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF"
Mel Gorman 767090
Reported-by: Doug Smythies <dsmythies@telus.net>
Mel Gorman 767090
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Mel Gorman 767090
Signed-off-by: Mel Gorman <mgorman@suse.de>
Mel Gorman 767090
---
Mel Gorman 767090
 arch/x86/kernel/cpu/aperfmperf.c | 40 ++++++++++++++++++++++++++--------------
Mel Gorman 767090
 1 file changed, 26 insertions(+), 14 deletions(-)
Mel Gorman 767090
Mel Gorman 767090
diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c
Mel Gorman 767090
index d869c8671e36..7cf7c70b6ef2 100644
Mel Gorman 767090
--- a/arch/x86/kernel/cpu/aperfmperf.c
Mel Gorman 767090
+++ b/arch/x86/kernel/cpu/aperfmperf.c
Mel Gorman 767090
@@ -8,20 +8,25 @@
Mel Gorman 767090
  * This file is licensed under GPLv2.
Mel Gorman 767090
  */
Mel Gorman 767090
 
Mel Gorman 767090
-#include <linux/jiffies.h>
Mel Gorman 767090
+#include <linux/delay.h>
Mel Gorman 767090
+#include <linux/ktime.h>
Mel Gorman 767090
 #include <linux/math64.h>
Mel Gorman 767090
 #include <linux/percpu.h>
Mel Gorman 767090
 #include <linux/smp.h>
Mel Gorman 767090
 
Mel Gorman 767090
 struct aperfmperf_sample {
Mel Gorman 767090
 	unsigned int	khz;
Mel Gorman 767090
-	unsigned long	jiffies;
Mel Gorman 767090
+	ktime_t	time;
Mel Gorman 767090
 	u64	aperf;
Mel Gorman 767090
 	u64	mperf;
Mel Gorman 767090
 };
Mel Gorman 767090
 
Mel Gorman 767090
 static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
Mel Gorman 767090
 
Mel Gorman 767090
+#define APERFMPERF_CACHE_THRESHOLD_MS	10
Mel Gorman 767090
+#define APERFMPERF_REFRESH_DELAY_MS	20
Mel Gorman 767090
+#define APERFMPERF_STALE_THRESHOLD_MS	1000
Mel Gorman 767090
+
Mel Gorman 767090
 /*
Mel Gorman 767090
  * aperfmperf_snapshot_khz()
Mel Gorman 767090
  * On the current CPU, snapshot APERF, MPERF, and jiffies
Mel Gorman 767090
@@ -33,9 +38,11 @@ static void aperfmperf_snapshot_khz(void *dummy)
Mel Gorman 767090
 	u64 aperf, aperf_delta;
Mel Gorman 767090
 	u64 mperf, mperf_delta;
Mel Gorman 767090
 	struct aperfmperf_sample *s = this_cpu_ptr(&samples);
Mel Gorman 767090
+	ktime_t now = ktime_get();
Mel Gorman 767090
+	s64 time_delta = ktime_ms_delta(now, s->time);
Mel Gorman 767090
 
Mel Gorman 767090
-	/* Don't bother re-computing within 10 ms */
Mel Gorman 767090
-	if (time_before(jiffies, s->jiffies + HZ/100))
Mel Gorman 767090
+	/* Don't bother re-computing within the cache threshold time. */
Mel Gorman 767090
+	if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
Mel Gorman 767090
 		return;
Mel Gorman 767090
 
Mel Gorman 767090
 	rdmsrl(MSR_IA32_APERF, aperf);
Mel Gorman 767090
@@ -51,22 +58,21 @@ static void aperfmperf_snapshot_khz(void *dummy)
Mel Gorman 767090
 	if (mperf_delta == 0)
Mel Gorman 767090
 		return;
Mel Gorman 767090
 
Mel Gorman 767090
-	/*
Mel Gorman 767090
-	 * if (cpu_khz * aperf_delta) fits into ULLONG_MAX, then
Mel Gorman 767090
-	 *	khz = (cpu_khz * aperf_delta) / mperf_delta
Mel Gorman 767090
-	 */
Mel Gorman 767090
-	if (div64_u64(ULLONG_MAX, cpu_khz) > aperf_delta)
Mel Gorman 767090
-		s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
Mel Gorman 767090
-	else	/* khz = aperf_delta / (mperf_delta / cpu_khz) */
Mel Gorman 767090
-		s->khz = div64_u64(aperf_delta,
Mel Gorman 767090
-			div64_u64(mperf_delta, cpu_khz));
Mel Gorman 767090
-	s->jiffies = jiffies;
Mel Gorman 767090
+	s->time = now;
Mel Gorman 767090
 	s->aperf = aperf;
Mel Gorman 767090
 	s->mperf = mperf;
Mel Gorman 767090
+
Mel Gorman 767090
+	/* If the previous iteration was too long ago, discard it. */
Mel Gorman 767090
+	if (time_delta > APERFMPERF_STALE_THRESHOLD_MS)
Mel Gorman 767090
+		s->khz = 0;
Mel Gorman 767090
+	else
Mel Gorman 767090
+		s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
Mel Gorman 767090
 }
Mel Gorman 767090
 
Mel Gorman 767090
 unsigned int arch_freq_get_on_cpu(int cpu)
Mel Gorman 767090
 {
Mel Gorman 767090
+	unsigned int khz;
Mel Gorman 767090
+
Mel Gorman 767090
 	if (!cpu_khz)
Mel Gorman 767090
 		return 0;
Mel Gorman 767090
 
Mel Gorman 767090
@@ -74,6 +80,12 @@ unsigned int arch_freq_get_on_cpu(int cpu)
Mel Gorman 767090
 		return 0;
Mel Gorman 767090
 
Mel Gorman 767090
 	smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
Mel Gorman 767090
+	khz = per_cpu(samples.khz, cpu);
Mel Gorman 767090
+	if (khz)
Mel Gorman 767090
+		return khz;
Mel Gorman 767090
+
Mel Gorman 767090
+	msleep(APERFMPERF_REFRESH_DELAY_MS);
Mel Gorman 767090
+	smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
Mel Gorman 767090
 
Mel Gorman 767090
 	return per_cpu(samples.khz, cpu);
Mel Gorman 767090
 }