.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. |
---|
3 | | - * |
---|
4 | | - * Licensed under the terms of the GNU GPL License version 2. |
---|
5 | 4 | */ |
---|
6 | 5 | |
---|
7 | 6 | #if defined(__i386__) || defined(__x86_64__) |
---|
.. | .. |
---|
19 | 18 | |
---|
20 | 19 | #define MSR_APERF 0xE8 |
---|
21 | 20 | #define MSR_MPERF 0xE7 |
---|
| 21 | + |
---|
| 22 | +#define RDPRU ".byte 0x0f, 0x01, 0xfd" |
---|
| 23 | +#define RDPRU_ECX_MPERF 0 |
---|
| 24 | +#define RDPRU_ECX_APERF 1 |
---|
22 | 25 | |
---|
23 | 26 | #define MSR_TSC 0x10 |
---|
24 | 27 | |
---|
.. | .. |
---|
87 | 90 | return ret; |
---|
88 | 91 | } |
---|
89 | 92 | |
---|
90 | | -static int mperf_init_stats(unsigned int cpu) |
---|
| 93 | +static int get_aperf_mperf(int cpu, unsigned long long *aval, |
---|
| 94 | + unsigned long long *mval) |
---|
91 | 95 | { |
---|
92 | | - unsigned long long val; |
---|
| 96 | + unsigned long low_a, high_a; |
---|
| 97 | + unsigned long low_m, high_m; |
---|
93 | 98 | int ret; |
---|
94 | 99 | |
---|
95 | | - ret = read_msr(cpu, MSR_APERF, &val); |
---|
96 | | - aperf_previous_count[cpu] = val; |
---|
97 | | - ret |= read_msr(cpu, MSR_MPERF, &val); |
---|
98 | | - mperf_previous_count[cpu] = val; |
---|
| 100 | + /* |
---|
| 101 | + * Running on the cpu from which we read the registers will |
---|
| 102 | + * prevent APERF/MPERF from going out of sync because of IPI |
---|
| 103 | + * latency introduced by read_msr()s. |
---|
| 104 | + */ |
---|
| 105 | + if (mperf_monitor.flags.per_cpu_schedule) { |
---|
| 106 | + if (bind_cpu(cpu)) |
---|
| 107 | + return 1; |
---|
| 108 | + } |
---|
| 109 | + |
---|
| 110 | + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_RDPRU) { |
---|
| 111 | + asm volatile(RDPRU |
---|
| 112 | + : "=a" (low_a), "=d" (high_a) |
---|
| 113 | + : "c" (RDPRU_ECX_APERF)); |
---|
| 114 | + asm volatile(RDPRU |
---|
| 115 | + : "=a" (low_m), "=d" (high_m) |
---|
| 116 | + : "c" (RDPRU_ECX_MPERF)); |
---|
| 117 | + |
---|
| 118 | + *aval = ((low_a) | (high_a) << 32); |
---|
| 119 | + *mval = ((low_m) | (high_m) << 32); |
---|
| 120 | + |
---|
| 121 | + return 0; |
---|
| 122 | + } |
---|
| 123 | + |
---|
| 124 | + ret = read_msr(cpu, MSR_APERF, aval); |
---|
| 125 | + ret |= read_msr(cpu, MSR_MPERF, mval); |
---|
| 126 | + |
---|
| 127 | + return ret; |
---|
| 128 | +} |
---|
| 129 | + |
---|
| 130 | +static int mperf_init_stats(unsigned int cpu) |
---|
| 131 | +{ |
---|
| 132 | + unsigned long long aval, mval; |
---|
| 133 | + int ret; |
---|
| 134 | + |
---|
| 135 | + ret = get_aperf_mperf(cpu, &aval, &mval); |
---|
| 136 | + aperf_previous_count[cpu] = aval; |
---|
| 137 | + mperf_previous_count[cpu] = mval; |
---|
99 | 138 | is_valid[cpu] = !ret; |
---|
100 | 139 | |
---|
101 | 140 | return 0; |
---|
.. | .. |
---|
103 | 142 | |
---|
104 | 143 | static int mperf_measure_stats(unsigned int cpu) |
---|
105 | 144 | { |
---|
106 | | - unsigned long long val; |
---|
| 145 | + unsigned long long aval, mval; |
---|
107 | 146 | int ret; |
---|
108 | 147 | |
---|
109 | | - ret = read_msr(cpu, MSR_APERF, &val); |
---|
110 | | - aperf_current_count[cpu] = val; |
---|
111 | | - ret |= read_msr(cpu, MSR_MPERF, &val); |
---|
112 | | - mperf_current_count[cpu] = val; |
---|
| 148 | + ret = get_aperf_mperf(cpu, &aval, &mval); |
---|
| 149 | + aperf_current_count[cpu] = aval; |
---|
| 150 | + mperf_current_count[cpu] = mval; |
---|
113 | 151 | is_valid[cpu] = !ret; |
---|
114 | 152 | |
---|
115 | 153 | return 0; |
---|
.. | .. |
---|
241 | 279 | if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC)) |
---|
242 | 280 | goto use_sysfs; |
---|
243 | 281 | |
---|
244 | | - if (cpupower_cpu_info.vendor == X86_VENDOR_AMD) { |
---|
| 282 | + if (cpupower_cpu_info.vendor == X86_VENDOR_AMD || |
---|
| 283 | + cpupower_cpu_info.vendor == X86_VENDOR_HYGON) { |
---|
245 | 284 | /* MSR_AMD_HWCR tells us whether TSC runs at P0/mperf |
---|
246 | 285 | * freq. |
---|
247 | 286 | * A test whether hwcr is accessable/available would be: |
---|
.. | .. |
---|
305 | 344 | if (init_maxfreq_mode()) |
---|
306 | 345 | return NULL; |
---|
307 | 346 | |
---|
| 347 | + if (cpupower_cpu_info.vendor == X86_VENDOR_AMD) |
---|
| 348 | + mperf_monitor.flags.per_cpu_schedule = 1; |
---|
| 349 | + |
---|
308 | 350 | /* Free this at program termination */ |
---|
309 | 351 | is_valid = calloc(cpu_count, sizeof(int)); |
---|
310 | 352 | mperf_previous_count = calloc(cpu_count, sizeof(unsigned long long)); |
---|
.. | .. |
---|
333 | 375 | .stop = mperf_stop, |
---|
334 | 376 | .do_register = mperf_register, |
---|
335 | 377 | .unregister = mperf_unregister, |
---|
336 | | - .needs_root = 1, |
---|
| 378 | + .flags.needs_root = 1, |
---|
337 | 379 | .overflow_s = 922000000 /* 922337203 seconds TSC overflow |
---|
338 | 380 | at 20GHz */ |
---|
339 | 381 | }; |
---|