Linux ACPI
 help / color / mirror / Atom feed
* [PATCH v1 0/2] cpufreq: Fix issues related to cached resolved frequency and index
@ 2026-11-06 14:59 Rafael J. Wysocki
  2026-11-06 15:04 ` [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq() Rafael J. Wysocki
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-11-06 14:59 UTC (permalink / raw)
  To: Linux PM
  Cc: Viresh Kumar, Christian Loehle, Zhongqiu Han, Linux ACPI,
	Vincent Donnefort

Hi All,

This series addresses two issues related to the caching of the
resolved frequency and index in __resolve_freq().

The first one is that the cached resolved frequency value should be
the post-resolution one, not the pre-resolution one (which is the
case now).  Patch [1/2] fixes this.

The other issue is that __resolve_freq() is now called in multiple
places, some of which may race with the cached frequency and index
updates in the schedutil governor path and that may cause the cached
values to become invalid.  This is fixed by patch [2/2].

Please see the individual patch changelogs for details.

Thanks!




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

* [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq()
  2026-11-06 14:59 [PATCH v1 0/2] cpufreq: Fix issues related to cached resolved frequency and index Rafael J. Wysocki
@ 2026-11-06 15:04 ` Rafael J. Wysocki
  2026-11-06 17:02   ` Christian Loehle
  2026-11-06 15:05 ` [PATCH v1 2/2] cpufreq: Fix updates of cached resolved frequency and index Rafael J. Wysocki
  2026-11-06 17:02 ` [PATCH v1 0/2] cpufreq: Fix issues related to " Christian Loehle
  2 siblings, 1 reply; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-11-06 15:04 UTC (permalink / raw)
  To: Linux PM
  Cc: Viresh Kumar, Christian Loehle, Zhongqiu Han, Linux ACPI,
	Vincent Donnefort

From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>

get_next_freq() in the schedutil governor returns the resolved
frequency from cpufreq_driver_resolve_freq(), and that resolved value
is then passed to cpufreq_driver_fast_switch().  The only reader of
cached_target_freq, acpi_cpufreq_fast_switch(), compares it with the
post-resolution target frequency, but it holds the pre-resolution
clamped frequency, so any request that falls between table entries
misses the cache and falls back to a table scan.

Address this by making __resolve_freq() store the post-resolution
frequency in cached_target_freq.

Fixes: e3c062360870 ("cpufreq: add cpufreq_driver_resolve_freq()")
Cc: All applicable <stable@vger.kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/cpufreq/cpufreq.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -488,8 +488,8 @@ static unsigned int __resolve_freq(struc
 
 	idx = cpufreq_frequency_table_target(policy, target_freq, min, max, relation);
 	policy->cached_resolved_idx = idx;
-	policy->cached_target_freq = target_freq;
-	return policy->freq_table[idx].frequency;
+	policy->cached_target_freq = policy->freq_table[idx].frequency;
+	return policy->cached_target_freq;
 }
 
 /**




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

* [PATCH v1 2/2] cpufreq: Fix updates of cached resolved frequency and index
  2026-11-06 14:59 [PATCH v1 0/2] cpufreq: Fix issues related to cached resolved frequency and index Rafael J. Wysocki
  2026-11-06 15:04 ` [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq() Rafael J. Wysocki
@ 2026-11-06 15:05 ` Rafael J. Wysocki
  2026-11-06 17:32   ` Christian Loehle
  2026-11-06 17:02 ` [PATCH v1 0/2] cpufreq: Fix issues related to " Christian Loehle
  2 siblings, 1 reply; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-11-06 15:05 UTC (permalink / raw)
  To: Linux PM
  Cc: Viresh Kumar, Christian Loehle, Zhongqiu Han, Linux ACPI,
	Vincent Donnefort

From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>

The cached resolved frequency and index, cached_target_freq and
cached_resolved_idx, respectively, may be updated concurrently if
cpufreq_driver_resolve_freq() is called by the schedutil governor
while cpufreq_set_policy() is running for the same policy.  Those
concurrent updates are not synchronized in any way and the values
written by cpufreq_set_policy(), if it wins the race, may confuse
drivers using those policy fields [1].

To address this, use the observation that the only place where
cached_target_freq and cached_resolved_idx need to be updated is
cpufreq_driver_resolve_freq() because the only legitimate users of
them are .fast_switch() driver callbacks.  Although cached_resolved_idx
is also used in __cpufreq_driver_target(), that is in fact unnecessary
and it is better to use a local variable for storing the frequency
table index in that function.

Accordingly, rearrange __resolve_freq() and its callers so that
cached_target_freq and cached_resolved_idx are only updated in
cpufreq_driver_resolve_freq().

The cached_target_freq initialization in cpufreq_set_policy() is still
needed, but move it after stopping the old governor and before starting
the new one to avoid racing with cpufreq_driver_resolve_freq().

Fixes: f9ccdec24d91 ("cpufreq: Reuse cpufreq_driver_resolve_freq() in __cpufreq_driver_target()")
Fixes: 151717690694 ("cpufreq: Make policy min/max hard requirements")
Reported-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
Closes: https://lore.kernel.org/linux-pm/20261105140357.1331894-1-zhongqiu.han@oss.qualcomm.com/ [1]
Co-developed-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
Signed-off-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
Cc: All applicable <stable@vger.kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/cpufreq/cpufreq.c |   31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)

--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -477,7 +477,7 @@ EXPORT_SYMBOL_GPL(cpufreq_disable_fast_s
 static unsigned int __resolve_freq(struct cpufreq_policy *policy,
 				   unsigned int target_freq,
 				   unsigned int min, unsigned int max,
-				   unsigned int relation)
+				   unsigned int relation, unsigned int *idx_p)
 {
 	unsigned int idx;
 
@@ -487,9 +487,10 @@ static unsigned int __resolve_freq(struc
 		return target_freq;
 
 	idx = cpufreq_frequency_table_target(policy, target_freq, min, max, relation);
-	policy->cached_resolved_idx = idx;
-	policy->cached_target_freq = policy->freq_table[idx].frequency;
-	return policy->cached_target_freq;
+	if (idx_p)
+		*idx_p = idx;
+
+	return policy->freq_table[idx].frequency;
 }
 
 /**
@@ -520,7 +521,10 @@ unsigned int cpufreq_driver_resolve_freq
 	if (unlikely(min > max))
 		min = max;
 
-	return __resolve_freq(policy, target_freq, min, max, CPUFREQ_RELATION_LE);
+	policy->cached_target_freq = __resolve_freq(policy, target_freq, min, max,
+						    CPUFREQ_RELATION_LE,
+						    &policy->cached_resolved_idx);
+	return policy->cached_target_freq;
 }
 EXPORT_SYMBOL_GPL(cpufreq_driver_resolve_freq);
 
@@ -2398,12 +2402,13 @@ int __cpufreq_driver_target(struct cpufr
 			    unsigned int relation)
 {
 	unsigned int old_target_freq = target_freq;
+	unsigned int idx = 0;
 
 	if (cpufreq_disabled())
 		return -ENODEV;
 
 	target_freq = __resolve_freq(policy, target_freq, policy->min,
-				     policy->max, relation);
+				     policy->max, relation, &idx);
 
 	pr_debug("CPU %u: cur %u kHz -> target %u kHz (req %u kHz, rel %u)\n",
 		policy->cpu, policy->cur, target_freq, old_target_freq, relation);
@@ -2433,7 +2438,7 @@ int __cpufreq_driver_target(struct cpufr
 	if (!cpufreq_driver->target_index)
 		return -EINVAL;
 
-	return __target_index(policy, policy->cached_resolved_idx);
+	return __target_index(policy, idx);
 }
 EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
 
@@ -2712,7 +2717,7 @@ static int cpufreq_set_policy(struct cpu
 	 * concurrently by cpufreq_driver_resolve_freq() during the update.
 	 */
 	freq = __resolve_freq(policy, new_data.max, new_data.min, new_data.max,
-			      CPUFREQ_RELATION_H);
+			      CPUFREQ_RELATION_H, NULL);
 	if (freq != policy->max) {
 		WRITE_ONCE(policy->max, freq);
 		if (need_update_limits)
@@ -2720,7 +2725,7 @@ static int cpufreq_set_policy(struct cpu
 	}
 
 	freq = __resolve_freq(policy, new_data.min, new_data.min, new_data.max,
-			      CPUFREQ_RELATION_L);
+			      CPUFREQ_RELATION_L, NULL);
 	freq = min(freq, policy->max);
 	if (freq != policy->min) {
 		WRITE_ONCE(policy->min, freq);
@@ -2732,8 +2737,6 @@ static int cpufreq_set_policy(struct cpu
 
 	cpufreq_update_pressure(policy);
 
-	policy->cached_target_freq = UINT_MAX;
-
 	pr_debug("new min and max freqs are %u - %u kHz\n",
 		 policy->min, policy->max);
 
@@ -2759,6 +2762,12 @@ static int cpufreq_set_policy(struct cpu
 		cpufreq_exit_governor(policy);
 	}
 
+	/*
+	 * Initialize cached_target_freq while no governor is running to avoid
+	 * racing with cpufreq_driver_resolve_freq(), which updates it.
+	 */
+	policy->cached_target_freq = UINT_MAX;
+
 	/* start new governor */
 	policy->governor = new_gov;
 	ret = cpufreq_init_governor(policy);




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

* Re: [PATCH v1 0/2] cpufreq: Fix issues related to cached resolved frequency and index
  2026-11-06 14:59 [PATCH v1 0/2] cpufreq: Fix issues related to cached resolved frequency and index Rafael J. Wysocki
  2026-11-06 15:04 ` [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq() Rafael J. Wysocki
  2026-11-06 15:05 ` [PATCH v1 2/2] cpufreq: Fix updates of cached resolved frequency and index Rafael J. Wysocki
@ 2026-11-06 17:02 ` Christian Loehle
  2 siblings, 0 replies; 6+ messages in thread
From: Christian Loehle @ 2026-11-06 17:02 UTC (permalink / raw)
  To: Rafael J. Wysocki, Linux PM
  Cc: Viresh Kumar, Zhongqiu Han, Linux ACPI, Vincent Donnefort

On 11/6/26 14:59, Rafael J. Wysocki wrote:
> Hi All,
> 
> This series addresses two issues related to the caching of the
> resolved frequency and index in __resolve_freq().
> 
> The first one is that the cached resolved frequency value should be
> the post-resolution one, not the pre-resolution one (which is the
> case now).  Patch [1/2] fixes this.
> 
> The other issue is that __resolve_freq() is now called in multiple
> places, some of which may race with the cached frequency and index
> updates in the schedutil governor path and that may cause the cached
> values to become invalid.  This is fixed by patch [2/2].
> 
> Please see the individual patch changelogs for details.
> 
> Thanks!
> 
> 
> 

Tested-by: Christian Loehle <christian.loehle@arm.com>

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

* Re: [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq()
  2026-11-06 15:04 ` [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq() Rafael J. Wysocki
@ 2026-11-06 17:02   ` Christian Loehle
  0 siblings, 0 replies; 6+ messages in thread
From: Christian Loehle @ 2026-11-06 17:02 UTC (permalink / raw)
  To: Rafael J. Wysocki, Linux PM
  Cc: Viresh Kumar, Zhongqiu Han, Linux ACPI, Vincent Donnefort

On 11/6/26 15:04, Rafael J. Wysocki wrote:
> From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
> 
> get_next_freq() in the schedutil governor returns the resolved
> frequency from cpufreq_driver_resolve_freq(), and that resolved value
> is then passed to cpufreq_driver_fast_switch().  The only reader of
> cached_target_freq, acpi_cpufreq_fast_switch(), compares it with the
> post-resolution target frequency, but it holds the pre-resolution
> clamped frequency, so any request that falls between table entries
> misses the cache and falls back to a table scan.
> 
> Address this by making __resolve_freq() store the post-resolution
> frequency in cached_target_freq.
> 
> Fixes: e3c062360870 ("cpufreq: add cpufreq_driver_resolve_freq()")
> Cc: All applicable <stable@vger.kernel.org>
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Reviewed-by: Christian Loehle <christian.loehle@arm.com>

> ---
>  drivers/cpufreq/cpufreq.c |    4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -488,8 +488,8 @@ static unsigned int __resolve_freq(struc
>  
>  	idx = cpufreq_frequency_table_target(policy, target_freq, min, max, relation);
>  	policy->cached_resolved_idx = idx;
> -	policy->cached_target_freq = target_freq;
> -	return policy->freq_table[idx].frequency;
> +	policy->cached_target_freq = policy->freq_table[idx].frequency;
> +	return policy->cached_target_freq;
>  }
>  
>  /**
> 
> 
> 


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

* Re: [PATCH v1 2/2] cpufreq: Fix updates of cached resolved frequency and index
  2026-11-06 15:05 ` [PATCH v1 2/2] cpufreq: Fix updates of cached resolved frequency and index Rafael J. Wysocki
@ 2026-11-06 17:32   ` Christian Loehle
  0 siblings, 0 replies; 6+ messages in thread
From: Christian Loehle @ 2026-11-06 17:32 UTC (permalink / raw)
  To: Rafael J. Wysocki, Linux PM
  Cc: Viresh Kumar, Zhongqiu Han, Linux ACPI, Vincent Donnefort

On 11/6/26 15:05, Rafael J. Wysocki wrote:
> From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
> 
> The cached resolved frequency and index, cached_target_freq and
> cached_resolved_idx, respectively, may be updated concurrently if
> cpufreq_driver_resolve_freq() is called by the schedutil governor
> while cpufreq_set_policy() is running for the same policy.  Those
> concurrent updates are not synchronized in any way and the values
> written by cpufreq_set_policy(), if it wins the race, may confuse
> drivers using those policy fields [1].
> 
> To address this, use the observation that the only place where
> cached_target_freq and cached_resolved_idx need to be updated is
> cpufreq_driver_resolve_freq() because the only legitimate users of
> them are .fast_switch() driver callbacks.  Although cached_resolved_idx
> is also used in __cpufreq_driver_target(), that is in fact unnecessary
> and it is better to use a local variable for storing the frequency
> table index in that function.
> 
> Accordingly, rearrange __resolve_freq() and its callers so that
> cached_target_freq and cached_resolved_idx are only updated in
> cpufreq_driver_resolve_freq().
> 
> The cached_target_freq initialization in cpufreq_set_policy() is still
> needed, but move it after stopping the old governor and before starting
> the new one to avoid racing with cpufreq_driver_resolve_freq().
> 
> Fixes: f9ccdec24d91 ("cpufreq: Reuse cpufreq_driver_resolve_freq() in __cpufreq_driver_target()")
> Fixes: 151717690694 ("cpufreq: Make policy min/max hard requirements")
> Reported-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> Closes: https://lore.kernel.org/linux-pm/20261105140357.1331894-1-zhongqiu.han@oss.qualcomm.com/ [1]
> Co-developed-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> Signed-off-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> Cc: All applicable <stable@vger.kernel.org>
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Reviewed-by: Christian Loehle <christian.loehle@arm.com>

> ---
>  drivers/cpufreq/cpufreq.c |   31 ++++++++++++++++++++-----------
>  1 file changed, 20 insertions(+), 11 deletions(-)
> 
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -477,7 +477,7 @@ EXPORT_SYMBOL_GPL(cpufreq_disable_fast_s
>  static unsigned int __resolve_freq(struct cpufreq_policy *policy,
>  				   unsigned int target_freq,
>  				   unsigned int min, unsigned int max,
> -				   unsigned int relation)
> +				   unsigned int relation, unsigned int *idx_p)
>  {
>  	unsigned int idx;
>  
> @@ -487,9 +487,10 @@ static unsigned int __resolve_freq(struc
>  		return target_freq;
>  
>  	idx = cpufreq_frequency_table_target(policy, target_freq, min, max, relation);
> -	policy->cached_resolved_idx = idx;
> -	policy->cached_target_freq = policy->freq_table[idx].frequency;
> -	return policy->cached_target_freq;
> +	if (idx_p)
> +		*idx_p = idx;
> +
> +	return policy->freq_table[idx].frequency;
>  }
>  
>  /**
> @@ -520,7 +521,10 @@ unsigned int cpufreq_driver_resolve_freq
>  	if (unlikely(min > max))
>  		min = max;
>  
> -	return __resolve_freq(policy, target_freq, min, max, CPUFREQ_RELATION_LE);
> +	policy->cached_target_freq = __resolve_freq(policy, target_freq, min, max,
> +						    CPUFREQ_RELATION_LE,
> +						    &policy->cached_resolved_idx);
> +	return policy->cached_target_freq;
>  }
>  EXPORT_SYMBOL_GPL(cpufreq_driver_resolve_freq);
>  
> @@ -2398,12 +2402,13 @@ int __cpufreq_driver_target(struct cpufr
>  			    unsigned int relation)
>  {
>  	unsigned int old_target_freq = target_freq;
> +	unsigned int idx = 0;
>  
>  	if (cpufreq_disabled())
>  		return -ENODEV;
>  
>  	target_freq = __resolve_freq(policy, target_freq, policy->min,
> -				     policy->max, relation);
> +				     policy->max, relation, &idx);
>  
>  	pr_debug("CPU %u: cur %u kHz -> target %u kHz (req %u kHz, rel %u)\n",
>  		policy->cpu, policy->cur, target_freq, old_target_freq, relation);
> @@ -2433,7 +2438,7 @@ int __cpufreq_driver_target(struct cpufr
>  	if (!cpufreq_driver->target_index)
>  		return -EINVAL;
>  
> -	return __target_index(policy, policy->cached_resolved_idx);
> +	return __target_index(policy, idx);
>  }
>  EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
>  
> @@ -2712,7 +2717,7 @@ static int cpufreq_set_policy(struct cpu
>  	 * concurrently by cpufreq_driver_resolve_freq() during the update.
>  	 */
>  	freq = __resolve_freq(policy, new_data.max, new_data.min, new_data.max,
> -			      CPUFREQ_RELATION_H);
> +			      CPUFREQ_RELATION_H, NULL);
>  	if (freq != policy->max) {
>  		WRITE_ONCE(policy->max, freq);
>  		if (need_update_limits)
> @@ -2720,7 +2725,7 @@ static int cpufreq_set_policy(struct cpu
>  	}
>  
>  	freq = __resolve_freq(policy, new_data.min, new_data.min, new_data.max,
> -			      CPUFREQ_RELATION_L);
> +			      CPUFREQ_RELATION_L, NULL);
>  	freq = min(freq, policy->max);
>  	if (freq != policy->min) {
>  		WRITE_ONCE(policy->min, freq);
> @@ -2732,8 +2737,6 @@ static int cpufreq_set_policy(struct cpu
>  
>  	cpufreq_update_pressure(policy);
>  
> -	policy->cached_target_freq = UINT_MAX;
> -
>  	pr_debug("new min and max freqs are %u - %u kHz\n",
>  		 policy->min, policy->max);
>  
> @@ -2759,6 +2762,12 @@ static int cpufreq_set_policy(struct cpu
>  		cpufreq_exit_governor(policy);
>  	}
>  
> +	/*
> +	 * Initialize cached_target_freq while no governor is running to avoid
> +	 * racing with cpufreq_driver_resolve_freq(), which updates it.
> +	 */
> +	policy->cached_target_freq = UINT_MAX;
> +
>  	/* start new governor */
>  	policy->governor = new_gov;
>  	ret = cpufreq_init_governor(policy);
> 
> 
> 


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

end of thread, other threads:[~2026-11-06 17:32 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-06 14:59 [PATCH v1 0/2] cpufreq: Fix issues related to cached resolved frequency and index Rafael J. Wysocki
2026-11-06 15:04 ` [PATCH v1 1/2] cpufreq: Cache post-resolution target frequency in __resolve_freq() Rafael J. Wysocki
2026-11-06 17:02   ` Christian Loehle
2026-11-06 15:05 ` [PATCH v1 2/2] cpufreq: Fix updates of cached resolved frequency and index Rafael J. Wysocki
2026-11-06 17:32   ` Christian Loehle
2026-11-06 17:02 ` [PATCH v1 0/2] cpufreq: Fix issues related to " Christian Loehle

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