aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2017-06-24 01:11:52 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-06-26 19:47:32 -0400
commitf8475cef90082bf0902ddab106112de130d90395 (patch)
treec457befe40d6412c24fde174882524f1eac9b9c8
parent1a4fe38add8be45eb3873ad756561b9baf6bcaef (diff)
x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF
The goal of this change is to give users a uniform and meaningful result when they read /sys/...cpufreq/scaling_cur_freq on modern x86 hardware, as compared to what they get today. Modern x86 processors include the hardware needed to accurately calculate frequency over an interval -- APERF, MPERF, and the TSC. Here we provide an x86 routine to make this calculation on supported hardware, and use it in preference to any driver driver-specific cpufreq_driver.get() routine. MHz is computed like so: MHz = base_MHz * delta_APERF / delta_MPERF MHz is the average frequency of the busy processor over a measurement interval. The interval is defined to be the time between successive invocations of aperfmperf_khz_on_cpu(), which are expected to to happen on-demand when users read sysfs attribute cpufreq/scaling_cur_freq. As with previous methods of calculating MHz, idle time is excluded. base_MHz above is from TSC calibration global "cpu_khz". This x86 native method to calculate MHz returns a meaningful result no matter if P-states are controlled by hardware or firmware and/or if the Linux cpufreq sub-system is or is-not installed. When this routine is invoked more frequently, the measurement interval becomes shorter. However, the code limits re-computation to 10ms intervals so that average frequency remains meaningful. Discerning users are encouraged to take advantage of the turbostat(8) utility, which can gracefully handle concurrent measurement intervals of arbitrary length. Signed-off-by: Len Brown <len.brown@intel.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--arch/x86/kernel/cpu/Makefile1
-rw-r--r--arch/x86/kernel/cpu/aperfmperf.c79
-rw-r--r--drivers/cpufreq/cpufreq.c12
-rw-r--r--include/linux/cpufreq.h2
4 files changed, 93 insertions, 1 deletions
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 52000010c62e..cdf82492b770 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -21,6 +21,7 @@ obj-y += common.o
21obj-y += rdrand.o 21obj-y += rdrand.o
22obj-y += match.o 22obj-y += match.o
23obj-y += bugs.o 23obj-y += bugs.o
24obj-$(CONFIG_CPU_FREQ) += aperfmperf.o
24 25
25obj-$(CONFIG_PROC_FS) += proc.o 26obj-$(CONFIG_PROC_FS) += proc.o
26obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o 27obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o
diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c
new file mode 100644
index 000000000000..d869c8671e36
--- /dev/null
+++ b/arch/x86/kernel/cpu/aperfmperf.c
@@ -0,0 +1,79 @@
1/*
2 * x86 APERF/MPERF KHz calculation for
3 * /sys/.../cpufreq/scaling_cur_freq
4 *
5 * Copyright (C) 2017 Intel Corp.
6 * Author: Len Brown <len.brown@intel.com>
7 *
8 * This file is licensed under GPLv2.
9 */
10
11#include <linux/jiffies.h>
12#include <linux/math64.h>
13#include <linux/percpu.h>
14#include <linux/smp.h>
15
16struct aperfmperf_sample {
17 unsigned int khz;
18 unsigned long jiffies;
19 u64 aperf;
20 u64 mperf;
21};
22
23static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
24
25/*
26 * aperfmperf_snapshot_khz()
27 * On the current CPU, snapshot APERF, MPERF, and jiffies
28 * unless we already did it within 10ms
29 * calculate kHz, save snapshot
30 */
31static void aperfmperf_snapshot_khz(void *dummy)
32{
33 u64 aperf, aperf_delta;
34 u64 mperf, mperf_delta;
35 struct aperfmperf_sample *s = this_cpu_ptr(&samples);
36
37 /* Don't bother re-computing within 10 ms */
38 if (time_before(jiffies, s->jiffies + HZ/100))
39 return;
40
41 rdmsrl(MSR_IA32_APERF, aperf);
42 rdmsrl(MSR_IA32_MPERF, mperf);
43
44 aperf_delta = aperf - s->aperf;
45 mperf_delta = mperf - s->mperf;
46
47 /*
48 * There is no architectural guarantee that MPERF
49 * increments faster than we can read it.
50 */
51 if (mperf_delta == 0)
52 return;
53
54 /*
55 * if (cpu_khz * aperf_delta) fits into ULLONG_MAX, then
56 * khz = (cpu_khz * aperf_delta) / mperf_delta
57 */
58 if (div64_u64(ULLONG_MAX, cpu_khz) > aperf_delta)
59 s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
60 else /* khz = aperf_delta / (mperf_delta / cpu_khz) */
61 s->khz = div64_u64(aperf_delta,
62 div64_u64(mperf_delta, cpu_khz));
63 s->jiffies = jiffies;
64 s->aperf = aperf;
65 s->mperf = mperf;
66}
67
68unsigned int arch_freq_get_on_cpu(int cpu)
69{
70 if (!cpu_khz)
71 return 0;
72
73 if (!static_cpu_has(X86_FEATURE_APERFMPERF))
74 return 0;
75
76 smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
77
78 return per_cpu(samples.khz, cpu);
79}
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 26b643d57847..6e7424d12de9 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -632,11 +632,21 @@ show_one(cpuinfo_transition_latency, cpuinfo.transition_latency);
632show_one(scaling_min_freq, min); 632show_one(scaling_min_freq, min);
633show_one(scaling_max_freq, max); 633show_one(scaling_max_freq, max);
634 634
635__weak unsigned int arch_freq_get_on_cpu(int cpu)
636{
637 return 0;
638}
639
635static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf) 640static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf)
636{ 641{
637 ssize_t ret; 642 ssize_t ret;
643 unsigned int freq;
638 644
639 if (cpufreq_driver && cpufreq_driver->setpolicy && cpufreq_driver->get) 645 freq = arch_freq_get_on_cpu(policy->cpu);
646 if (freq)
647 ret = sprintf(buf, "%u\n", freq);
648 else if (cpufreq_driver && cpufreq_driver->setpolicy &&
649 cpufreq_driver->get)
640 ret = sprintf(buf, "%u\n", cpufreq_driver->get(policy->cpu)); 650 ret = sprintf(buf, "%u\n", cpufreq_driver->get(policy->cpu));
641 else 651 else
642 ret = sprintf(buf, "%u\n", policy->cur); 652 ret = sprintf(buf, "%u\n", policy->cur);
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index a5ce0bbeadb5..905117bd5012 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -883,6 +883,8 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy)
883} 883}
884#endif 884#endif
885 885
886extern unsigned int arch_freq_get_on_cpu(int cpu);
887
886/* the following are really really optional */ 888/* the following are really really optional */
887extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; 889extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
888extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs; 890extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs;