aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;