-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/…
…MPERF The goal of this change is to give users a uniform and meaningful result when they read /sys/...cpufreq/scaling_cur_freq on modern x86 hardware, as compared to what they get today. Modern x86 processors include the hardware needed to accurately calculate frequency over an interval -- APERF, MPERF, and the TSC. Here we provide an x86 routine to make this calculation on supported hardware, and use it in preference to any driver driver-specific cpufreq_driver.get() routine. MHz is computed like so: MHz = base_MHz * delta_APERF / delta_MPERF MHz is the average frequency of the busy processor over a measurement interval. The interval is defined to be the time between successive invocations of aperfmperf_khz_on_cpu(), which are expected to to happen on-demand when users read sysfs attribute cpufreq/scaling_cur_freq. As with previous methods of calculating MHz, idle time is excluded. base_MHz above is from TSC calibration global "cpu_khz". This x86 native method to calculate MHz returns a meaningful result no matter if P-states are controlled by hardware or firmware and/or if the Linux cpufreq sub-system is or is-not installed. When this routine is invoked more frequently, the measurement interval becomes shorter. However, the code limits re-computation to 10ms intervals so that average frequency remains meaningful. Discerning users are encouraged to take advantage of the turbostat(8) utility, which can gracefully handle concurrent measurement intervals of arbitrary length. Signed-off-by: Len Brown <len.brown@intel.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
- Loading branch information
Len Brown
authored and
Rafael J. Wysocki
committed
Jun 26, 2017
1 parent
1a4fe38
commit f8475ce
Showing
4 changed files
with
93 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* x86 APERF/MPERF KHz calculation for | ||
* /sys/.../cpufreq/scaling_cur_freq | ||
* | ||
* Copyright (C) 2017 Intel Corp. | ||
* Author: Len Brown <len.brown@intel.com> | ||
* | ||
* This file is licensed under GPLv2. | ||
*/ | ||
|
||
#include <linux/jiffies.h> | ||
#include <linux/math64.h> | ||
#include <linux/percpu.h> | ||
#include <linux/smp.h> | ||
|
||
struct aperfmperf_sample { | ||
unsigned int khz; | ||
unsigned long jiffies; | ||
u64 aperf; | ||
u64 mperf; | ||
}; | ||
|
||
static DEFINE_PER_CPU(struct aperfmperf_sample, samples); | ||
|
||
/* | ||
* aperfmperf_snapshot_khz() | ||
* On the current CPU, snapshot APERF, MPERF, and jiffies | ||
* unless we already did it within 10ms | ||
* calculate kHz, save snapshot | ||
*/ | ||
static void aperfmperf_snapshot_khz(void *dummy) | ||
{ | ||
u64 aperf, aperf_delta; | ||
u64 mperf, mperf_delta; | ||
struct aperfmperf_sample *s = this_cpu_ptr(&samples); | ||
|
||
/* Don't bother re-computing within 10 ms */ | ||
if (time_before(jiffies, s->jiffies + HZ/100)) | ||
return; | ||
|
||
rdmsrl(MSR_IA32_APERF, aperf); | ||
rdmsrl(MSR_IA32_MPERF, mperf); | ||
|
||
aperf_delta = aperf - s->aperf; | ||
mperf_delta = mperf - s->mperf; | ||
|
||
/* | ||
* There is no architectural guarantee that MPERF | ||
* increments faster than we can read it. | ||
*/ | ||
if (mperf_delta == 0) | ||
return; | ||
|
||
/* | ||
* if (cpu_khz * aperf_delta) fits into ULLONG_MAX, then | ||
* khz = (cpu_khz * aperf_delta) / mperf_delta | ||
*/ | ||
if (div64_u64(ULLONG_MAX, cpu_khz) > aperf_delta) | ||
s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta); | ||
else /* khz = aperf_delta / (mperf_delta / cpu_khz) */ | ||
s->khz = div64_u64(aperf_delta, | ||
div64_u64(mperf_delta, cpu_khz)); | ||
s->jiffies = jiffies; | ||
s->aperf = aperf; | ||
s->mperf = mperf; | ||
} | ||
|
||
unsigned int arch_freq_get_on_cpu(int cpu) | ||
{ | ||
if (!cpu_khz) | ||
return 0; | ||
|
||
if (!static_cpu_has(X86_FEATURE_APERFMPERF)) | ||
return 0; | ||
|
||
smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1); | ||
|
||
return per_cpu(samples.khz, cpu); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters