[PATCH 1/3] cpufreq: x86: allow external frequency measures

From: Redha Gouicem
Date: Tue Oct 20 2020 - 11:45:07 EST


Allow other subsystems to query the current frequency from the CPU without
affecting the cached value of cpufreq. The subsystems doing this will need
to maintain their own version of struct aperfmperf_sample.

This is useful if you need to query frequency more frequently than
APERFMPERF_CACHE_THRESHOLD_MS but don't want to mess with the current
caching behavior.

Even though querying too frequently may render the measures inaccurate, it
is still useful if your subsystem tolerates this inaccuracy.

Co-developed-by: Damien Carver <carverdamien@xxxxxxxxx>
Signed-off-by: Damien Carver <carverdamien@xxxxxxxxx>
Signed-off-by: Redha Gouicem <redha.gouicem@xxxxxxxxx>
---
arch/x86/kernel/cpu/aperfmperf.c | 31 +++++++++++++++++++++++++++----
drivers/cpufreq/cpufreq.c | 5 +++++
include/linux/cpufreq.h | 1 +
3 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c
index e2f319dc992d..c3be81d689f4 100644
--- a/arch/x86/kernel/cpu/aperfmperf.c
+++ b/arch/x86/kernel/cpu/aperfmperf.c
@@ -36,11 +36,11 @@ static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
* unless we already did it within 10ms
* calculate kHz, save snapshot
*/
-static void aperfmperf_snapshot_khz(void *dummy)
+static void aperfmperf_snapshot_khz(void *prev_sample)
{
u64 aperf, aperf_delta;
u64 mperf, mperf_delta;
- struct aperfmperf_sample *s = this_cpu_ptr(&samples);
+ struct aperfmperf_sample *s = prev_sample;
unsigned long flags;

local_irq_save(flags);
@@ -72,7 +72,8 @@ static bool aperfmperf_snapshot_cpu(int cpu, ktime_t now, bool wait)
if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
return true;

- smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, wait);
+ smp_call_function_single(cpu, aperfmperf_snapshot_khz,
+ per_cpu_ptr(&samples, cpu), wait);

/* Return false if the previous iteration was too long ago. */
return time_delta <= APERFMPERF_STALE_THRESHOLD_MS;
@@ -131,7 +132,29 @@ unsigned int arch_freq_get_on_cpu(int cpu)
return per_cpu(samples.khz, cpu);

msleep(APERFMPERF_REFRESH_DELAY_MS);
- smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
+ smp_call_function_single(cpu, aperfmperf_snapshot_khz,
+ per_cpu_ptr(&samples, cpu), 1);

return per_cpu(samples.khz, cpu);
}
+
+unsigned int arch_freq_get_on_cpu_from_sample(int cpu, void *sample)
+{
+ struct aperfmperf_sample *s = sample;
+
+ if (!sample)
+ return 0;
+
+ if (!cpu_khz)
+ return 0;
+
+ if (!boot_cpu_has(X86_FEATURE_APERFMPERF))
+ return 0;
+
+ if (!housekeeping_cpu(cpu, HK_FLAG_MISC))
+ return 0;
+
+ smp_call_function_single(cpu, aperfmperf_snapshot_khz, s, 1);
+
+ return s->khz;
+}
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 02ab56b2a0d8..36e6dbd87317 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -695,6 +695,11 @@ __weak unsigned int arch_freq_get_on_cpu(int cpu)
return 0;
}

+__weak unsigned int arch_freq_get_on_cpu_from_sample(int cpu, void *sample)
+{
+ return 0;
+}
+
static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf)
{
ssize_t ret;
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 8f141d4c859c..129083684ca0 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -1005,6 +1005,7 @@ static inline void sched_cpufreq_governor_change(struct cpufreq_policy *policy,

extern void arch_freq_prepare_all(void);
extern unsigned int arch_freq_get_on_cpu(int cpu);
+extern unsigned int arch_freq_get_on_cpu_from_sample(int cpu, void *sample);

extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
unsigned long max_freq);
--
2.28.0