diff options
| -rw-r--r-- | arch/x86/kernel/cpu/Makefile | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/aperfmperf.c | 74 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/cpu.h | 3 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/proc.c | 6 | ||||
| -rw-r--r-- | fs/proc/cpuinfo.c | 6 | ||||
| -rw-r--r-- | include/linux/cpufreq.h | 1 |
6 files changed, 68 insertions, 24 deletions
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 90cb82dbba57..570e8bb1f386 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile | |||
| @@ -22,7 +22,7 @@ obj-y += common.o | |||
| 22 | obj-y += rdrand.o | 22 | obj-y += rdrand.o |
| 23 | obj-y += match.o | 23 | obj-y += match.o |
| 24 | obj-y += bugs.o | 24 | obj-y += bugs.o |
| 25 | obj-$(CONFIG_CPU_FREQ) += aperfmperf.o | 25 | obj-y += aperfmperf.o |
| 26 | obj-y += cpuid-deps.o | 26 | obj-y += cpuid-deps.o |
| 27 | 27 | ||
| 28 | obj-$(CONFIG_PROC_FS) += proc.o | 28 | obj-$(CONFIG_PROC_FS) += proc.o |
diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c index 957813e0180d..7eba34df54c3 100644 --- a/arch/x86/kernel/cpu/aperfmperf.c +++ b/arch/x86/kernel/cpu/aperfmperf.c | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include <linux/percpu.h> | 14 | #include <linux/percpu.h> |
| 15 | #include <linux/smp.h> | 15 | #include <linux/smp.h> |
| 16 | 16 | ||
| 17 | #include "cpu.h" | ||
| 18 | |||
| 17 | struct aperfmperf_sample { | 19 | struct aperfmperf_sample { |
| 18 | unsigned int khz; | 20 | unsigned int khz; |
| 19 | ktime_t time; | 21 | ktime_t time; |
| @@ -24,7 +26,7 @@ struct aperfmperf_sample { | |||
| 24 | static DEFINE_PER_CPU(struct aperfmperf_sample, samples); | 26 | static DEFINE_PER_CPU(struct aperfmperf_sample, samples); |
| 25 | 27 | ||
| 26 | #define APERFMPERF_CACHE_THRESHOLD_MS 10 | 28 | #define APERFMPERF_CACHE_THRESHOLD_MS 10 |
| 27 | #define APERFMPERF_REFRESH_DELAY_MS 20 | 29 | #define APERFMPERF_REFRESH_DELAY_MS 10 |
| 28 | #define APERFMPERF_STALE_THRESHOLD_MS 1000 | 30 | #define APERFMPERF_STALE_THRESHOLD_MS 1000 |
| 29 | 31 | ||
| 30 | /* | 32 | /* |
| @@ -38,8 +40,6 @@ static void aperfmperf_snapshot_khz(void *dummy) | |||
| 38 | u64 aperf, aperf_delta; | 40 | u64 aperf, aperf_delta; |
| 39 | u64 mperf, mperf_delta; | 41 | u64 mperf, mperf_delta; |
| 40 | struct aperfmperf_sample *s = this_cpu_ptr(&samples); | 42 | struct aperfmperf_sample *s = this_cpu_ptr(&samples); |
| 41 | ktime_t now = ktime_get(); | ||
| 42 | s64 time_delta = ktime_ms_delta(now, s->time); | ||
| 43 | unsigned long flags; | 43 | unsigned long flags; |
| 44 | 44 | ||
| 45 | local_irq_save(flags); | 45 | local_irq_save(flags); |
| @@ -57,38 +57,68 @@ static void aperfmperf_snapshot_khz(void *dummy) | |||
| 57 | if (mperf_delta == 0) | 57 | if (mperf_delta == 0) |
| 58 | return; | 58 | return; |
| 59 | 59 | ||
| 60 | s->time = now; | 60 | s->time = ktime_get(); |
| 61 | s->aperf = aperf; | 61 | s->aperf = aperf; |
| 62 | s->mperf = mperf; | 62 | s->mperf = mperf; |
| 63 | 63 | s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); | |
| 64 | /* If the previous iteration was too long ago, discard it. */ | ||
| 65 | if (time_delta > APERFMPERF_STALE_THRESHOLD_MS) | ||
| 66 | s->khz = 0; | ||
| 67 | else | ||
| 68 | s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); | ||
| 69 | } | 64 | } |
| 70 | 65 | ||
| 71 | unsigned int arch_freq_get_on_cpu(int cpu) | 66 | static bool aperfmperf_snapshot_cpu(int cpu, ktime_t now, bool wait) |
| 72 | { | 67 | { |
| 73 | s64 time_delta; | 68 | s64 time_delta = ktime_ms_delta(now, per_cpu(samples.time, cpu)); |
| 74 | unsigned int khz; | 69 | |
| 70 | /* Don't bother re-computing within the cache threshold time. */ | ||
| 71 | if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS) | ||
| 72 | return true; | ||
| 73 | |||
| 74 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, wait); | ||
| 75 | |||
| 76 | /* Return false if the previous iteration was too long ago. */ | ||
| 77 | return time_delta <= APERFMPERF_STALE_THRESHOLD_MS; | ||
| 78 | } | ||
| 75 | 79 | ||
| 80 | unsigned int aperfmperf_get_khz(int cpu) | ||
| 81 | { | ||
| 76 | if (!cpu_khz) | 82 | if (!cpu_khz) |
| 77 | return 0; | 83 | return 0; |
| 78 | 84 | ||
| 79 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | 85 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) |
| 80 | return 0; | 86 | return 0; |
| 81 | 87 | ||
| 82 | /* Don't bother re-computing within the cache threshold time. */ | 88 | aperfmperf_snapshot_cpu(cpu, ktime_get(), true); |
| 83 | time_delta = ktime_ms_delta(ktime_get(), per_cpu(samples.time, cpu)); | 89 | return per_cpu(samples.khz, cpu); |
| 84 | khz = per_cpu(samples.khz, cpu); | 90 | } |
| 85 | if (khz && time_delta < APERFMPERF_CACHE_THRESHOLD_MS) | ||
| 86 | return khz; | ||
| 87 | 91 | ||
| 88 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | 92 | void arch_freq_prepare_all(void) |
| 89 | khz = per_cpu(samples.khz, cpu); | 93 | { |
| 90 | if (khz) | 94 | ktime_t now = ktime_get(); |
| 91 | return khz; | 95 | bool wait = false; |
| 96 | int cpu; | ||
| 97 | |||
| 98 | if (!cpu_khz) | ||
| 99 | return; | ||
| 100 | |||
| 101 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | ||
| 102 | return; | ||
| 103 | |||
| 104 | for_each_online_cpu(cpu) | ||
| 105 | if (!aperfmperf_snapshot_cpu(cpu, now, false)) | ||
| 106 | wait = true; | ||
| 107 | |||
| 108 | if (wait) | ||
| 109 | msleep(APERFMPERF_REFRESH_DELAY_MS); | ||
| 110 | } | ||
| 111 | |||
| 112 | unsigned int arch_freq_get_on_cpu(int cpu) | ||
| 113 | { | ||
| 114 | if (!cpu_khz) | ||
| 115 | return 0; | ||
| 116 | |||
| 117 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | ||
| 118 | return 0; | ||
| 119 | |||
| 120 | if (aperfmperf_snapshot_cpu(cpu, ktime_get(), true)) | ||
| 121 | return per_cpu(samples.khz, cpu); | ||
| 92 | 122 | ||
| 93 | msleep(APERFMPERF_REFRESH_DELAY_MS); | 123 | msleep(APERFMPERF_REFRESH_DELAY_MS); |
| 94 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | 124 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); |
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index f52a370b6c00..e806b11a99af 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h | |||
| @@ -47,4 +47,7 @@ extern const struct cpu_dev *const __x86_cpu_dev_start[], | |||
| 47 | 47 | ||
| 48 | extern void get_cpu_cap(struct cpuinfo_x86 *c); | 48 | extern void get_cpu_cap(struct cpuinfo_x86 *c); |
| 49 | extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c); | 49 | extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c); |
| 50 | |||
| 51 | unsigned int aperfmperf_get_khz(int cpu); | ||
| 52 | |||
| 50 | #endif /* ARCH_X86_CPU_H */ | 53 | #endif /* ARCH_X86_CPU_H */ |
diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index 6b7e17bf0b71..e7ecedafa1c8 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <linux/seq_file.h> | 5 | #include <linux/seq_file.h> |
| 6 | #include <linux/cpufreq.h> | 6 | #include <linux/cpufreq.h> |
| 7 | 7 | ||
| 8 | #include "cpu.h" | ||
| 9 | |||
| 8 | /* | 10 | /* |
| 9 | * Get CPU information for use by the procfs. | 11 | * Get CPU information for use by the procfs. |
| 10 | */ | 12 | */ |
| @@ -78,9 +80,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) | |||
| 78 | seq_printf(m, "microcode\t: 0x%x\n", c->microcode); | 80 | seq_printf(m, "microcode\t: 0x%x\n", c->microcode); |
| 79 | 81 | ||
| 80 | if (cpu_has(c, X86_FEATURE_TSC)) { | 82 | if (cpu_has(c, X86_FEATURE_TSC)) { |
| 81 | unsigned int freq = cpufreq_quick_get(cpu); | 83 | unsigned int freq = aperfmperf_get_khz(cpu); |
| 82 | 84 | ||
| 83 | if (!freq) | 85 | if (!freq) |
| 86 | freq = cpufreq_quick_get(cpu); | ||
| 87 | if (!freq) | ||
| 84 | freq = cpu_khz; | 88 | freq = cpu_khz; |
| 85 | seq_printf(m, "cpu MHz\t\t: %u.%03u\n", | 89 | seq_printf(m, "cpu MHz\t\t: %u.%03u\n", |
| 86 | freq / 1000, (freq % 1000)); | 90 | freq / 1000, (freq % 1000)); |
diff --git a/fs/proc/cpuinfo.c b/fs/proc/cpuinfo.c index e0f867cd8553..96f1087e372c 100644 --- a/fs/proc/cpuinfo.c +++ b/fs/proc/cpuinfo.c | |||
| @@ -1,12 +1,18 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #include <linux/cpufreq.h> | ||
| 2 | #include <linux/fs.h> | 3 | #include <linux/fs.h> |
| 3 | #include <linux/init.h> | 4 | #include <linux/init.h> |
| 4 | #include <linux/proc_fs.h> | 5 | #include <linux/proc_fs.h> |
| 5 | #include <linux/seq_file.h> | 6 | #include <linux/seq_file.h> |
| 6 | 7 | ||
| 8 | __weak void arch_freq_prepare_all(void) | ||
| 9 | { | ||
| 10 | } | ||
| 11 | |||
| 7 | extern const struct seq_operations cpuinfo_op; | 12 | extern const struct seq_operations cpuinfo_op; |
| 8 | static int cpuinfo_open(struct inode *inode, struct file *file) | 13 | static int cpuinfo_open(struct inode *inode, struct file *file) |
| 9 | { | 14 | { |
| 15 | arch_freq_prepare_all(); | ||
| 10 | return seq_open(file, &cpuinfo_op); | 16 | return seq_open(file, &cpuinfo_op); |
| 11 | } | 17 | } |
| 12 | 18 | ||
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 28734ee185a7..065f3a8eb486 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h | |||
| @@ -917,6 +917,7 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy) | |||
| 917 | } | 917 | } |
| 918 | #endif | 918 | #endif |
| 919 | 919 | ||
| 920 | extern void arch_freq_prepare_all(void); | ||
| 920 | extern unsigned int arch_freq_get_on_cpu(int cpu); | 921 | extern unsigned int arch_freq_get_on_cpu(int cpu); |
| 921 | 922 | ||
| 922 | extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, | 923 | extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, |
