Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1696648 > unrolled thread

RE: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF

Started by"Doug Smythies" <dsmythies@telus.net>
First post2017-07-26 00:50 +0200
Last post2017-07-28 14:40 +0200
Articles 6 — 3 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  RE: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF "Doug Smythies" <dsmythies@telus.net> - 2017-07-26 00:50 +0200
    Re: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to  calculate KHz using APERF/MPERF Len Brown <lenb@kernel.org> - 2017-07-26 19:30 +0200
    [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-28 02:30 +0200
      [PATCH v2] cpufreq: x86: Make scaling_cur_freq behave more as expected "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-28 15:00 +0200
    RE: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected "Doug Smythies" <dsmythies@telus.net> - 2017-07-28 08:10 +0200
      Re: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-28 14:40 +0200

#1696648 — RE: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF

From"Doug Smythies" <dsmythies@telus.net>
Date2017-07-26 00:50 +0200
SubjectRE: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF
Message-ID<u7gOm-s4-17@gated-at.bofh.it>
Sorry to be late to the party on this one:

On 2017.06.23 10:12 Len Brown wrote:

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

Myself, I like what I got then, and not what I get now.

> 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

Yes, thanks very much.

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

Yes but that can be hours apart, resulting in useless information.
This threw me for a loop for several days.
 
> As with previous methods of calculating MHz,
> idle time is excluded.

Which makes the response time to a correct answer
asymmetric. i.e. removal of a load on a CPU will
linger much much longer that adding a load on a CPU.

> base_MHz above is from TSC calibration global "cpu_khz".

Yes, thank you very much.

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

Somehow, somewhere along the way, turbostat no longer seems
to use base_MHz based on the actual TSC. It used to.
 
> Signed-off-by: Len Brown <len.brown@intel.com>
> ---
> arch/x86/kernel/cpu/Makefile     |  1 +
> arch/x86/kernel/cpu/aperfmperf.c | 79 ++++++++++++++++++++++++++++++++++++++++
> drivers/cpufreq/cpufreq.c        | 12 +++++-
> include/linux/cpufreq.h          |  2 +
> 4 files changed, 93 insertions(+), 1 deletion(-)
> create mode 100644 arch/x86/kernel/cpu/aperfmperf.c

... [deleted some] ...

> + * aperfmperf_snapshot_khz()
> + * On the current CPU, snapshot APERF, MPERF, and jiffies
> + * unless we already did it within 10ms

Well, it'll be 8 mSec on a 250 Hz kernel.
There is no maximum time defined, so the interval can be anything,
and therefore the result can be dominated by stale information.

> + * 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;

The above condition would be 8 mSec on a 250 Hertz kernel,
wouldn't it?
(I don't care, I'm just saying.)
__________________________________

A long boring story is copied below, but it also includes my test data.

Summary:

. There no longer seems to be a way to check the CPU frequency without affecting the processor (i.e. forcing a wakeup),
thereby potentially influencing the system under test.
. Yes, the old way might have been a "lie", but in some situations it was much much less of a "lie", and took data that
was already available (and at the very maximum 4 seconds old), and didn't force a wakeup, thus monitoring CPU frequency
was a negligible perturbation to the system.
. Now the data is as old as the time the command was run, which might be hours.

For reference my test computer contains an i7-2600K processor, and TSC is 3411.1043 MHz. Minimum pstate 16.

I did follow the e-mail thread [1] about changes to the "cpu MHz" line from /proc/cpuinfo, and expected it to have changed,
and indeed, it only ever prints TSC now and never changes. Whereas with kernel 4.12 it printed the actual CPU frequency,
albeit with the limitations stated in the e-mail thread, which I have always understood and accepted. O.K. so now it
is useless as an actual CPU frequency inquiry tool.

Now, there are two other methods (well three if one includes turbostat) for observing CPU frequency:

The "sudo cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq" method, works the same as it
did in the past (well, there is another active thread about issues with it), but requires root access.
 
And the "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq" method, which works fine
with kernel 4.12, but seems to give incorrect information with kernel 4.13-rc1, unless one inquires two or
more times and discards the first inquiry.

Test 1 data: 

Notes:
CPU 7 only. It is 100% busy all the time.
The CPU burn program prints a time stamp every N loops, as a way to do a sanity check on CPU frequency.
Sanity checks were also done by acquiring trace data.
Turbo is disabled, so the maximum CPU frequency is predicable and known, independent of what other cores are doing.
The data is not from the first loop through this test.

Data:
/sys/devices/system/cpu/intel_pstate/max_perf_pct: 100
Actual CPU 7 frequency: 3411104
Kernel 4.12: /sys/devices/system/cpu/cpu7/cpufreq/cpuinfo_cur_freq: 3400000
Kernel 4.13-rc1: /sys/devices/system/cpu/cpu7/cpufreq/cpuinfo_cur_freq: 3400000
Kernel 4.12: /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 3400000
Kernel 4.13-rc1, 1st read: /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 1765012*
Kernel 4.13-rc1, 2nd read: /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 3411286

/sys/devices/system/cpu/intel_pstate/max_perf_pct: 42
Actual CPU 7 frequency: 1605225
Kernel 4.12: /sys/devices/system/cpu/cpu7/cpufreq/cpuinfo_cur_freq: 1599768
Kernel 4.13-rc1: /sys/devices/system/cpu/cpu7/cpufreq/cpuinfo_cur_freq: 1599975
Kernel 4.12: /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 1599768
Kernel 4.13-rc1, 1st read: /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 3309707*
Kernel 4.13-rc1, 2nd read: /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 1605311

* The value listed for the first read is both a function of the time difference between
changing the maximum CPU frequency and the inquiry and how long since the last read the
actual CPU frequency was changed.

Data (for increase from 1.6 GHz to 3.4 GHz):
First read quickly (manually): 1765012
0.25 seconds to first read: 1663176
0.5 seconds to first read: 1671658
 1 seconds to first read: 1767889
 2 seconds to first read: 1872128
 3 seconds to first read: 1769770
 4 seconds to first read: 1814673
 5 seconds to first read: 2297147
10 seconds to first read: 2394407
20 seconds to first read: 2720619
30 seconds to first read: 2875374
2 minutes to first read: 3373563
5 minutes to first read: 3363630
10 minutes to first read: 3376521

Data (for decrease from 3.4 GHz to 1.6 GHz):
0.25 seconds to first read: 3381255
0.5 seconds to first read: 3323808
 1 seconds to first read: 3247873
 2 seconds to first read: 3090182
 3 seconds to first read: 3104870
 4 seconds to first read: 2837281
 5 seconds to first read: 2962827
10 seconds to first read: 2510951
20 seconds to first read: 2763956
30 seconds to first read: 2116198
2 minutes to first read: 1876923
5 minutes to first read: 1715839
10 minutes to first read: 1634040

Note: the above table was done more or less manually.

Test 2 data:

Just take the load off of CPU 7 and then look at its frequency (any amount of time later, I have yet to find a time limit):

Kernel 4.13-rc1, 1st read (1 minute after load removed): /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 3410964
Kernel 4.13-rc1, 2nd read (anytime after the 1st read): /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 1605326

Kernel 4.13-rc1, 1st read (24 minutes after load removed): /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 3268873
Kernel 4.13-rc1, 2nd read (anytime after the 1st read): /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq: 1605233
 
[1] http://marc.info/?t=149766883400002&r=1&w=2

Note: now also tested with kernel 4.13-rc2.

... Doug

[toc] | [next] | [standalone]


#1697424 — Re: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF

FromLen Brown <lenb@kernel.org>
Date2017-07-26 19:30 +0200
SubjectRe: [PATCH 2/4 v2] x86: use common aperfmperf_khz_on_cpu() to calculate KHz using APERF/MPERF
Message-ID<u7yif-3eE-45@gated-at.bofh.it>
In reply to#1696648
Hi Doug,

Clearly you are a "discerning user", who understands the limitations
of the kernel sysfs interface,
both new and old, for communicating frequency.  With the limitations
of the (old and new)
sysfs interfaces, why are you using it, rather than turbostat?

>> As with previous methods of calculating MHz,
>> idle time is excluded.
>
> Which makes the response time to a correct answer
> asymmetric. i.e. removal of a load on a CPU will
> linger much much longer that adding a load on a CPU.

If the measurement interval is not defined, then a "correct answer"
is also no defined.

Users now have the capability to define the measurement interval.
Before they didn't, they just could observe it was "short enough
that it looks current".  Others may want to measure frequency over
a longer (or even known) interval, and the previous code made
that impossible.

Also, you may be interested to know that in HWP mode, intel_pstate
used to have a periodic timer who's _only_ job was to wake up the
CPU so that the driver could update the frequency statistic for sysfs.
(now it is a scheduler callback)
Sure, a "discerning user" may have noticed that they have "fresh" data
in sysfs, but most users were better served by not having a timer
fire to refresh data that they'll never consume...  The new code
never runs at all, unless the user asks it to.

> Somehow, somewhere along the way, turbostat no longer seems
> to use base_MHz based on the actual TSC. It used to.

True, though not directly related to this thread...
On current and future Intel hardware, base_mhz and TSC rate
are not in the same clock domain.  Only on very specific configurations
are those clock rates now equal.

>> +     /* Don't bother re-computing within 10 ms */
>> +     if (time_before(jiffies, s->jiffies + HZ/100))
>> +             return;
>
> The above condition would be 8 mSec on a 250 Hertz kernel,
> wouldn't it?
> (I don't care, I'm just saying.)

True.  We could replace the "10ms" comment with "typically 10ms", or "recently".
Note that the value here isn't precise, it is there just to prevent
wasted overhead.
The previous version of this patch was equally valid with a value 10x larger.

> Summary:
>
> . There no longer seems to be a way to check the CPU frequency without affecting the processor (i.e. forcing a wakeup),
> thereby potentially influencing the system under test.

This has always been true, just that the wakeups used to happen inside
the kernel -- whether you consumed the answer or not.

> . Yes, the old way might have been a "lie", but in some situations it was much much less of a "lie", and took data that
> was already available (and at the very maximum 4 seconds old), and didn't force a wakeup, thus monitoring CPU frequency
> was a negligible perturbation to the system.

Frequency data isn't "already available", it has to be measured.
A measurement is not valid unless it is made over a known measurement interval.

> . Now the data is as old as the time the command was run, which might be hours.

True, under controlled conditions, the sysfs measurement interval
could be days or months long.

If a known interval is desired, than something need to provoke a read
of the attribute
at the start of the interval of interest.

Yes, we could do this inside the kernel, but then that would add
overhead  to the
system for the vast majority of users who never even read this attribute,
and it would also take control of the interval away from the user.

Making this interface more complex inside the kernel doesn't seem like
a prudent path to go down
when turbostat already exists and can already measure
concurrent/overlapping intervals of arbitrary length
in user-space.

While I still haven't gleaned exactly what you are trying to measure,
I'm very much interested to know if/why you can't measure it using
the new sysfs attribute semantics, or better yet, using turbostat.

thanks,
Len Brown, Intel Open Source Technology Center

[toc] | [prev] | [next] | [standalone]


#1698380 — [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-28 02:30 +0200
Subject[PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected
Message-ID<u81kd-4Lf-3@gated-at.bofh.it>
In reply to#1696648
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

After commit f8475cef9008 "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
aperfmperf_snapshot_khz() call which may be stale.  However, the
interface is expected to return meaningful values on every read,
including the first one.

To address this problem 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) and
adjust aperfmperf_snapshot_khz() for that.

Fixes: f8475cef9008 "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>
---
 arch/x86/kernel/cpu/aperfmperf.c |   36 +++++++++++++++++++++++++++++-------
 1 file changed, 29 insertions(+), 7 deletions(-)

Index: linux-pm/arch/x86/kernel/cpu/aperfmperf.c
===================================================================
--- linux-pm.orig/arch/x86/kernel/cpu/aperfmperf.c
+++ linux-pm/arch/x86/kernel/cpu/aperfmperf.c
@@ -8,20 +8,25 @@
  * This file is licensed under GPLv2.
  */
 
-#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/math64.h>
 #include <linux/percpu.h>
 #include <linux/smp.h>
 
 struct aperfmperf_sample {
 	unsigned int	khz;
-	unsigned long	jiffies;
+	ktime_t	time;
 	u64	aperf;
 	u64	mperf;
 };
 
 static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
 
+#define APERFMPERF_CACHE_THRESHOLD_MS	10
+#define APERFMPERF_REFRESH_DELAY_MS	20
+#define APERFMPERF_STALE_THRESHOLD_MS	1000
+
 /*
  * aperfmperf_snapshot_khz()
  * On the current CPU, snapshot APERF, MPERF, and jiffies
@@ -33,9 +38,11 @@ static void aperfmperf_snapshot_khz(void
 	u64 aperf, aperf_delta;
 	u64 mperf, mperf_delta;
 	struct aperfmperf_sample *s = this_cpu_ptr(&samples);
+	ktime_t now = ktime_get();
+	s64 time_delta = ktime_ms_delta(now, s->time);
 
-	/* Don't bother re-computing within 10 ms */
-	if (time_before(jiffies, s->jiffies + HZ/100))
+	/* Don't bother re-computing within the cache threshold time. */
+	if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
 		return;
 
 	rdmsrl(MSR_IA32_APERF, aperf);
@@ -51,6 +58,16 @@ static void aperfmperf_snapshot_khz(void
 	if (mperf_delta == 0)
 		return;
 
+	s->time = now;
+	s->aperf = aperf;
+	s->mperf = mperf;
+
+	/* If the previous iteration was too long ago, discard it. */
+	if (time_delta > APERFMPERF_STALE_THRESHOLD_MS) {
+		s->khz = 0;
+		return;
+	}
+
 	/*
 	 * if (cpu_khz * aperf_delta) fits into ULLONG_MAX, then
 	 *	khz = (cpu_khz * aperf_delta) / mperf_delta
@@ -60,13 +77,12 @@ static void aperfmperf_snapshot_khz(void
 	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)
 {
+	unsigned int khz;
+
 	if (!cpu_khz)
 		return 0;
 
@@ -74,6 +90,12 @@ unsigned int arch_freq_get_on_cpu(int cp
 		return 0;
 
 	smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
+	khz = per_cpu(samples.khz, cpu);
+	if (khz)
+		return khz;
+
+	msleep(APERFMPERF_REFRESH_DELAY_MS);
+	smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
 
 	return per_cpu(samples.khz, cpu);
 }

[toc] | [prev] | [next] | [standalone]


#1698752 — [PATCH v2] cpufreq: x86: Make scaling_cur_freq behave more as expected

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-28 15:00 +0200
Subject[PATCH v2] cpufreq: x86: Make scaling_cur_freq behave more as expected
Message-ID<u8d22-3Rv-7@gated-at.bofh.it>
In reply to#1698380
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

After commit f8475cef9008 "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: f8475cef9008 "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>
---

-> v2: Simplify the khz computations as per the Doug's suggestion.

---
 arch/x86/kernel/cpu/aperfmperf.c |   40 +++++++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 14 deletions(-)

Index: linux-pm/arch/x86/kernel/cpu/aperfmperf.c
===================================================================
--- linux-pm.orig/arch/x86/kernel/cpu/aperfmperf.c
+++ linux-pm/arch/x86/kernel/cpu/aperfmperf.c
@@ -8,20 +8,25 @@
  * This file is licensed under GPLv2.
  */
 
-#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/math64.h>
 #include <linux/percpu.h>
 #include <linux/smp.h>
 
 struct aperfmperf_sample {
 	unsigned int	khz;
-	unsigned long	jiffies;
+	ktime_t	time;
 	u64	aperf;
 	u64	mperf;
 };
 
 static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
 
+#define APERFMPERF_CACHE_THRESHOLD_MS	10
+#define APERFMPERF_REFRESH_DELAY_MS	20
+#define APERFMPERF_STALE_THRESHOLD_MS	1000
+
 /*
  * aperfmperf_snapshot_khz()
  * On the current CPU, snapshot APERF, MPERF, and jiffies
@@ -33,9 +38,11 @@ static void aperfmperf_snapshot_khz(void
 	u64 aperf, aperf_delta;
 	u64 mperf, mperf_delta;
 	struct aperfmperf_sample *s = this_cpu_ptr(&samples);
+	ktime_t now = ktime_get();
+	s64 time_delta = ktime_ms_delta(now, s->time);
 
-	/* Don't bother re-computing within 10 ms */
-	if (time_before(jiffies, s->jiffies + HZ/100))
+	/* Don't bother re-computing within the cache threshold time. */
+	if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
 		return;
 
 	rdmsrl(MSR_IA32_APERF, aperf);
@@ -51,22 +58,21 @@ static void aperfmperf_snapshot_khz(void
 	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->time = now;
 	s->aperf = aperf;
 	s->mperf = mperf;
+
+	/* If the previous iteration was too long ago, discard it. */
+	if (time_delta > APERFMPERF_STALE_THRESHOLD_MS)
+		s->khz = 0;
+	else
+		s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
 }
 
 unsigned int arch_freq_get_on_cpu(int cpu)
 {
+	unsigned int khz;
+
 	if (!cpu_khz)
 		return 0;
 
@@ -74,6 +80,12 @@ unsigned int arch_freq_get_on_cpu(int cp
 		return 0;
 
 	smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
+	khz = per_cpu(samples.khz, cpu);
+	if (khz)
+		return khz;
+
+	msleep(APERFMPERF_REFRESH_DELAY_MS);
+	smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
 
 	return per_cpu(samples.khz, cpu);
 }

[toc] | [prev] | [next] | [standalone]


#1698486 — RE: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected

From"Doug Smythies" <dsmythies@telus.net>
Date2017-07-28 08:10 +0200
SubjectRE: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected
Message-ID<u86Df-8nH-3@gated-at.bofh.it>
In reply to#1696648
On 2017.07.27 17:13 Rafael J. Wysocki wrote:

> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> After commit f8475cef9008 "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
> aperfmperf_snapshot_khz() call which may be stale.  However, the
> interface is expected to return meaningful values on every read,
> including the first one.
>
> To address this problem 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) and
> adjust aperfmperf_snapshot_khz() for that.
>
> Fixes: f8475cef9008 "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>
> ---
> arch/x86/kernel/cpu/aperfmperf.c |   36 +++++++++++++++++++++++++++++-------
> 1 file changed, 29 insertions(+), 7 deletions(-)
>
> Index: linux-pm/arch/x86/kernel/cpu/aperfmperf.c

...[deleted the rest]...

This proposed patch would be good. However, I can only try it maybe by Sunday.
I think the maximum time span means that this code:

        /*
         * 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));

Could be reduced to this:

        s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);

Because it could never overflow anymore.

... Doug

[toc] | [prev] | [next] | [standalone]


#1698733 — Re: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-28 14:40 +0200
SubjectRe: [PATCH] cpufreq: x86: Make scaling_cur_freq behave more as expected
Message-ID<u8cIF-3Kl-9@gated-at.bofh.it>
In reply to#1698486
On Thursday, July 27, 2017 11:01:39 PM Doug Smythies wrote:
> On 2017.07.27 17:13 Rafael J. Wysocki wrote:
> 
> > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> >
> > After commit f8475cef9008 "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
> > aperfmperf_snapshot_khz() call which may be stale.  However, the
> > interface is expected to return meaningful values on every read,
> > including the first one.
> >
> > To address this problem 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) and
> > adjust aperfmperf_snapshot_khz() for that.
> >
> > Fixes: f8475cef9008 "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>
> > ---
> > arch/x86/kernel/cpu/aperfmperf.c |   36 +++++++++++++++++++++++++++++-------
> > 1 file changed, 29 insertions(+), 7 deletions(-)
> >
> > Index: linux-pm/arch/x86/kernel/cpu/aperfmperf.c
> 
> ...[deleted the rest]...
> 
> This proposed patch would be good. However, I can only try it maybe by Sunday.
> I think the maximum time span means that this code:
> 
>         /*
>          * 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));
> 
> Could be reduced to this:
> 
>         s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
> 
> Because it could never overflow anymore.

Right, that's a good point.

I'll send a v2 with this change included shortly.

Thanks,
Rafael

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web