Skip to content

Commit 4815d3c

Browse files
committed
cpufreq: x86: Make scaling_cur_freq behave more as expected
After commit f8475ce "x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF" the scaling_cur_freq policy attribute in sysfs only behaves as expected on x86 with APERF/MPERF registers available when it is read from at least twice in a row. The value returned by the first read may not be meaningful, because the computations in there use cached values from the previous iteration of aperfmperf_snapshot_khz() which may be stale. To prevent that from happening, modify arch_freq_get_on_cpu() to call aperfmperf_snapshot_khz() twice, with a short delay between these calls, if the previous invocation of aperfmperf_snapshot_khz() was too far back in the past (specifically, more that 1s ago). Also, as pointed out by Doug Smythies, aperf_delta is limited now and the multiplication of it by cpu_khz won't overflow, so simplify the s->khz computations too. Fixes: f8475ce "x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF" Reported-by: Doug Smythies <dsmythies@telus.net> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 520eccd commit 4815d3c

File tree

1 file changed

+26
-14
lines changed

1 file changed

+26
-14
lines changed

arch/x86/kernel/cpu/aperfmperf.c

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,25 @@
88
* This file is licensed under GPLv2.
99
*/
1010

11-
#include <linux/jiffies.h>
11+
#include <linux/delay.h>
12+
#include <linux/ktime.h>
1213
#include <linux/math64.h>
1314
#include <linux/percpu.h>
1415
#include <linux/smp.h>
1516

1617
struct aperfmperf_sample {
1718
unsigned int khz;
18-
unsigned long jiffies;
19+
ktime_t time;
1920
u64 aperf;
2021
u64 mperf;
2122
};
2223

2324
static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
2425

26+
#define APERFMPERF_CACHE_THRESHOLD_MS 10
27+
#define APERFMPERF_REFRESH_DELAY_MS 20
28+
#define APERFMPERF_STALE_THRESHOLD_MS 1000
29+
2530
/*
2631
* aperfmperf_snapshot_khz()
2732
* On the current CPU, snapshot APERF, MPERF, and jiffies
@@ -33,9 +38,11 @@ static void aperfmperf_snapshot_khz(void *dummy)
3338
u64 aperf, aperf_delta;
3439
u64 mperf, mperf_delta;
3540
struct aperfmperf_sample *s = this_cpu_ptr(&samples);
41+
ktime_t now = ktime_get();
42+
s64 time_delta = ktime_ms_delta(now, s->time);
3643

37-
/* Don't bother re-computing within 10 ms */
38-
if (time_before(jiffies, s->jiffies + HZ/100))
44+
/* Don't bother re-computing within the cache threshold time. */
45+
if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
3946
return;
4047

4148
rdmsrl(MSR_IA32_APERF, aperf);
@@ -51,29 +58,34 @@ static void aperfmperf_snapshot_khz(void *dummy)
5158
if (mperf_delta == 0)
5259
return;
5360

54-
/*
55-
* if (cpu_khz * aperf_delta) fits into ULLONG_MAX, then
56-
* khz = (cpu_khz * aperf_delta) / mperf_delta
57-
*/
58-
if (div64_u64(ULLONG_MAX, cpu_khz) > aperf_delta)
59-
s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
60-
else /* khz = aperf_delta / (mperf_delta / cpu_khz) */
61-
s->khz = div64_u64(aperf_delta,
62-
div64_u64(mperf_delta, cpu_khz));
63-
s->jiffies = jiffies;
61+
s->time = now;
6462
s->aperf = aperf;
6563
s->mperf = mperf;
64+
65+
/* If the previous iteration was too long ago, discard it. */
66+
if (time_delta > APERFMPERF_STALE_THRESHOLD_MS)
67+
s->khz = 0;
68+
else
69+
s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
6670
}
6771

6872
unsigned int arch_freq_get_on_cpu(int cpu)
6973
{
74+
unsigned int khz;
75+
7076
if (!cpu_khz)
7177
return 0;
7278

7379
if (!static_cpu_has(X86_FEATURE_APERFMPERF))
7480
return 0;
7581

7682
smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
83+
khz = per_cpu(samples.khz, cpu);
84+
if (khz)
85+
return khz;
86+
87+
msleep(APERFMPERF_REFRESH_DELAY_MS);
88+
smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
7789

7890
return per_cpu(samples.khz, cpu);
7991
}

0 commit comments

Comments
 (0)