enum MAX_FREQ_MODE { MAX_FREQ_SYSFS, MAX_FREQ_TSC_REF }; staticint max_freq_mode; /* * The max frequency mperf is ticking at (in C0), either retrieved via: * 1) calculated after measurements if we know TSC ticks at mperf/P0 frequency * 2) cpufreq /sys/devices/.../cpu0/cpufreq/cpuinfo_max_freq at init time * 1. Is preferred as it also works without cpufreq subsystem (e.g. on Xen)
*/ staticunsignedlong max_frequency;
/* * Running on the cpu from which we read the registers will * prevent APERF/MPERF from going out of sync because of IPI * latency introduced by read_msr()s.
*/ if (mperf_monitor.flags.per_cpu_schedule) { if (bind_cpu(cpu)) return 1;
}
for (cpu = 0; cpu < cpu_count; cpu++) {
clock_gettime(CLOCK_REALTIME, &time_start[cpu]);
mperf_get_tsc(&tsc_at_measure_start[cpu]);
mperf_init_stats(cpu);
}
return 0;
}
staticint mperf_stop(void)
{ int cpu;
for (cpu = 0; cpu < cpu_count; cpu++) {
clock_gettime(CLOCK_REALTIME, &time_end[cpu]);
mperf_get_tsc(&tsc_at_measure_end[cpu]);
mperf_measure_stats(cpu);
}
return 0;
}
/* * Mperf register is defined to tick at P0 (maximum) frequency * * Instead of reading out P0 which can be tricky to read out from HW, * we use TSC counter if it reliably ticks at P0/mperf frequency. * * Still try to fall back to: * /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq * on older Intel HW without invariant TSC feature. * Or on AMD machines where TSC does not tick at P0 (do not exist yet, but * it's still double checked (MSR_AMD_HWCR)). * * On these machines the user would still get useful mperf * stats when acpi-cpufreq driver is loaded.
*/ staticint init_maxfreq_mode(void)
{ int ret; unsignedlonglong hwcr; unsignedlong min;
if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC)) goto use_sysfs;
if (cpupower_cpu_info.vendor == X86_VENDOR_AMD ||
cpupower_cpu_info.vendor == X86_VENDOR_HYGON) { /* MSR_AMD_HWCR tells us whether TSC runs at P0/mperf * freq. * A test whether hwcr is accessable/available would be: * (cpupower_cpu_info.family > 0x10 || * cpupower_cpu_info.family == 0x10 && * cpupower_cpu_info.model >= 0x2)) * This should be the case for all aperf/mperf * capable AMD machines and is therefore safe to test here. * Compare with Linus kernel git commit: acf01734b1747b1ec4
*/
ret = read_msr(0, MSR_AMD_HWCR, &hwcr); /* * If the MSR read failed, assume a Xen system that did * not explicitly provide access to it and assume TSC works
*/ if (ret != 0) {
dprint("TSC read 0x%x failed - assume TSC working\n",
MSR_AMD_HWCR); return 0;
} elseif (1 & (hwcr >> 24)) {
max_freq_mode = MAX_FREQ_TSC_REF; return 0;
} else { /* Use sysfs max frequency if available */ }
} elseif (cpupower_cpu_info.vendor == X86_VENDOR_INTEL) { /* * On Intel we assume mperf (in C0) is ticking at same * rate than TSC
*/
max_freq_mode = MAX_FREQ_TSC_REF; return 0;
}
use_sysfs: if (cpufreq_get_hardware_limits(0, &min, &max_frequency)) {
dprint("Cannot retrieve max freq from cpufreq kernel " "subsystem\n"); return -1;
}
max_freq_mode = MAX_FREQ_SYSFS;
max_frequency /= 1000; /* Default automatically to MHz value */ return 0;
}
/* * This monitor provides: * * 1) Average frequency a CPU resided in * This always works if the CPU has aperf/mperf capabilities * * 2) C0 and Cx (any sleep state) time a CPU resided in * Works if mperf timer stops ticking in sleep states which * seem to be the case on all current HW. * Both is directly retrieved from HW registers and is independent * from kernel statistics.
*/ struct cpuidle_monitor mperf_monitor; struct cpuidle_monitor *mperf_register(void)
{ if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) return NULL;
if (init_maxfreq_mode()) return NULL;
if (cpupower_cpu_info.vendor == X86_VENDOR_AMD)
mperf_monitor.flags.per_cpu_schedule = 1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.