mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups
@ 2026-08-13  9:01 lirongqing
  2026-08-13  9:01 ` [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io() lirongqing
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: lirongqing @ 2026-08-13  9:01 UTC (permalink / raw)
  To: Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han, Li RongQing

From: Li RongQing <lirongqing@baidu.com>

policy->freq_table is built with duplicate _PSS frequencies dropped, so it
no longer lines up positionally with perf->states[]; the original P-state
index is kept in freq_table[].driver_data. extract_io() and
get_cur_freq_on_cpu() indexed freq_table[] with a perf->states[] index
anyway, which can return the frequency of a wrong P-state.

Changes since v1:
- Reword patch 1: v1 described it as an out-of-bounds read, which is wrong;
  it is an index-mismatch / wrong-result bug, not a memory-safety issue.
- Add patch 2 for get_cur_freq_on_cpu().
- Add Fixes: tags (not exact, but reasonable backport targets).

Li RongQing (2):
  cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io()
  cpufreq: acpi-cpufreq: fix P-state index mismatch in
    get_cur_freq_on_cpu()

 drivers/cpufreq/acpi-cpufreq.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

-- 
2.9.4


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io()
  2026-08-13  9:01 [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups lirongqing
@ 2026-08-13  9:01 ` lirongqing
  2026-08-17 13:33   ` Zhongqiu Han
  2026-08-13  9:01 ` [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu() lirongqing
  2026-08-17 12:45 ` [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups Zhongqiu Han
  2 siblings, 1 reply; 9+ messages in thread
From: lirongqing @ 2026-08-13  9:01 UTC (permalink / raw)
  To: Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han, Li RongQing

From: Li RongQing <lirongqing@baidu.com>

When policy->freq_table is built in acpi_cpufreq_cpu_init(), entries
with duplicate frequencies are skipped. The original P-state index for
each remaining entry is stored in freq_table[].driver_data, so the index
space of freq_table no longer matches perf->states[].

extract_io() walks perf->states[] with index i and uses the same i to
index policy->freq_table[i]. This causes two problems when duplicate
frequencies exist:

  - Returning the frequency of the wrong P-state
  - Returning 0 from a zeroed tail entry, or even CPUFREQ_TABLE_END
    (~1u) reported as 0xfffffffe kHz

Fix it by walking policy->freq_table with cpufreq_for_each_entry() and
using perf->states[pos->driver_data].status, aligning with extract_msr().

Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from acpi_cpufreq_data")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
---
 drivers/cpufreq/acpi-cpufreq.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 21639d9..1abe9ab 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -197,14 +197,13 @@ static unsigned extract_io(struct cpufreq_policy *policy, u32 value)
 {
 	struct acpi_cpufreq_data *data = policy->driver_data;
 	struct acpi_processor_performance *perf;
-	int i;
+	struct cpufreq_frequency_table *pos;
 
 	perf = to_perf_data(data);
 
-	for (i = 0; i < perf->state_count; i++) {
-		if (value == perf->states[i].status)
-			return policy->freq_table[i].frequency;
-	}
+	cpufreq_for_each_entry(pos, policy->freq_table)
+		if (value == perf->states[pos->driver_data].status)
+			return pos->frequency;
 	return 0;
 }
 
-- 
2.9.4


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu()
  2026-08-13  9:01 [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups lirongqing
  2026-08-13  9:01 ` [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io() lirongqing
@ 2026-08-13  9:01 ` lirongqing
  2026-08-18 14:02   ` Zhongqiu Han
  2026-08-17 12:45 ` [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups Zhongqiu Han
  2 siblings, 1 reply; 9+ messages in thread
From: lirongqing @ 2026-08-13  9:01 UTC (permalink / raw)
  To: Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han, Li RongQing

From: Li RongQing <lirongqing@baidu.com>

get_cur_freq_on_cpu() reads the cached frequency as
policy->freq_table[to_perf_data(data)->state], mixing two different index
spaces: perf->state indexes perf->states[], while policy->freq_table[] is
built with duplicate frequencies removed and stores the original P-state
index in freq_table[].driver_data.

Once any _PSS entry has been skipped the two arrays no longer line up, so
the cached frequency used to detect a "BIOS changed frequency behind our
back" event could be taken from the wrong table slot.

Look up the freq_table entry whose driver_data matches perf->state instead
of indexing freq_table[] with perf->state directly.

Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from acpi_cpufreq_data")
Reported-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
Signed-off-by: Li RongQing <lirongqing@baidu.com>
---
 drivers/cpufreq/acpi-cpufreq.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 1abe9ab..61ede49c 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -353,6 +353,7 @@ static u32 get_cur_val(const struct cpumask *mask, struct acpi_cpufreq_data *dat
 
 static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
 {
+	struct cpufreq_frequency_table *pos;
 	struct acpi_cpufreq_data *data;
 	struct cpufreq_policy *policy;
 	unsigned int freq;
@@ -368,7 +369,13 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
 	if (unlikely(!data || !policy->freq_table))
 		return 0;
 
-	cached_freq = policy->freq_table[to_perf_data(data)->state].frequency;
+	cached_freq = 0;
+	cpufreq_for_each_entry(pos, policy->freq_table)
+		if (pos->driver_data == to_perf_data(data)->state) {
+			cached_freq = pos->frequency;
+			break;
+		}
+
 	freq = extract_freq(policy, get_cur_val(cpumask_of(cpu), data));
 	if (freq != cached_freq) {
 		/*
-- 
2.9.4


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups
  2026-08-13  9:01 [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups lirongqing
  2026-08-13  9:01 ` [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io() lirongqing
  2026-08-13  9:01 ` [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu() lirongqing
@ 2026-08-17 12:45 ` Zhongqiu Han
  2 siblings, 0 replies; 9+ messages in thread
From: Zhongqiu Han @ 2026-08-17 12:45 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han

On 8/13/2026 5:01 PM, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
> 
> policy->freq_table is built with duplicate _PSS frequencies dropped, so it
> no longer lines up positionally with perf->states[]; the original P-state
> index is kept in freq_table[].driver_data. extract_io() and
> get_cur_freq_on_cpu() indexed freq_table[] with a perf->states[] index
> anyway, which can return the frequency of a wrong P-state.
> 
> Changes since v1:
> - Reword patch 1: v1 described it as an out-of-bounds read, which is wrong;
>    it is an index-mismatch / wrong-result bug, not a memory-safety issue.
> - Add patch 2 for get_cur_freq_on_cpu().
> - Add Fixes: tags (not exact, but reasonable backport targets).

Hi RongQing,

The Fixes tag should point to the commit that introduced the bug.

> 
> Li RongQing (2):
>    cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io()
>    cpufreq: acpi-cpufreq: fix P-state index mismatch in
>      get_cur_freq_on_cpu()
> 
>   drivers/cpufreq/acpi-cpufreq.c | 18 ++++++++++++------
>   1 file changed, 12 insertions(+), 6 deletions(-)
> 


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io()
  2026-08-13  9:01 ` [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io() lirongqing
@ 2026-08-17 13:33   ` Zhongqiu Han
  2026-08-18  6:46     ` 答复: [外部邮件] " Li,Rongqing
  0 siblings, 1 reply; 9+ messages in thread
From: Zhongqiu Han @ 2026-08-17 13:33 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han

On 8/13/2026 5:01 PM, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
> 
> When policy->freq_table is built in acpi_cpufreq_cpu_init(), entries
> with duplicate frequencies are skipped. The original P-state index for
> each remaining entry is stored in freq_table[].driver_data, so the index
> space of freq_table no longer matches perf->states[].
> 
> extract_io() walks perf->states[] with index i and uses the same i to
> index policy->freq_table[i]. This causes two problems when duplicate
> frequencies exist:
> 
>    - Returning the frequency of the wrong P-state
>    - Returning 0 from a zeroed tail entry, or even CPUFREQ_TABLE_END
>      (~1u) reported as 0xfffffffe kHz

AFAICT, It may be worth expanding the changelog to cover the
consequences described below , particularly the resulting divergence
between the hardware state and the cpufreq core's view of the current
frequency:

Furthermore, extract_io() is only reachable on ACPI_ADR_SPACE_SYSTEM_IO
platforms, where cpufreq_driver->get is not installed, so its only
caller in practice is check_freqs(), i.e. only when the
acpi_pstate_strict module parameter is set.

There, the mismatched lookup makes the frequency comparison fail even
though drv_write() has already switched the hardware to the requested
P-state.  So check_freqs() sleeps through all 100 iterations - at least
~1 ms of usleep_range() plus 100 cross-CPU calls and I/O port reads, all
with policy->rwsem held - and ->target_index() returns -EAGAIN. perf
->state is therefore left at its previous value while the hardware sits
at the new one.

The core then restores policy->cur to the old frequency, and
because __cpufreq_driver_target() returns early when the requested
frequency equals policy->cur, the driver is not called again for it - so
the control register is not rewritten and the CPU is left running at a
frequency the core does not know about.


> 
> Fix it by walking policy->freq_table with cpufreq_for_each_entry() and
> using perf->states[pos->driver_data].status, aligning with extract_msr().
> 
> Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from acpi_cpufreq_data")

The real tag should be fe27cb358835 ("[CPUFREQ][2/8] acpi:
reorganize code to make MSR support addition easier")

That commit added both the entry-skipping loop with the
freq_table[].index (now .driver_data) back-pointer, which is what makes
the two indices diverge, and the faulty lookup itself -- back then in a
function called extract_freq(). dde9f7ba60ad ("[CPUFREQ][3/8] acpi
cpufreq: Pull in MSR based transition support") merely renamed it to
extract_io().


> Signed-off-by: Li RongQing <lirongqing@baidu.com>
> ---
>   drivers/cpufreq/acpi-cpufreq.c | 9 ++++-----
>   1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
> index 21639d9..1abe9ab 100644
> --- a/drivers/cpufreq/acpi-cpufreq.c
> +++ b/drivers/cpufreq/acpi-cpufreq.c
> @@ -197,14 +197,13 @@ static unsigned extract_io(struct cpufreq_policy *policy, u32 value)
>   {
>   	struct acpi_cpufreq_data *data = policy->driver_data;
>   	struct acpi_processor_performance *perf;
> -	int i;
> +	struct cpufreq_frequency_table *pos;
>   
>   	perf = to_perf_data(data);
>   
> -	for (i = 0; i < perf->state_count; i++) {
> -		if (value == perf->states[i].status)
> -			return policy->freq_table[i].frequency;
> -	}
> +	cpufreq_for_each_entry(pos, policy->freq_table)
> +		if (value == perf->states[pos->driver_data].status)
> +			return pos->frequency;
>   	return 0;
>   }
>   


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 9+ messages in thread

* 答复: [外部邮件] Re: [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io()
  2026-08-17 13:33   ` Zhongqiu Han
@ 2026-08-18  6:46     ` Li,Rongqing
  2026-08-18  8:38       ` Zhongqiu Han
  0 siblings, 1 reply; 9+ messages in thread
From: Li,Rongqing @ 2026-08-18  6:46 UTC (permalink / raw)
  To: Zhongqiu Han, Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel

> > When policy->freq_table is built in acpi_cpufreq_cpu_init(), entries
> > with duplicate frequencies are skipped. The original P-state index for
> > each remaining entry is stored in freq_table[].driver_data, so the
> > index space of freq_table no longer matches perf->states[].
> >
> > extract_io() walks perf->states[] with index i and uses the same i to
> > index policy->freq_table[i]. This causes two problems when duplicate
> > frequencies exist:
> >
> >    - Returning the frequency of the wrong P-state
> >    - Returning 0 from a zeroed tail entry, or even CPUFREQ_TABLE_END
> >      (~1u) reported as 0xfffffffe kHz
> 
> AFAICT, It may be worth expanding the changelog to cover the consequences
> described below , particularly the resulting divergence between the hardware
> state and the cpufreq core's view of the current
> frequency:
> 
> Furthermore, extract_io() is only reachable on ACPI_ADR_SPACE_SYSTEM_IO
> platforms, where cpufreq_driver->get is not installed, so its only caller in
> practice is check_freqs(), i.e. only when the acpi_pstate_strict module
> parameter is set.
> 
> There, the mismatched lookup makes the frequency comparison fail even
> though drv_write() has already switched the hardware to the requested P-state.
> So check_freqs() sleeps through all 100 iterations - at least
> ~1 ms of usleep_range() plus 100 cross-CPU calls and I/O port reads, all with
> policy->rwsem held - and ->target_index() returns -EAGAIN. perf
> ->state is therefore left at its previous value while the hardware sits
> at the new one.
> 
> The core then restores policy->cur to the old frequency, and because
> __cpufreq_driver_target() returns early when the requested frequency equals
> policy->cur, the driver is not called again for it - so the control register is not
> rewritten and the CPU is left running at a frequency the core does not know
> about.
> 
> 
> >
> > Fix it by walking policy->freq_table with cpufreq_for_each_entry() and
> > using perf->states[pos->driver_data].status, aligning with extract_msr().
> >
> > Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from
> > acpi_cpufreq_data")
> 
> The real tag should be fe27cb358835 ("[CPUFREQ][2/8] acpi:
> reorganize code to make MSR support addition easier")
> 
> That commit added both the entry-skipping loop with the freq_table[].index
> (now .driver_data) back-pointer, which is what makes the two indices diverge,
> and the faulty lookup itself -- back then in a function called extract_freq().
> dde9f7ba60ad ("[CPUFREQ][3/8] acpi
> cpufreq: Pull in MSR based transition support") merely renamed it to
> extract_io().
> 

Thanks, I will send v3

[Li,Rongqing] 


> 
> > Signed-off-by: Li RongQing <lirongqing@baidu.com>
> > ---
> >   drivers/cpufreq/acpi-cpufreq.c | 9 ++++-----
> >   1 file changed, 4 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/cpufreq/acpi-cpufreq.c
> > b/drivers/cpufreq/acpi-cpufreq.c index 21639d9..1abe9ab 100644
> > --- a/drivers/cpufreq/acpi-cpufreq.c
> > +++ b/drivers/cpufreq/acpi-cpufreq.c
> > @@ -197,14 +197,13 @@ static unsigned extract_io(struct cpufreq_policy
> *policy, u32 value)
> >   {
> >   	struct acpi_cpufreq_data *data = policy->driver_data;
> >   	struct acpi_processor_performance *perf;
> > -	int i;
> > +	struct cpufreq_frequency_table *pos;
> >
> >   	perf = to_perf_data(data);
> >
> > -	for (i = 0; i < perf->state_count; i++) {
> > -		if (value == perf->states[i].status)
> > -			return policy->freq_table[i].frequency;
> > -	}
> > +	cpufreq_for_each_entry(pos, policy->freq_table)
> > +		if (value == perf->states[pos->driver_data].status)
> > +			return pos->frequency;
> >   	return 0;
> >   }
> >
> 
> 
> --
> Thx and BRs,
> Zhongqiu Han


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: 答复: [外部邮件] Re: [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io()
  2026-08-18  6:46     ` 答复: [外部邮件] " Li,Rongqing
@ 2026-08-18  8:38       ` Zhongqiu Han
  0 siblings, 0 replies; 9+ messages in thread
From: Zhongqiu Han @ 2026-08-18  8:38 UTC (permalink / raw)
  To: Li,Rongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han

On 8/18/2026 2:46 PM, Li,Rongqing wrote:
>>> When policy->freq_table is built in acpi_cpufreq_cpu_init(), entries
>>> with duplicate frequencies are skipped. The original P-state index for
>>> each remaining entry is stored in freq_table[].driver_data, so the
>>> index space of freq_table no longer matches perf->states[].
>>>
>>> extract_io() walks perf->states[] with index i and uses the same i to
>>> index policy->freq_table[i]. This causes two problems when duplicate
>>> frequencies exist:
>>>
>>>     - Returning the frequency of the wrong P-state
>>>     - Returning 0 from a zeroed tail entry, or even CPUFREQ_TABLE_END
>>>       (~1u) reported as 0xfffffffe kHz
>>
>> AFAICT, It may be worth expanding the changelog to cover the consequences
>> described below , particularly the resulting divergence between the hardware
>> state and the cpufreq core's view of the current
>> frequency:
>>
>> Furthermore, extract_io() is only reachable on ACPI_ADR_SPACE_SYSTEM_IO
>> platforms, where cpufreq_driver->get is not installed, so its only caller in
>> practice is check_freqs(), i.e. only when the acpi_pstate_strict module
>> parameter is set.
>>
>> There, the mismatched lookup makes the frequency comparison fail even
>> though drv_write() has already switched the hardware to the requested P-state.
>> So check_freqs() sleeps through all 100 iterations - at least
>> ~1 ms of usleep_range() plus 100 cross-CPU calls and I/O port reads, all with
>> policy->rwsem held - and ->target_index() returns -EAGAIN. perf
>> ->state is therefore left at its previous value while the hardware sits
>> at the new one.
>>
>> The core then restores policy->cur to the old frequency, and because
>> __cpufreq_driver_target() returns early when the requested frequency equals
>> policy->cur, the driver is not called again for it - so the control register is not
>> rewritten and the CPU is left running at a frequency the core does not know
>> about.
>>
>>
>>>
>>> Fix it by walking policy->freq_table with cpufreq_for_each_entry() and
>>> using perf->states[pos->driver_data].status, aligning with extract_msr().
>>>
>>> Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from
>>> acpi_cpufreq_data")
>>
>> The real tag should be fe27cb358835 ("[CPUFREQ][2/8] acpi:
>> reorganize code to make MSR support addition easier")
>>
>> That commit added both the entry-skipping loop with the freq_table[].index
>> (now .driver_data) back-pointer, which is what makes the two indices diverge,
>> and the faulty lookup itself -- back then in a function called extract_freq().
>> dde9f7ba60ad ("[CPUFREQ][3/8] acpi
>> cpufreq: Pull in MSR based transition support") merely renamed it to
>> extract_io().
>>
> 
> Thanks, I will send v3

Thanks, I will review the patch v2 2/2

> 
> [Li,Rongqing]
> 
> 
>>
>>> Signed-off-by: Li RongQing <lirongqing@baidu.com>
>>> ---
>>>    drivers/cpufreq/acpi-cpufreq.c | 9 ++++-----
>>>    1 file changed, 4 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/cpufreq/acpi-cpufreq.c
>>> b/drivers/cpufreq/acpi-cpufreq.c index 21639d9..1abe9ab 100644
>>> --- a/drivers/cpufreq/acpi-cpufreq.c
>>> +++ b/drivers/cpufreq/acpi-cpufreq.c
>>> @@ -197,14 +197,13 @@ static unsigned extract_io(struct cpufreq_policy
>> *policy, u32 value)
>>>    {
>>>    	struct acpi_cpufreq_data *data = policy->driver_data;
>>>    	struct acpi_processor_performance *perf;
>>> -	int i;
>>> +	struct cpufreq_frequency_table *pos;
>>>
>>>    	perf = to_perf_data(data);
>>>
>>> -	for (i = 0; i < perf->state_count; i++) {
>>> -		if (value == perf->states[i].status)
>>> -			return policy->freq_table[i].frequency;
>>> -	}
>>> +	cpufreq_for_each_entry(pos, policy->freq_table)
>>> +		if (value == perf->states[pos->driver_data].status)
>>> +			return pos->frequency;
>>>    	return 0;
>>>    }
>>>
>>
>>
>> --
>> Thx and BRs,
>> Zhongqiu Han
> 


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu()
  2026-08-13  9:01 ` [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu() lirongqing
@ 2026-08-18 14:02   ` Zhongqiu Han
  2026-08-19 11:21     ` 答复: [外部邮件] " Li,Rongqing
  0 siblings, 1 reply; 9+ messages in thread
From: Zhongqiu Han @ 2026-08-18 14:02 UTC (permalink / raw)
  To: lirongqing, Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel
  Cc: zhongqiu.han

On 8/13/2026 5:01 PM, lirongqing wrote:
> From: Li RongQing <lirongqing@baidu.com>
> 
> get_cur_freq_on_cpu() reads the cached frequency as
> policy->freq_table[to_perf_data(data)->state], mixing two different index
> spaces: perf->state indexes perf->states[], while policy->freq_table[] is
> built with duplicate frequencies removed and stores the original P-state
> index in freq_table[].driver_data.
> 
> Once any _PSS entry has been skipped the two arrays no longer line up, so
> the cached frequency used to detect a "BIOS changed frequency behind our
> back" event could be taken from the wrong table slot.

A further consequence of this index-space mismatch should be that,
depending on which entry is picked, the check either fails on every call
for P-states whose freq_table index differs from their _PSS index,
causing data->resume to force a redundant control-register rewrite on
every ->target(), or silently passes when the wrong slot happens to hold
the frequency the firmware actually moved the CPU to, causing
acpi_cpufreq_target() to short-circuit and leave the CPU running at a
frequency the core does not expect until a different P-state is
requested.

> 
> Look up the freq_table entry whose driver_data matches perf->state instead
> of indexing freq_table[] with perf->state directly.
> 
> Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from acpi_cpufreq_data")

The real Fixes tag should be e56a727b023d ("[CPUFREQ] Make acpi-cpufreq
more robust against BIOS freq changes behind our back.")

> Reported-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> Signed-off-by: Li RongQing <lirongqing@baidu.com>
> ---
>   drivers/cpufreq/acpi-cpufreq.c | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
> index 1abe9ab..61ede49c 100644
> --- a/drivers/cpufreq/acpi-cpufreq.c
> +++ b/drivers/cpufreq/acpi-cpufreq.c
> @@ -353,6 +353,7 @@ static u32 get_cur_val(const struct cpumask *mask, struct acpi_cpufreq_data *dat
>   
>   static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
>   {
> +	struct cpufreq_frequency_table *pos;
>   	struct acpi_cpufreq_data *data;
>   	struct cpufreq_policy *policy;
>   	unsigned int freq;
> @@ -368,7 +369,13 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
>   	if (unlikely(!data || !policy->freq_table))
>   		return 0;
>   
> -	cached_freq = policy->freq_table[to_perf_data(data)->state].frequency;

How about:
	struct acpi_processor_performance *perf = to_perf_data(data);
	...
	cached_freq = perf->states[perf->state].core_frequency * 1000;

get_cur_freq_on_cpu() is only called on ACPI_ADR_SPACE_FIXED_HARDWARE
platforms, and on such platforms perf->state is only assigned in the
following functions:

(1) acpi_cpufreq_target(): perf->state is then the index of the P-state
     last written to the hardware.
(2) acpi_cpufreq_fast_switch(): same as above (1).

(3) acpi_cpufreq_cpu_init(): this sets the initial value perf->state =
     0. In cpufreq_online(), after .init() has been called, .get() - i.e.
     get_cur_freq_on_cpu() - is called once.  The freq read at that point
     may be a leftover value from the hardware, but whether or not
     "if (freq != cached_freq)" holds, the only consequence is
     data->resume = 1, and data->resume has already been initialised to 1
     in .init() anyway.


> +	cached_freq = 0;
> +	cpufreq_for_each_entry(pos, policy->freq_table)
> +		if (pos->driver_data == to_perf_data(data)->state) {
> +			cached_freq = pos->frequency;
> +			break;
> +		}
> +
>   	freq = extract_freq(policy, get_cur_val(cpumask_of(cpu), data));
>   	if (freq != cached_freq) {
>   		/*


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply	[flat|nested] 9+ messages in thread

* 答复: [外部邮件] Re: [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu()
  2026-08-18 14:02   ` Zhongqiu Han
@ 2026-08-19 11:21     ` Li,Rongqing
  0 siblings, 0 replies; 9+ messages in thread
From: Li,Rongqing @ 2026-08-19 11:21 UTC (permalink / raw)
  To: Zhongqiu Han, Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-kernel



> -----邮件原件-----
> 发件人: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> 发送时间: 2026年8月18日 22:02
> 收件人: Li,Rongqing <lirongqing@baidu.com>; Rafael J . Wysocki
> <rafael@kernel.org>; Viresh Kumar <viresh.kumar@linaro.org>;
> linux-pm@vger.kernel.org; linux-kernel@vger.kernel.org
> 抄送: zhongqiu.han@oss.qualcomm.com
> 主题: [外部邮件] Re: [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index
> mismatch in get_cur_freq_on_cpu()
> 
> On 8/13/2026 5:01 PM, lirongqing wrote:
> > From: Li RongQing <lirongqing@baidu.com>
> >
> > get_cur_freq_on_cpu() reads the cached frequency as
> > policy->freq_table[to_perf_data(data)->state], mixing two different
> > policy->index
> > spaces: perf->state indexes perf->states[], while policy->freq_table[]
> > is built with duplicate frequencies removed and stores the original
> > P-state index in freq_table[].driver_data.
> >
> > Once any _PSS entry has been skipped the two arrays no longer line up,
> > so the cached frequency used to detect a "BIOS changed frequency
> > behind our back" event could be taken from the wrong table slot.
> 
> A further consequence of this index-space mismatch should be that, depending
> on which entry is picked, the check either fails on every call for P-states whose
> freq_table index differs from their _PSS index, causing data->resume to force a
> redundant control-register rewrite on every ->target(), or silently passes when
> the wrong slot happens to hold the frequency the firmware actually moved the
> CPU to, causing
> acpi_cpufreq_target() to short-circuit and leave the CPU running at a frequency
> the core does not expect until a different P-state is requested.
> 
> >
> > Look up the freq_table entry whose driver_data matches perf->state
> > instead of indexing freq_table[] with perf->state directly.
> >
> > Fixes: 8cee1eed8e78 ("cpufreq: ACPI: Remove freq_table from
> > acpi_cpufreq_data")
> 
> The real Fixes tag should be e56a727b023d ("[CPUFREQ] Make acpi-cpufreq
> more robust against BIOS freq changes behind our back.")
> 
> > Reported-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> > Signed-off-by: Li RongQing <lirongqing@baidu.com>
> > ---
> >   drivers/cpufreq/acpi-cpufreq.c | 9 ++++++++-
> >   1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/cpufreq/acpi-cpufreq.c
> > b/drivers/cpufreq/acpi-cpufreq.c index 1abe9ab..61ede49c 100644
> > --- a/drivers/cpufreq/acpi-cpufreq.c
> > +++ b/drivers/cpufreq/acpi-cpufreq.c
> > @@ -353,6 +353,7 @@ static u32 get_cur_val(const struct cpumask *mask,
> > struct acpi_cpufreq_data *dat
> >
> >   static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
> >   {
> > +	struct cpufreq_frequency_table *pos;
> >   	struct acpi_cpufreq_data *data;
> >   	struct cpufreq_policy *policy;
> >   	unsigned int freq;
> > @@ -368,7 +369,13 @@ static unsigned int get_cur_freq_on_cpu(unsigned
> int cpu)
> >   	if (unlikely(!data || !policy->freq_table))
> >   		return 0;
> >
> > -	cached_freq = policy->freq_table[to_perf_data(data)->state].frequency;
> 
> How about:
> 	struct acpi_processor_performance *perf = to_perf_data(data);
> 	...
> 	cached_freq = perf->states[perf->state].core_frequency * 1000;
> 

Thanks, that's cleaner and I've adopted it in v3.

Using perf->state to index perf->states[] (its native index space) is exactly right: it avoids the freq_table[] index-space mismatch at the root, and perf->states[perf->state].core_frequency * 1000 is the same value that gets stored into freq_table[] at init time, so the freq != cached_freq resync check is unchanged.

It also removes a corner case in my earlier freq_table-walk version: if perf->state points at a P-state that was deduplicated out of freq_table[], the walk finds no match and leaves cached_freq at 0, misfiring the check. Indexing perf->states[] directly avoids that.

Will send as part of v3.

[Li,Rongqing] 


> get_cur_freq_on_cpu() is only called on ACPI_ADR_SPACE_FIXED_HARDWARE
> platforms, and on such platforms perf->state is only assigned in the following
> functions:
> 
> (1) acpi_cpufreq_target(): perf->state is then the index of the P-state
>      last written to the hardware.
> (2) acpi_cpufreq_fast_switch(): same as above (1).
> 
> (3) acpi_cpufreq_cpu_init(): this sets the initial value perf->state =
>      0. In cpufreq_online(), after .init() has been called, .get() - i.e.
>      get_cur_freq_on_cpu() - is called once.  The freq read at that point
>      may be a leftover value from the hardware, but whether or not
>      "if (freq != cached_freq)" holds, the only consequence is
>      data->resume = 1, and data->resume has already been initialised to 1
>      in .init() anyway.
> 
> 
> > +	cached_freq = 0;
> > +	cpufreq_for_each_entry(pos, policy->freq_table)
> > +		if (pos->driver_data == to_perf_data(data)->state) {
> > +			cached_freq = pos->frequency;
> > +			break;
> > +		}
> > +
> >   	freq = extract_freq(policy, get_cur_val(cpumask_of(cpu), data));
> >   	if (freq != cached_freq) {
> >   		/*
> 
> 
> --
> Thx and BRs,
> Zhongqiu Han

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-08-19 11:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-13  9:01 [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups lirongqing
2026-08-13  9:01 ` [PATCH v2 1/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io() lirongqing
2026-08-17 13:33   ` Zhongqiu Han
2026-08-18  6:46     ` 答复: [外部邮件] " Li,Rongqing
2026-08-18  8:38       ` Zhongqiu Han
2026-08-13  9:01 ` [PATCH v2 2/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in get_cur_freq_on_cpu() lirongqing
2026-08-18 14:02   ` Zhongqiu Han
2026-08-19 11:21     ` 答复: [外部邮件] " Li,Rongqing
2026-08-17 12:45 ` [PATCH v2 0/2] cpufreq: acpi-cpufreq: fix P-state index mismatch in freq lookups Zhongqiu Han

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®