]>
Commit | Line | Data |
---|---|---|
f8475cef LB |
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 | ||
4815d3c5 RW |
11 | #include <linux/delay.h> |
12 | #include <linux/ktime.h> | |
f8475cef LB |
13 | #include <linux/math64.h> |
14 | #include <linux/percpu.h> | |
15 | #include <linux/smp.h> | |
16 | ||
17 | struct aperfmperf_sample { | |
18 | unsigned int khz; | |
4815d3c5 | 19 | ktime_t time; |
f8475cef LB |
20 | u64 aperf; |
21 | u64 mperf; | |
22 | }; | |
23 | ||
24 | static DEFINE_PER_CPU(struct aperfmperf_sample, samples); | |
25 | ||
4815d3c5 RW |
26 | #define APERFMPERF_CACHE_THRESHOLD_MS 10 |
27 | #define APERFMPERF_REFRESH_DELAY_MS 20 | |
28 | #define APERFMPERF_STALE_THRESHOLD_MS 1000 | |
29 | ||
f8475cef LB |
30 | /* |
31 | * aperfmperf_snapshot_khz() | |
32 | * On the current CPU, snapshot APERF, MPERF, and jiffies | |
33 | * unless we already did it within 10ms | |
34 | * calculate kHz, save snapshot | |
35 | */ | |
36 | static void aperfmperf_snapshot_khz(void *dummy) | |
37 | { | |
38 | u64 aperf, aperf_delta; | |
39 | u64 mperf, mperf_delta; | |
40 | struct aperfmperf_sample *s = this_cpu_ptr(&samples); | |
4815d3c5 RW |
41 | ktime_t now = ktime_get(); |
42 | s64 time_delta = ktime_ms_delta(now, s->time); | |
8e2f3bce | 43 | unsigned long flags; |
f8475cef | 44 | |
4815d3c5 RW |
45 | /* Don't bother re-computing within the cache threshold time. */ |
46 | if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS) | |
f8475cef LB |
47 | return; |
48 | ||
8e2f3bce | 49 | local_irq_save(flags); |
f8475cef LB |
50 | rdmsrl(MSR_IA32_APERF, aperf); |
51 | rdmsrl(MSR_IA32_MPERF, mperf); | |
8e2f3bce | 52 | local_irq_restore(flags); |
f8475cef LB |
53 | |
54 | aperf_delta = aperf - s->aperf; | |
55 | mperf_delta = mperf - s->mperf; | |
56 | ||
57 | /* | |
58 | * There is no architectural guarantee that MPERF | |
59 | * increments faster than we can read it. | |
60 | */ | |
61 | if (mperf_delta == 0) | |
62 | return; | |
63 | ||
4815d3c5 | 64 | s->time = now; |
f8475cef LB |
65 | s->aperf = aperf; |
66 | s->mperf = mperf; | |
4815d3c5 RW |
67 | |
68 | /* If the previous iteration was too long ago, discard it. */ | |
69 | if (time_delta > APERFMPERF_STALE_THRESHOLD_MS) | |
70 | s->khz = 0; | |
71 | else | |
72 | s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); | |
f8475cef LB |
73 | } |
74 | ||
75 | unsigned int arch_freq_get_on_cpu(int cpu) | |
76 | { | |
4815d3c5 RW |
77 | unsigned int khz; |
78 | ||
f8475cef LB |
79 | if (!cpu_khz) |
80 | return 0; | |
81 | ||
82 | if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | |
83 | return 0; | |
84 | ||
85 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | |
4815d3c5 RW |
86 | khz = per_cpu(samples.khz, cpu); |
87 | if (khz) | |
88 | return khz; | |
89 | ||
90 | msleep(APERFMPERF_REFRESH_DELAY_MS); | |
91 | smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | |
f8475cef LB |
92 | |
93 | return per_cpu(samples.khz, cpu); | |
94 | } |