diff options
-rw-r--r-- | arch/x86/kernel/cpu/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/aperfmperf.c | 79 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 12 | ||||
-rw-r--r-- | include/linux/cpufreq.h | 2 |
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 | |||
21 | obj-y += rdrand.o | 21 | obj-y += rdrand.o |
22 | obj-y += match.o | 22 | obj-y += match.o |
23 | obj-y += bugs.o | 23 | obj-y += bugs.o |
24 | obj-$(CONFIG_CPU_FREQ) += aperfmperf.o | ||
24 | 25 | ||
25 | obj-$(CONFIG_PROC_FS) += proc.o | 26 | obj-$(CONFIG_PROC_FS) += proc.o |
26 | obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o | 27 | obj-$(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 | |||
16 | struct aperfmperf_sample { | ||
17 | unsigned int khz; | ||
18 | unsigned long jiffies; | ||
19 | u64 aperf; | ||
20 | u64 mperf; | ||
21 | }; | ||
22 | |||
23 | static 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 | */ | ||
31 | static 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 | |||
68 | unsigned 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); | |||
632 | show_one(scaling_min_freq, min); | 632 | show_one(scaling_min_freq, min); |
633 | show_one(scaling_max_freq, max); | 633 | show_one(scaling_max_freq, max); |
634 | 634 | ||
635 | __weak unsigned int arch_freq_get_on_cpu(int cpu) | ||
636 | { | ||
637 | return 0; | ||
638 | } | ||
639 | |||
635 | static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf) | 640 | static 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 | ||
886 | extern unsigned int arch_freq_get_on_cpu(int cpu); | ||
887 | |||
886 | /* the following are really really optional */ | 888 | /* the following are really really optional */ |
887 | extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; | 889 | extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; |
888 | extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs; | 890 | extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs; |