| .. | .. |
|---|
| 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 | }; |
|---|