Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH v2 06/19] cpufreq: Use trace_call__##name() at guarded tracepoint call sites
From: Gautham R. Shenoy @ 2026-03-27  9:10 UTC (permalink / raw)
  To: Vineeth Pillai (Google)
  Cc: Steven Rostedt, Peter Zijlstra, Huang Rui, Mario Limonciello,
	Perry Yuan, Rafael J. Wysocki, Viresh Kumar, Srinivas Pandruvada,
	Len Brown, linux-pm, linux-kernel, linux-trace-kernel
In-Reply-To: <20260323160052.17528-7-vineeth@bitbyteword.org>

Hello Vineeth,

On Mon, Mar 23, 2026 at 12:00:25PM -0400, Vineeth Pillai (Google) wrote:
> Replace trace_foo() with the new trace_call__foo() at sites already
> guarded by trace_foo_enabled(), avoiding a redundant
> static_branch_unlikely() re-evaluation inside the tracepoint.
> trace_call__foo() calls the tracepoint callbacks directly without
> utilizing the static branch again.
> 
> Suggested-by: Steven Rostedt <rostedt@goodmis.org>
> Suggested-by: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Vineeth Pillai (Google) <vineeth@bitbyteword.org>
> Assisted-by: Claude:claude-sonnet-4-6


For drivers/cpufreq/amd-pstate.c and drivers/cpufreq/cpufreq.c

Reviewed-by: Gautham R. Shenoy <gautham.shenoy@amd.com>

-- 
Thanks and Regards
gautham.

> ---
>  drivers/cpufreq/amd-pstate.c   | 10 +++++-----
>  drivers/cpufreq/cpufreq.c      |  2 +-
>  drivers/cpufreq/intel_pstate.c |  2 +-
>  3 files changed, 7 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 5aa9fcd80cf51..4c47324aa2f73 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -247,7 +247,7 @@ static int msr_update_perf(struct cpufreq_policy *policy, u8 min_perf,
>  	if (trace_amd_pstate_epp_perf_enabled()) {
>  		union perf_cached perf = READ_ONCE(cpudata->perf);
>  
> -		trace_amd_pstate_epp_perf(cpudata->cpu,
> +		trace_call__amd_pstate_epp_perf(cpudata->cpu,
>  					  perf.highest_perf,
>  					  epp,
>  					  min_perf,
> @@ -298,7 +298,7 @@ static int msr_set_epp(struct cpufreq_policy *policy, u8 epp)
>  	if (trace_amd_pstate_epp_perf_enabled()) {
>  		union perf_cached perf = cpudata->perf;
>  
> -		trace_amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
> +		trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
>  					  epp,
>  					  FIELD_GET(AMD_CPPC_MIN_PERF_MASK,
>  						    cpudata->cppc_req_cached),
> @@ -343,7 +343,7 @@ static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp)
>  	if (trace_amd_pstate_epp_perf_enabled()) {
>  		union perf_cached perf = cpudata->perf;
>  
> -		trace_amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
> +		trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
>  					  epp,
>  					  FIELD_GET(AMD_CPPC_MIN_PERF_MASK,
>  						    cpudata->cppc_req_cached),
> @@ -507,7 +507,7 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
>  	if (trace_amd_pstate_epp_perf_enabled()) {
>  		union perf_cached perf = READ_ONCE(cpudata->perf);
>  
> -		trace_amd_pstate_epp_perf(cpudata->cpu,
> +		trace_call__amd_pstate_epp_perf(cpudata->cpu,
>  					  perf.highest_perf,
>  					  epp,
>  					  min_perf,
> @@ -588,7 +588,7 @@ static void amd_pstate_update(struct amd_cpudata *cpudata, u8 min_perf,
>  	}
>  
>  	if (trace_amd_pstate_perf_enabled() && amd_pstate_sample(cpudata)) {
> -		trace_amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq,
> +		trace_call__amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq,
>  			cpudata->cur.mperf, cpudata->cur.aperf, cpudata->cur.tsc,
>  				cpudata->cpu, fast_switch);
>  	}
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 277884d91913c..58901047eae5a 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -2222,7 +2222,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy,
>  
>  	if (trace_cpu_frequency_enabled()) {
>  		for_each_cpu(cpu, policy->cpus)
> -			trace_cpu_frequency(freq, cpu);
> +			trace_call__cpu_frequency(freq, cpu);
>  	}
>  
>  	return freq;
> diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
> index 11c58af419006..70be952209144 100644
> --- a/drivers/cpufreq/intel_pstate.c
> +++ b/drivers/cpufreq/intel_pstate.c
> @@ -3132,7 +3132,7 @@ static void intel_cpufreq_trace(struct cpudata *cpu, unsigned int trace_type, in
>  		return;
>  
>  	sample = &cpu->sample;
> -	trace_pstate_sample(trace_type,
> +	trace_call__pstate_sample(trace_type,
>  		0,
>  		old_pstate,
>  		cpu->pstate.current_pstate,
> -- 
> 2.53.0
> 

^ permalink raw reply

* [PATCH v2] thermal: fix error condition for reading st,thermal-flags
From: Gopi Krishna Menon @ 2026-03-27  9:05 UTC (permalink / raw)
  To: rafael, daniel.lezcano, rui.zhang, lukasz.luba
  Cc: Gopi Krishna Menon, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry, linux-pm, linux-kernel

of_property_read_u32 returns 0 on success. The current check returns
-EINVAL if the property is read successfully.

Fix the check by removing ! from of_property_read_u32

Fixes: b9c7aff481f1 ("drivers/thermal/spear_thermal.c: add Device Tree probing capability")
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Suggested-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
---
Changes since v1:
- Add Fixes and Reviewed-by tags

Note:
* This patch is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings
* Changes have only been compile tested

 drivers/thermal/spear_thermal.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c
index 603dadcd3df5..5e3e9c1f32f8 100644
--- a/drivers/thermal/spear_thermal.c
+++ b/drivers/thermal/spear_thermal.c
@@ -93,7 +93,7 @@ static int spear_thermal_probe(struct platform_device *pdev)
 	struct device_node *np = pdev->dev.of_node;
 	int ret = 0, val;
 
-	if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
+	if (!np || of_property_read_u32(np, "st,thermal-flags", &val)) {
 		dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");
 		return -EINVAL;
 	}
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v5 5/5] cpufreq/amd-pstate-ut: Add a unit test for raw EPP
From: Gautham R. Shenoy @ 2026-03-27  8:59 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK, Mario Limonciello
In-Reply-To: <20260106051441.60093-6-superm1@kernel.org>

Hello Mario,

On Mon, Jan 05, 2026 at 11:14:41PM -0600, Mario Limonciello wrote:
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> Ensure that all supported raw EPP values work properly.
> 
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> ---
>  drivers/cpufreq/amd-pstate-ut.c | 58 +++++++++++++++++++++++++++++++++
>  1 file changed, 58 insertions(+)
> 
> diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
> index 447b9aa5ce40b..d51233b753db6 100644
> --- a/drivers/cpufreq/amd-pstate-ut.c
> +++ b/drivers/cpufreq/amd-pstate-ut.c
> @@ -26,6 +26,7 @@
>  #include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
> +#include <linux/mm.h>
>  #include <linux/fs.h>
>  #include <linux/cleanup.h>
>  
> @@ -35,6 +36,7 @@
>  
>  #include "amd-pstate.h"
>  
> +DEFINE_FREE(cleanup_page, void *, if (_T) free_page((unsigned long)_T))
>  
>  struct amd_pstate_ut_struct {
>  	const char *name;
> @@ -48,6 +50,7 @@ static int amd_pstate_ut_acpi_cpc_valid(u32 index);
>  static int amd_pstate_ut_check_enabled(u32 index);
>  static int amd_pstate_ut_check_perf(u32 index);
>  static int amd_pstate_ut_check_freq(u32 index);
> +static int amd_pstate_ut_epp(u32 index);
>  static int amd_pstate_ut_check_driver(u32 index);
>  
>  static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
> @@ -55,6 +58,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
>  	{"amd_pstate_ut_check_enabled",    amd_pstate_ut_check_enabled    },
>  	{"amd_pstate_ut_check_perf",       amd_pstate_ut_check_perf       },
>  	{"amd_pstate_ut_check_freq",       amd_pstate_ut_check_freq       },
> +	{"amd_pstate_ut_epp",              amd_pstate_ut_epp              },
>  	{"amd_pstate_ut_check_driver",	   amd_pstate_ut_check_driver     }
>  };
>  
> @@ -241,6 +245,60 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
>  	return amd_pstate_update_status(mode_str, strlen(mode_str));
>  }
>  
> +static int amd_pstate_ut_epp(u32 index)
> +{
> +	struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
> +	void *buf __free(cleanup_page) = NULL;
> +	struct amd_cpudata *cpudata;
> +	int ret, cpu = 0;
> +	u16 epp;
> +
> +	policy = cpufreq_cpu_get(cpu);
> +	if (!policy)
> +		return -ENODEV;
> +
> +	cpudata = policy->driver_data;
> +
> +	/* disable dynamic EPP before running test */
> +	if (cpudata->dynamic_epp) {
> +		pr_debug("Dynamic EPP is enabled, disabling it\n");
> +		amd_pstate_clear_dynamic_epp(policy);
> +	}
> +
> +	buf = (void *)__get_free_page(GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ret = amd_pstate_set_mode(AMD_PSTATE_ACTIVE);
> +	if (ret)
> +		return ret;
> +
> +	for (epp = 0; epp <= U8_MAX; epp++) {
> +		u8 val;
> +
> +		/* write all EPP values */
> +		memset(buf, 0, sizeof(*buf));
> +		snprintf(buf, PAGE_SIZE, "%d", epp);
> +		ret = store_energy_performance_preference(policy, buf, sizeof(*buf));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* check if the EPP value reads back correctly for raw numbers */
> +		memset(buf, 0, sizeof(*buf));
> +		ret = show_energy_performance_preference(policy, buf);
> +		if (ret < 0)
> +			return ret;
> +		strreplace(buf, '\n', '\0');
> +		ret = kstrtou8(buf, 0, &val);
> +		if (!ret && epp != val) {
> +			pr_err("Raw EPP value mismatch: %d != %d\n", epp, val);
> +			return -EINVAL;
> +		}

The loop validates that writing a raw integer reads back as an
integer, but the commit message for Patch 4 ("Add support for raw
EPP writes") promises:

    "If the last value written was an integer then an integer will
     be returned.  If the last value written was a string then a
     string will be returned."

The string-to-string direction is not tested.  After the raw integer
loop, raw_epp is left as true.  Writing "performance" should flip
raw_epp to false and reading back should return the string
"performance", not the integer "0".  The same applies to
"balance_performance", "balance_power", and "power".

This would also exercise the transition from raw_epp=true to
raw_epp=false, which is where a regression would most likely appear.

> +	}

Also, the test forces AMD_PSTATE_ACTIVE mode here but never saves or
restores the original mode.  It also disables dynamic EPP above
without re-enabling it.

Shouldn't amd_pstate_ut_epp() save the mode and dynamic_epp state at
the start and restore both before returning like the other tests, for
e.g., amd_pstate_ut_check_driver() do?

> +
> +	return 0;
> +}
> +
>  static int amd_pstate_ut_check_driver(u32 index)
>  {
>  	enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
> 
> -- 
> 2.43.0
> 

-- 
Thanks and Regards
gautham.

^ permalink raw reply

* Re: [PATCH v5 4/5] cpufreq/amd-pstate: Add support for raw EPP writes
From: Gautham R. Shenoy @ 2026-03-27  8:41 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK, Mario Limonciello
In-Reply-To: <20260106051441.60093-5-superm1@kernel.org>

On Mon, Jan 05, 2026 at 11:14:40PM -0600, Mario Limonciello wrote:
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> The energy performance preference field of the CPPC request MSR
> supports values from 0 to 255, but the strings only offer 4 values.
> 
> The other values are useful for tuning the performance of some
> workloads.
> 
> Add support for writing the raw energy performance preference value
> to the sysfs file.  If the last value written was an integer then
> an integer will be returned.  If the last value written was a string
> then a string will be returned.


Support for raw EPP values was present in the previous patch
itself. It appears that some hunks from the previous patch belong
here.


> 
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> ---
>  Documentation/admin-guide/pm/amd-pstate.rst | 16 +++++++++++-----
>  drivers/cpufreq/amd-pstate.c                | 11 +++++++++--
>  drivers/cpufreq/amd-pstate.h                |  1 +
>  3 files changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
> index a6745f2358e61..674e76ff9293e 100644
> --- a/Documentation/admin-guide/pm/amd-pstate.rst
> +++ b/Documentation/admin-guide/pm/amd-pstate.rst
> @@ -280,16 +280,22 @@ A list of all the supported EPP preferences that could be used for
>  These profiles represent different hints that are provided
>  to the low-level firmware about the user's desired energy vs efficiency
>  tradeoff.  ``default`` represents the epp value is set by platform
> -firmware. This attribute is read-only.
> +firmware. ``custom`` designates that integer values 0-255 may be written
> +as well.  This attribute is read-only.
>  
>  ``energy_performance_preference``
>  
>  The current energy performance preference can be read from this attribute.
>  and user can change current preference according to energy or performance needs
> -Please get all support profiles list from
> -``energy_performance_available_preferences`` attribute, all the profiles are
> -integer values defined between 0 to 255 when EPP feature is enabled by platform
> -firmware, but if the dynamic EPP feature is enabled, driver will block writes.
> +Coarse named profiles are available in the attribute
> +``energy_performance_available_preferences``.
> +Users can also write individual integer values between 0 to 255.
> +When EPP feature is enabled by platform firmware but if the dynamic EPP feature is
> +enabled, driver will ignore the written value. Lower epp values shift the bias
> +towards improved performance while a higher epp value shifts the bias towards
> +power-savings. The exact impact can change from one platform to the other.
> +If a valid integer was last written, then a number will be returned on future reads.
> +If a valid string was last written then a string will be returned on future reads.
>  This attribute is read-write.
>  
>  ``boost``
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index e1ccbbdd56d42..14347baf3cefb 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -1333,6 +1333,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
>  {
>  	struct amd_cpudata *cpudata = policy->driver_data;
>  	ssize_t ret;
> +	bool raw_epp = FALSE;

s/FALSE/false ?

false is already defined in <linux/types.h> and rest of the file uses
it.



>  	u8 epp;
>  
>  	if (cpudata->dynamic_epp) {
> @@ -1345,6 +1346,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
>  	 * matches an index in the energy_perf_strings array
>  	 */
>  	ret = kstrtou8(buf, 0, &epp);
> +	raw_epp = !ret;
>  	if (ret) {
>  		ret = sysfs_match_string(energy_perf_strings, buf);
>  		if (ret < 0 || ret == EPP_INDEX_CUSTOM)
> @@ -1364,7 +1366,9 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
>  	if (ret)
>  		return ret;
>  
> -	return ret ? ret : count;
> +	cpudata->raw_epp = raw_epp;
> +
> +	return count;
>  }
>  EXPORT_SYMBOL_GPL(store_energy_performance_preference);
>  
> @@ -1375,6 +1379,9 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
>  
>  	epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
>  
> +	if (cpudata->raw_epp)
> +		return sysfs_emit(buf, "%u\n", epp);
> +
>  	switch (epp) {
>  	case AMD_CPPC_EPP_PERFORMANCE:
>  		preference = EPP_INDEX_PERFORMANCE;
> @@ -1389,7 +1396,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
>  		preference = EPP_INDEX_POWERSAVE;
>  		break;
>  	default:
> -		return sysfs_emit(buf, "%u\n", epp);
> +		return -EINVAL;
>  	}
>  
>  	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
> diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
> index 9839c7c6558f4..38b575b8ad959 100644
> --- a/drivers/cpufreq/amd-pstate.h
> +++ b/drivers/cpufreq/amd-pstate.h
> @@ -110,6 +110,7 @@ struct amd_cpudata {
>  	u8	epp_default_ac;
>  	u8	epp_default_dc;
>  	bool	dynamic_epp;
> +	bool	raw_epp;
>  	struct notifier_block power_nb;

Looks good otherwise.

-- 
Thanks and Regards
gautham.

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
From: Krzysztof Kozlowski @ 2026-03-27  8:06 UTC (permalink / raw)
  To: Gopi Krishna Menon
  Cc: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <20260324-belligerent-armadillo-of-camouflage-e52f7b@quoll>

On 24/03/2026 10:30, Krzysztof Kozlowski wrote:
> On Mon, Mar 23, 2026 at 07:08:08PM +0530, Gopi Krishna Menon wrote:
>> +properties:
>> +  compatible:
>> +    const: st,thermal-spear1340
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  st,thermal-flags:
>> +    description: flags used to enable thermal sensor
>> +    $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - st,thermal-flags
>> +
>> +unevaluatedProperties: false
> 
> additionalProperties: true

Thanks for pinging me on IRC about this - it should be:

additionalProperties: false

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v5 3/5] cpufreq/amd-pstate: Add support for platform profile class
From: Gautham R. Shenoy @ 2026-03-27  6:58 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK, Mario Limonciello
In-Reply-To: <20260106051441.60093-4-superm1@kernel.org>

On Mon, Jan 05, 2026 at 11:14:39PM -0600, Mario Limonciello wrote:
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> The platform profile core allows multiple drivers and devices to
> register platform profile support.
> 
> When the legacy platform profile interface is used all drivers will
> adjust the platform profile as well.
> 
> Add support for registering every CPU with the platform profile handler
> when dynamic EPP is enabled.
> 
> The end result will be that changing the platform profile will modify
> EPP accordingly.

The commit message only describes the platform profile integration.

However, the patch also does the following other things

 * introduces raw integer EPP parsing via in store_energy_performance_preference()
 
 * adds EPP_INDEX_CUSTOM
 
 * exports store_energy_performance_preference(),
   show_energy_performance_preference(), and
   amd_pstate_clear_dynamic_epp()

 * changes the performance policy check from policy->policy to
   cpudata->policy.


These are separate functional changes that aren't mentioned in the
changelog.

Or perhaps split the suport for raw epp as a separate patch, and the
policy check from policy->policy to use cpudata->policy as a separate
check.

-- 
Thanks and Regards
gautham.

> 
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> ---
>  Documentation/admin-guide/pm/amd-pstate.rst |   4 +-
>  drivers/cpufreq/Kconfig.x86                 |   1 +
>  drivers/cpufreq/amd-pstate.c                | 144 +++++++++++++++++---
>  drivers/cpufreq/amd-pstate.h                |  10 ++
>  4 files changed, 141 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
> index df4607b6a5f62..a6745f2358e61 100644
> --- a/Documentation/admin-guide/pm/amd-pstate.rst
> +++ b/Documentation/admin-guide/pm/amd-pstate.rst
> @@ -321,7 +321,9 @@ Whether this behavior is enabled by default with the kernel config option
>  at runtime by the sysfs file ``/sys/devices/system/cpu/cpufreq/policyX/dynamic_epp``.
>  
>  When set to enabled, the driver will select a different energy performance
> -profile when the machine is running on battery or AC power.
> +profile when the machine is running on battery or AC power. The driver will
> +also register with the platform profile handler to receive notifications of
> +user desired power state and react to those.
>  When set to disabled, the driver will not change the energy performance profile
>  based on the power source and will not react to user desired power state.
>  
> diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
> index cdaa8d858045a..a0dbb9808ae99 100644
> --- a/drivers/cpufreq/Kconfig.x86
> +++ b/drivers/cpufreq/Kconfig.x86
> @@ -40,6 +40,7 @@ config X86_AMD_PSTATE
>  	select ACPI_PROCESSOR
>  	select ACPI_CPPC_LIB if X86_64
>  	select CPU_FREQ_GOV_SCHEDUTIL if SMP
> +	select ACPI_PLATFORM_PROFILE
>  	help
>  	  This driver adds a CPUFreq driver which utilizes a fine grain
>  	  processor performance frequency control range instead of legacy
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 7dd50b5825d78..e1ccbbdd56d42 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -109,6 +109,7 @@ static struct quirk_entry *quirks;
>   *	2		balance_performance
>   *	3		balance_power
>   *	4		power
> + *	5		custom (for raw EPP values)
>   */
>  enum energy_perf_value_index {
>  	EPP_INDEX_DEFAULT = 0,
> @@ -116,6 +117,7 @@ enum energy_perf_value_index {
>  	EPP_INDEX_BALANCE_PERFORMANCE,
>  	EPP_INDEX_BALANCE_POWERSAVE,
>  	EPP_INDEX_POWERSAVE,
> +	EPP_INDEX_CUSTOM,
>  	EPP_INDEX_MAX,
>  };
>  
> @@ -125,6 +127,7 @@ static const char * const energy_perf_strings[] = {
>  	[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
>  	[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
>  	[EPP_INDEX_POWERSAVE] = "power",
> +	[EPP_INDEX_CUSTOM] = "custom",
>  };
>  static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
>  
> @@ -135,7 +138,7 @@ static unsigned int epp_values[] = {
>  	[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
>  	[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
>  };
> -static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX);
> +static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1);
>  
>  typedef int (*cppc_mode_transition_fn)(int);
>  
> @@ -1094,6 +1097,10 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
>  	if (event != PSY_EVENT_PROP_CHANGED)
>  		return NOTIFY_OK;
>  
> +	/* dynamic actions are only applied while platform profile is in balanced */
> +	if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED)
> +		return 0;
> +
>  	epp = amd_pstate_get_balanced_epp(policy);
>  
>  	ret = amd_pstate_set_epp(policy, epp);
> @@ -1102,14 +1109,84 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
>  
>  	return NOTIFY_OK;
>  }
> -static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
> +
> +static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices)
> +{
> +	set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
> +
> +	return 0;
> +}
> +
> +static int amd_pstate_profile_get(struct device *dev,
> +				  enum platform_profile_option *profile)
> +{
> +	struct amd_cpudata *cpudata = dev_get_drvdata(dev);
> +
> +	*profile = cpudata->current_profile;
> +
> +	return 0;
> +}
> +
> +static int amd_pstate_profile_set(struct device *dev,
> +				  enum platform_profile_option profile)
> +{
> +	struct amd_cpudata *cpudata = dev_get_drvdata(dev);
> +	struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu);
> +	int ret;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		if (cpudata->policy != CPUFREQ_POLICY_POWERSAVE)
> +			cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
> +		ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE);
> +		if (ret)
> +			return ret;
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		if (cpudata->policy != CPUFREQ_POLICY_POWERSAVE)
> +			cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
> +		ret = amd_pstate_set_epp(policy,
> +					 amd_pstate_get_balanced_epp(policy));
> +		if (ret)
> +			return ret;
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE);
> +		if (ret)
> +			return ret;
> +		break;
> +	default:
> +		pr_err("Unknown Platform Profile %d\n", profile);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	cpudata->current_profile = profile;
> +
> +	return 0;
> +}
> +
> +static const struct platform_profile_ops amd_pstate_profile_ops = {
> +	.probe = amd_pstate_profile_probe,
> +	.profile_set = amd_pstate_profile_set,
> +	.profile_get = amd_pstate_profile_get,
> +};
> +
> +void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
>  {
>  	struct amd_cpudata *cpudata = policy->driver_data;
>  
>  	if (cpudata->power_nb.notifier_call)
>  		power_supply_unreg_notifier(&cpudata->power_nb);
> +	if (cpudata->ppdev) {
> +		platform_profile_remove(cpudata->ppdev);
> +		cpudata->ppdev = NULL;
> +	}
> +	kfree(cpudata->profile_name);
>  	cpudata->dynamic_epp = false;
>  }
> +EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp);
>  
>  static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
>  {
> @@ -1117,11 +1194,35 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
>  	int ret;
>  	u8 epp;
>  
> -	epp = amd_pstate_get_balanced_epp(policy);
> +	switch (cpudata->current_profile) {
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		epp = AMD_CPPC_EPP_PERFORMANCE;
> +		break;
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		epp = AMD_CPPC_EPP_POWERSAVE;
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		epp = amd_pstate_get_balanced_epp(policy);
> +		break;
> +	default:
> +		pr_err("Unknown Platform Profile %d\n", cpudata->current_profile);
> +		return -EOPNOTSUPP;
> +	}
>  	ret = amd_pstate_set_epp(policy, epp);
>  	if (ret)
>  		return ret;
>  
> +	cpudata->profile_name = kasprintf(GFP_KERNEL, "amd-pstate-epp-cpu%d", cpudata->cpu);
> +
> +	cpudata->ppdev = platform_profile_register(get_cpu_device(policy->cpu),
> +						   cpudata->profile_name,
> +						   policy->driver_data,
> +						   &amd_pstate_profile_ops);
> +	if (IS_ERR(cpudata->ppdev)) {
> +		ret = PTR_ERR(cpudata->ppdev);
> +		goto cleanup;
> +	}
> +
>  	/* only enable notifier if things will actually change */
>  	if (cpudata->epp_default_ac != cpudata->epp_default_dc) {
>  		ret = power_supply_reg_notifier(&cpudata->power_nb);
> @@ -1227,8 +1328,8 @@ static ssize_t show_energy_performance_available_preferences(
>  	return offset;
>  }
>  
> -static ssize_t store_energy_performance_preference(
> -		struct cpufreq_policy *policy, const char *buf, size_t count)
> +ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
> +					    const char *buf, size_t count)
>  {
>  	struct amd_cpudata *cpudata = policy->driver_data;
>  	ssize_t ret;
> @@ -1239,16 +1340,22 @@ static ssize_t store_energy_performance_preference(
>  		return -EBUSY;
>  	}
>  
> -	ret = sysfs_match_string(energy_perf_strings, buf);
> -	if (ret < 0)
> -		return -EINVAL;
> -
> -	if (ret)
> -		epp = epp_values[ret];
> -	else
> -		epp = amd_pstate_get_balanced_epp(policy);
> +	/*
> +	 * if the value matches a number, use that, otherwise see if
> +	 * matches an index in the energy_perf_strings array
> +	 */
> +	ret = kstrtou8(buf, 0, &epp);
> +	if (ret) {
> +		ret = sysfs_match_string(energy_perf_strings, buf);
> +		if (ret < 0 || ret == EPP_INDEX_CUSTOM)
> +			return -EINVAL;
> +		if (ret)
> +			epp = epp_values[ret];
> +		else
> +			epp = amd_pstate_get_balanced_epp(policy);
> +	}
>  
> -	if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
> +	if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
>  		pr_debug("EPP cannot be set under performance policy\n");
>  		return -EBUSY;
>  	}
> @@ -1259,9 +1366,9 @@ static ssize_t store_energy_performance_preference(
>  
>  	return ret ? ret : count;
>  }
> +EXPORT_SYMBOL_GPL(store_energy_performance_preference);
>  
> -static ssize_t show_energy_performance_preference(
> -				struct cpufreq_policy *policy, char *buf)
> +ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
>  {
>  	struct amd_cpudata *cpudata = policy->driver_data;
>  	u8 preference, epp;
> @@ -1282,11 +1389,12 @@ static ssize_t show_energy_performance_preference(
>  		preference = EPP_INDEX_POWERSAVE;
>  		break;
>  	default:
> -		return -EINVAL;
> +		return sysfs_emit(buf, "%u\n", epp);
>  	}
>  
>  	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
>  }
> +EXPORT_SYMBOL_GPL(show_energy_performance_preference);
>  
>  static void amd_pstate_driver_cleanup(void)
>  {
> @@ -1621,10 +1729,12 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
>  	    amd_pstate_acpi_pm_profile_undefined()) {
>  		policy->policy = CPUFREQ_POLICY_PERFORMANCE;
>  		cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata);
> +		cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE;
>  	} else {
>  		policy->policy = CPUFREQ_POLICY_POWERSAVE;
>  		cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
>  		cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
> +		cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
>  	}
>  
>  	if (dynamic_epp) {
> diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
> index 7bfe5f8115623..9839c7c6558f4 100644
> --- a/drivers/cpufreq/amd-pstate.h
> +++ b/drivers/cpufreq/amd-pstate.h
> @@ -9,6 +9,7 @@
>  #define _LINUX_AMD_PSTATE_H
>  
>  #include <linux/pm_qos.h>
> +#include <linux/platform_profile.h>
>  
>  /*********************************************************************
>   *                        AMD P-state INTERFACE                       *
> @@ -110,6 +111,11 @@ struct amd_cpudata {
>  	u8	epp_default_dc;
>  	bool	dynamic_epp;
>  	struct notifier_block power_nb;
> +
> +	/* platform profile */
> +	enum platform_profile_option current_profile;
> +	struct device *ppdev;
> +	char *profile_name;
>  };
>  
>  /*
> @@ -126,5 +132,9 @@ enum amd_pstate_mode {
>  const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode);
>  int amd_pstate_get_status(void);
>  int amd_pstate_update_status(const char *buf, size_t size);
> +ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
> +					    const char *buf, size_t count);
> +ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf);
> +void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy);
>  
>  #endif /* _LINUX_AMD_PSTATE_H */
> 
> -- 
> 2.43.0
> 

^ permalink raw reply

* Re: [PATCH v5 2/5] cpufreq/amd-pstate: add kernel command line to override dynamic epp
From: Gautham R. Shenoy @ 2026-03-27  6:45 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK, Mario Limonciello
In-Reply-To: <20260106051441.60093-3-superm1@kernel.org>

On Mon, Jan 05, 2026 at 11:14:38PM -0600, Mario Limonciello wrote:
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> Add `amd_dynamic_epp=enable` and `amd_dynamic_epp=disable` to override
> the kernel configuration option `CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP`
> locally.
> 
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
> ---
>  Documentation/admin-guide/kernel-parameters.txt |  7 +++++++
>  Documentation/admin-guide/pm/amd-pstate.rst     |  7 +++++++
>  drivers/cpufreq/amd-pstate.c                    | 11 +++++++++++
>  3 files changed, 25 insertions(+)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index a8d0afde7f85a..d7d547bbda430 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -490,6 +490,13 @@ Kernel parameters
>  			disable
>  			  Disable amd-pstate preferred core.
>  
> +	amd_dynamic_epp=
> +			[X86]
> +			disable
> +			  Disable amd-pstate dynamic EPP.
> +			enable
> +			  Enable amd-pstate dynamic EPP.
> +
>  	amijoy.map=	[HW,JOY] Amiga joystick support
>  			Map of devices attached to JOY0DAT and JOY1DAT
>  			Format: <a>,<b>
> diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
> index 5aa9fb77a078c..df4607b6a5f62 100644
> --- a/Documentation/admin-guide/pm/amd-pstate.rst
> +++ b/Documentation/admin-guide/pm/amd-pstate.rst
> @@ -438,6 +438,13 @@ For systems that support ``amd-pstate`` preferred core, the core rankings will
>  always be advertised by the platform. But OS can choose to ignore that via the
>  kernel parameter ``amd_prefcore=disable``.
>  
> +``amd_dynamic_epp``
> +
> +When AMD pstate is in auto mode, dynamic EPP will control whether the kernel
> +autonomously changes the EPP mode. The default is configured by
> +``CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP`` but can be explicitly enabled with
> +``amd_pstate_epp=enable`` or disabled with ``amd_pstate_epp=disable``.

Do you mean amd_dynamic_epp instead of amd_pstate_epp here ?

The code and kernel-parameters.txt both use
amd_dynamic_epp, not amd_pstate_epp:

drivers/cpufreq/amd-pstate.c:
    early_param("amd_dynamic_epp", amd_dynamic_epp_param);

Documentation/admin-guide/kernel-parameters.txt:
    amd_dynamic_epp=
    
> +
>  User Space Interface in ``sysfs`` - General
>  ===========================================
>  
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 26a8552581c57..7dd50b5825d78 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -1992,8 +1992,19 @@ static int __init amd_prefcore_param(char *str)
>  	return 0;
>  }
>  
> +static int __init amd_dynamic_epp_param(char *str)
> +{
> +	if (!strcmp(str, "disable"))
> +		dynamic_epp = false;
> +	if (!strcmp(str, "enable"))
> +		dynamic_epp = true;
> +
> +	return 0;
> +}
> +
>  early_param("amd_pstate", amd_pstate_param);
>  early_param("amd_prefcore", amd_prefcore_param);
> +early_param("amd_dynamic_epp", amd_dynamic_epp_param);
>  
>  MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
>  MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver");
> 
> -- 
> 2.43.0
> 

-- 
Thanks and Regards
gautham.

^ permalink raw reply

* Re: [PATCH v5 1/5] cpufreq/amd-pstate: Add dynamic energy performance preference
From: Gautham R. Shenoy @ 2026-03-27  6:43 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK, Mario Limonciello
In-Reply-To: <20260106051441.60093-2-superm1@kernel.org>

Hello Mario,


On Mon, Jan 05, 2026 at 11:14:37PM -0600, Mario Limonciello wrote:
> From: Mario Limonciello <mario.limonciello@amd.com>
> 
> Dynamic energy performance preference will change the EPP profile
> based on whether the machine is running on AC or DC power.
> 
> A notification chain from the power supply core is used to adjust
> EPP values on plug in or plug out events.
> 
> For non-server systems:
>     * the default EPP for AC mode is `performance`.
>     * the default EPP for DC mode is `balance_performance`.

In addition to these, the commit also adds a sysfs toggle, blocks
manual EPP, forces PERFORMANCE policy.

Might be good to mention these in the commit log.

> 
> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>

[..snip..]


> +static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
> +{
> +	struct amd_cpudata *cpudata = policy->driver_data;
> +	int ret;
> +	u8 epp;
> +
> +	epp = amd_pstate_get_balanced_epp(policy);
> +	ret = amd_pstate_set_epp(policy, epp);
> +	if (ret)
> +		return ret;
> +
> +	/* only enable notifier if things will actually change */
> +	if (cpudata->epp_default_ac != cpudata->epp_default_dc) {
> +		ret = power_supply_reg_notifier(&cpudata->power_nb);
> +		if (ret)
> +			goto cleanup;
> +		cpudata->power_nb.notifier_call = amd_pstate_power_supply_notifier;
> +	}
> +
> +	cpudata->dynamic_epp = true;
> +
> +	return 0;
> +
> +cleanup:
> +	amd_pstate_clear_dynamic_epp(policy);
> +
> +	return ret;
> +}


Can the notifier_call assignment and registration be reordered?

The cpudata struct is allocated with kzalloc, so
power_nb.notifier_call is NULL when power_supply_reg_notifier() is
called.  power_supply_reg_notifier() calls
blocking_notifier_chain_register(), which adds the notifier block to
the chain and then releases the rwsem.

If power_supply_changed_work() fires between the registration and the
assignment, the notification path reaches notifier_call_chain():

kernel/notifier.c:notifier_call_chain() {
    ...
    ret = nb->notifier_call(nb, val, v);
    ...
}

At this point notifier_call is still NULL, resulting in a NULL pointer
dereference.  Setting notifier_call before calling
power_supply_reg_notifier() would close this window.



> +
>  /* Sysfs attributes */
>  

[..snip..]

>  
>  static struct freq_attr *amd_pstate_attr[] = {
>  	&amd_pstate_max_freq,
> @@ -1421,6 +1528,7 @@ static struct freq_attr *amd_pstate_epp_attr[] = {
>  static struct attribute *pstate_global_attributes[] = {
>  	&dev_attr_status.attr,
>  	&dev_attr_prefcore.attr,
> +	&dev_attr_dynamic_epp.attr,
>  	NULL
>  };
>  
> @@ -1512,15 +1620,20 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
>  	if (amd_pstate_acpi_pm_profile_server() ||
>  	    amd_pstate_acpi_pm_profile_undefined()) {
>  		policy->policy = CPUFREQ_POLICY_PERFORMANCE;
> -		cpudata->epp_default = amd_pstate_get_epp(cpudata);
> +		cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata);

I wonder if we can set dynamic_epp to false for Server platforms.

The reason being that there are Server users who switch to the
powersave governor via a systemd startup script to set the right EPP.

With dynamic_epp = true, this is no longer possible. The user has to
first disable dynamic_epp and then switch to the powersave govenor.

Or perhaps you have a commandline override to disable dynamic_epp at boot time ?


-- 
Thanks and Regards
gautham.

^ permalink raw reply

* Re: [PATCH v8 0/2] cpufreq: Introduce boost frequency QoS
From: Viresh Kumar @ 2026-03-27  3:43 UTC (permalink / raw)
  To: Pierre Gondois
  Cc: linux-kernel, Lifeng Zheng, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, linux-pm
In-Reply-To: <20260326204404.1401849-1-pierre.gondois@arm.com>

On 26-03-26, 21:43, Pierre Gondois wrote:
> Pierre Gondois (2):
>   cpufreq: Remove max_freq_req update for pre-existing policy
>   cpufreq: Add boost_freq_req QoS request
> 
>  drivers/cpufreq/amd-pstate.c   |  2 --
>  drivers/cpufreq/cppc_cpufreq.c | 10 ++-----
>  drivers/cpufreq/cpufreq.c      | 50 +++++++++++++++++++++-------------
>  include/linux/cpufreq.h        |  1 +
>  4 files changed, 34 insertions(+), 29 deletions(-)

Thanks Pierre for your patience.

Acked-by: Viresh Kumar <viresh.kumar@linaro.org>

-- 
viresh

^ permalink raw reply

* Re: [PATCH v1 00/10] devfreq: Fix NULL pointer dereference when a governor module is unloaded
From: Yaxiong Tian @ 2026-03-27  2:06 UTC (permalink / raw)
  To: Jie Zhan, cw00.choi, myungjoo.ham, kyungmin.park
  Cc: linux-pm, linux-arm-kernel, linuxarm, jonathan.cameron,
	zhenglifeng1, zhangpengjie2, lihuisong, prime.zeng
In-Reply-To: <1774576755240654.100.seg@mailgw.kylinos.cn>


在 2026/3/26 21:14, Jie Zhan 写道:
>
> On 3/26/2026 8:34 PM, Jie Zhan wrote:
>> When compiled as a kernel module, the governor module can be dynamically
>> inserted or removed.  'devfreq->governor' would become NULL if the governor
>> module is removed when it's in use, and NULL pointer dereference would be
>> triggered.  A similar issue was also reported in [1].
>>
>> To address this issue:
>>
>> Patch 1-5 rework mutex, factor out a common governor setting function, and
>> clean up some unreachable code.
>>
>> Patch 6-8 prevent a governor module in use from being removed (except for
>> force unload) by getting/putting a refcount of the governor's module when
>> switching governors.
>>
>> Patch 9-10 allow 'governor' and 'available_governors' to work normally even
>> when a governor module in use is force unloaded.
>>
>> Note that this series is based on [1] or devfreq-next, otherwise code

Sorry, please ignore the "remember to CC me on the patches." in my 
previous email.

  In my opinion, it would be better to prioritize the FIX first before 
proceeding with the lock mechanism optimizations and other work. This 
would make it easier to backport the patches to lower-version kernels. I 
noticed the patch is already in the devfreq-testing branch. I hope the 
FIX work can be moved forward smoothly to resolve the null pointer and 
other bugs. Thank you!



> sorry, based on [2] or devfreq-next
>> would conflict.
>>
>> [1] https://lore.kernel.org/all/20260319091409.998397-1-tianyaxiong@kylinos.cn/
>> [2] https://lore.kernel.org/all/20251216031153.2242306-1-zhangpengjie2@huawei.com/

^ permalink raw reply

* Re: [PATCH RESEND 0/4] Fix some errors in the devfreq core layer when governor is NULL
From: Yaxiong Tian @ 2026-03-27  1:55 UTC (permalink / raw)
  To: Jie Zhan, myungjoo.ham, kyungmin.park, cw00.choi, nm
  Cc: linux-pm, linux-kernel
In-Reply-To: <1774486650723160.85.seg@mailgw.kylinos.cn>


在 2026/3/25 18:06, Jie Zhan 写道:
>
> On 3/19/2026 5:14 PM, Yaxiong Tian wrote:
>> While doing some development work with devfreq_add_governor()/
>> devfreq_remove_governor(), I discovered several bugs caused when
>> devfreq->governor is NULL. Specifically:
>>
>> 1) A possible null pointer issue in devfreq_add_governor(), caused
>> by devfreq_remove_governor() setting devfreq->governor to NULL in
>> certain situations, while devfreq_add_governor() lacks corresponding
>> checks for devfreq->governor.
>>
>> 2) When operating on governor and available_governors under /sys,
>> there are also some unexpected errors.
>>
>> See the following patches for details.
> Hi Yaxiong,
>
> Sorry for catching this late.
>
> I happen to be working on the same issues recently on a local tree, though
> delayed for quite a while due to other business.
>
> It would be a totally different approach by reworking the locking logic,
> which, I supposed, can make the devfreq framework cleaner to maintain.
>
> I'll send it out shortly.  Hopefully it can be a reference for us to
> discuss with.
>
> Thanks!
> Jie

Hi  Jie
	Thank you for your reply. I saw your PATCH in the public linked list, and I think your optimization work, such as the lock mechanism improvements, should be placed after my FIX. This will make it easier to backport the patches and fix lower-version kernels.
	I see the patch is already in the devfreq-testing branch. I hope this work can be pushed forward as soon as possible to resolve the null pointer and other bugs.
	Finally, if you have any further optimizations, remember to CC me on the patches. :)

>> Yaxiong Tian (4):
>>    PM / devfreq: Fix possible null pointer issue in
>>      devfreq_add_governor()
>>    PM / devfreq: Fix available_governors_show() when no governor is set
>>    PM / devfreq: Fix governor_store() failing when device has no current
>>      governor
>>    PM / devfreq: Optimize error return value of governor_show()
>>
>>   drivers/devfreq/devfreq.c | 46 +++++++++++++++++++++------------------
>>   1 file changed, 25 insertions(+), 21 deletions(-)
>>

^ permalink raw reply

* Re: [PATCH v1] thermal: core: Address thermal zone removal races with resume
From: Mauricio Faria de Oliveira @ 2026-03-26 22:44 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM, LKML, Daniel Lezcano, Lukasz Luba
In-Reply-To: <CAJZ5v0hmkRsz2A0sh4PdEY9V=gLJejWH=kMJH6hR_bh=Q9ZdfA@mail.gmail.com>

On 2026-03-26 16:03, Rafael J. Wysocki wrote:
> On Thu, Mar 26, 2026 at 7:35 PM Mauricio Faria de Oliveira
> <mfo@igalia.com> wrote:
>>
>> On 2026-03-26 08:45, Rafael J. Wysocki wrote:
>> > Address the first failing scenario by ensuring that no thermal work
>> > items will be running when thermal_pm_notify_complete() is called.
>> > For this purpose, first move the cancel_delayed_work() call from
>> > thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent
>> > new work from entering the workqueue going forward.  Next, switch
>> > over to using a dedicated workqueue for thermal events and update
>> > the code in thermal_pm_notify() to flush that workqueue after
>> > thermal_pm_notify_prepare() has returned which will take care of
>> > all leftover thermal work already on the workqueue (that leftover
>> > work would do nothing useful anyway because all of the thermal zones
>> > have been flagged as suspended).
>>
>> Thanks for coming up with this alternative. I spent some time earlier
>> today thinking of corner cases in that it might fail, and it held OK.
>>
>> However, slightly unrelated: apparently, flushing the workqueue in
>> thermal_pm_notify() reintroduces the issue addressed by the Fixes:
>> commit, but moving it from PM_POST_* to PM_*_PREPARE?
> 
> Note that the work in question will be thermal_zone_device_check(),
> which simply calls thermal_zone_device_update() that essentially
> invokes __thermal_zone_device_update() under tz->lock.
> 
> Thus thermal_zone_device_update() can only run as a whole before or
> after thermal_zone_pm_complete() for the given zone because the latter

      ^ thermal_zone_pm_prepare(), you mean? 
        (in PM_*_PREPARE path; sets TZ_STATE_FLAG_SUSPENDED mentioned
below.)

> also acquires tz->lock and releases it at the end.  If it runs before
> the latter, it will be waited for because the latter will block on the
> lock, but that happens without the changes in the $subject patch.  If

Ok, right; that already happens.

> it runs after the latter, __thermal_zone_device_update() will see that
> tz->state is not TZ_STATE_READY (because TZ_STATE_FLAG_SUSPENDED is
> set) and it will bail out immediately.
> 
> So I don't see the problem here.

Well, __thermal_zone_device_update() taking long might indeed impact
the PM_*_PREPARE path too (the former case, "it will be waited for"),
however, as you said, it happens without this patch, and it is not
fixed with the patch I proposed either. :)

> PM_POST_* is different because thermal_zone_device_resume() calls
> __thermal_zone_device_update() when tz->state is TZ_STATE_READY and
> that may take time.
> 
>> IIIUC, that issue is __thermal_zone_device_update() might take long
>> thus block other thermal zones and other PM notifiers after thermal.
>>
>> Apparently, at least the latter also applies to PM_*_PREPARE?
> 
> Not at the point when the flush_workqueue() is called.

Thanks for clarifying.

>> Say, a currently running work item (i.e., that cancel_delayed_work()
>> cannot cancel) wins the race for tz->lock and doesn't see tz->state
>> TZ_STATE_FLAG_SUSPENDED set, so it runs, and say it might take long.
>>
>> Now, the workqueue flush blocks on it, also taking long, which thus
>> blocks other PM notifiers.
>>
>> > The second failing scenario is addressed by adding a tz->state check
>> > to thermal_zone_device_resume() to prevent it from reinitializing
>> > the poll_queue delayed work if the thermal zone is going away.
>>
>> This also held OK in the thinking of corner cases.
> 
> Thanks for the feedback!

Glad to help!

-- 
Mauricio

^ permalink raw reply

* [PATCH v8 2/2] cpufreq: Add boost_freq_req QoS request
From: Pierre Gondois @ 2026-03-26 20:44 UTC (permalink / raw)
  To: linux-kernel
  Cc: Lifeng Zheng, Pierre Gondois, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	linux-pm
In-Reply-To: <20260326204404.1401849-1-pierre.gondois@arm.com>

The Power Management Quality of Service (PM QoS) allows to
aggregate constraints from multiple entities. It is currently
used to manage the min/max frequency of a given policy.

Frequency constraints can come for instance from:
- Thermal framework: acpi_thermal_cpufreq_init()
- Firmware: _PPC objects: acpi_processor_ppc_init()
- User: by setting policyX/scaling_[min|max]_freq
The minimum of the max frequency constraints is used to compute
the resulting maximum allowed frequency.

When enabling boost frequencies, the same frequency request object
(policy->max_freq_req) as to handle requests from users is used.
As a result, when setting:
- scaling_max_freq
- boost
The last sysfs file used overwrites the request from the other
sysfs file.

To avoid this, create a per-policy boost_freq_req to save the boost
constraints instead of overwriting the last scaling_max_freq
constraint.

policy_set_boost() calls the cpufreq set_boost callback.
Update the newly added boost_freq_req request from there:
- whenever boost is toggled
- to cover all possible paths

In the existing .set_boost() callbacks:
- Don't update policy->max as this is done through the qos notifier
  cpufreq_notifier_max() which calls cpufreq_set_policy().
- Remove freq_qos_update_request() calls as the qos request is now
  done in policy_set_boost() and updates the new boost_freq_req

$ ## Init state
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

$ echo 700000 > scaling_max_freq
scaling_max_freq:700000
cpuinfo_max_freq:1000000

$ echo 1 > ../boost
scaling_max_freq:1200000
cpuinfo_max_freq:1200000

$ echo 800000 > scaling_max_freq
scaling_max_freq:800000
cpuinfo_max_freq:1200000

$ ## Final step:
$ ## Without the patches:
$ echo 0 > ../boost
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

$ ## With the patches:
$ echo 0 > ../boost
scaling_max_freq:800000
cpuinfo_max_freq:1000000

Note:
cpufreq_frequency_table_cpuinfo() updates policy->min
and max from:
A.
cpufreq_boost_set_sw()
\-cpufreq_frequency_table_cpuinfo()
B.
cpufreq_policy_online()
\-cpufreq_table_validate_and_sort()
  \-cpufreq_frequency_table_cpuinfo()
Keep these updates as some drivers expect policy->min and
max to be set through B.

Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
 drivers/cpufreq/amd-pstate.c   |  2 --
 drivers/cpufreq/cppc_cpufreq.c | 10 ++------
 drivers/cpufreq/cpufreq.c      | 46 +++++++++++++++++++++++-----------
 include/linux/cpufreq.h        |  1 +
 4 files changed, 34 insertions(+), 25 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 5aa9fcd80cf51..d0675d6a19fe1 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -769,8 +769,6 @@ static int amd_pstate_cpu_boost_update(struct cpufreq_policy *policy, bool on)
 	else if (policy->cpuinfo.max_freq > nominal_freq)
 		policy->cpuinfo.max_freq = nominal_freq;
 
-	policy->max = policy->cpuinfo.max_freq;
-
 	if (cppc_state == AMD_PSTATE_PASSIVE) {
 		ret = freq_qos_update_request(&cpudata->req[1], policy->cpuinfo.max_freq);
 		if (ret < 0)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 011f35cb47b94..f4f574fbe547b 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -807,17 +807,11 @@ static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
-	int ret;
 
 	if (state)
-		policy->max = cppc_perf_to_khz(caps, caps->highest_perf);
+		policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->highest_perf);
 	else
-		policy->max = cppc_perf_to_khz(caps, caps->nominal_perf);
-	policy->cpuinfo.max_freq = policy->max;
-
-	ret = freq_qos_update_request(policy->max_freq_req, policy->max);
-	if (ret < 0)
-		return ret;
+		policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->nominal_perf);
 
 	return 0;
 }
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 5757f12633d16..d2f393d738a39 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -609,10 +609,19 @@ static int policy_set_boost(struct cpufreq_policy *policy, bool enable)
 	policy->boost_enabled = enable;
 
 	ret = cpufreq_driver->set_boost(policy, enable);
-	if (ret)
+	if (ret) {
 		policy->boost_enabled = !policy->boost_enabled;
+		return ret;
+	}
 
-	return ret;
+	ret = freq_qos_update_request(policy->boost_freq_req, policy->cpuinfo.max_freq);
+	if (ret < 0) {
+		policy->boost_enabled = !policy->boost_enabled;
+		cpufreq_driver->set_boost(policy, policy->boost_enabled);
+		return ret;
+	}
+
+	return 0;
 }
 
 static ssize_t store_local_boost(struct cpufreq_policy *policy,
@@ -1377,6 +1386,7 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
 	}
 
 	freq_qos_remove_request(policy->min_freq_req);
+	freq_qos_remove_request(policy->boost_freq_req);
 	kfree(policy->min_freq_req);
 
 	cpufreq_policy_put_kobj(policy);
@@ -1445,26 +1455,38 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
 	cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);
 
 	if (new_policy) {
+		unsigned int count;
+
 		for_each_cpu(j, policy->related_cpus) {
 			per_cpu(cpufreq_cpu_data, j) = policy;
 			add_cpu_dev_symlink(policy, j, get_cpu_device(j));
 		}
 
-		policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
+		count = policy->boost_supported ? 3 : 2;
+		policy->min_freq_req = kzalloc(count * sizeof(*policy->min_freq_req),
 					       GFP_KERNEL);
 		if (!policy->min_freq_req) {
 			ret = -ENOMEM;
 			goto out_destroy_policy;
 		}
 
+		if (policy->boost_supported) {
+			policy->boost_freq_req = policy->min_freq_req + 2;
+
+			ret = freq_qos_add_request(&policy->constraints,
+						   policy->boost_freq_req,
+						   FREQ_QOS_MAX,
+						   policy->cpuinfo.max_freq);
+			if (ret < 0) {
+				policy->boost_freq_req = NULL;
+				goto out_destroy_policy;
+			}
+		}
+
 		ret = freq_qos_add_request(&policy->constraints,
 					   policy->min_freq_req, FREQ_QOS_MIN,
 					   FREQ_QOS_MIN_DEFAULT_VALUE);
 		if (ret < 0) {
-			/*
-			 * So we don't call freq_qos_remove_request() for an
-			 * uninitialized request.
-			 */
 			kfree(policy->min_freq_req);
 			policy->min_freq_req = NULL;
 			goto out_destroy_policy;
@@ -2788,16 +2810,10 @@ int cpufreq_boost_set_sw(struct cpufreq_policy *policy, int state)
 		return -ENXIO;
 
 	ret = cpufreq_frequency_table_cpuinfo(policy);
-	if (ret) {
+	if (ret)
 		pr_err("%s: Policy frequency update failed\n", __func__);
-		return ret;
-	}
-
-	ret = freq_qos_update_request(policy->max_freq_req, policy->max);
-	if (ret < 0)
-		return ret;
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(cpufreq_boost_set_sw);
 
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index cc894fc389710..89157e367eefa 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -81,6 +81,7 @@ struct cpufreq_policy {
 	struct freq_constraints	constraints;
 	struct freq_qos_request	*min_freq_req;
 	struct freq_qos_request	*max_freq_req;
+	struct freq_qos_request *boost_freq_req;
 
 	struct cpufreq_frequency_table	*freq_table;
 	enum cpufreq_table_sorting freq_table_sorted;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v8 1/2] cpufreq: Remove max_freq_req update for pre-existing policy
From: Pierre Gondois @ 2026-03-26 20:44 UTC (permalink / raw)
  To: linux-kernel
  Cc: Lifeng Zheng, Pierre Gondois, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	linux-pm
In-Reply-To: <20260326204404.1401849-1-pierre.gondois@arm.com>

policy->max_freq_req QoS constraint represents the maximal allowed
frequency than can be requested. It is set by:
- writing to policyX/scaling_max sysfs file
- toggling the cpufreq/boost sysfs file

Upon calling freq_qos_update_request(), a successful update
of the max_freq_req value triggers cpufreq_notifier_max(),
followed by cpufreq_set_policy() which update the requested
frequency for the policy.
If the new max_freq_req value is not different from the
original value, no frequency update is triggered.

In a specific sequence of toggling:
- cpufreq/boost sysfs file
- CPU hot-plugging
a CPU could end up with boost enabled but running at the
maximal non-boost frequency, cpufreq_notifier_max() not being
triggered. The following fixed that:
commit 1608f0230510 ("cpufreq: Fix re-boost issue after hotplugging
a CPU")

The following:
commit dd016f379ebc ("cpufreq: Introduce a more generic way to
set default per-policy boost flag")
also fixed the issue by correctly setting the max_freq_req
constraint of a policy that is re-activated. This makes the
first fix unnecessary.

As the original issue is fixed by another method,
this patch reverts:
commit 1608f0230510 ("cpufreq: Fix re-boost issue after hotplugging
a CPU")

Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
 drivers/cpufreq/cpufreq.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 277884d91913c..5757f12633d16 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1487,10 +1487,6 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
 
 		blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
 				CPUFREQ_CREATE_POLICY, policy);
-	} else {
-		ret = freq_qos_update_request(policy->max_freq_req, policy->max);
-		if (ret < 0)
-			goto out_destroy_policy;
 	}
 
 	if (cpufreq_driver->get && has_target()) {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v8 0/2] cpufreq: Introduce boost frequency QoS
From: Pierre Gondois @ 2026-03-26 20:43 UTC (permalink / raw)
  To: linux-kernel
  Cc: Lifeng Zheng, Pierre Gondois, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	linux-pm

The Power Management Quality of Service (PM QoS) allows to
aggregate constraints from multiple entities. It is currently
used to manage the min/max frequency of a given policy.

Frequency constraints can come from:
- Thermal framework: acpi_thermal_cpufreq_init()
- Firmware: _PPC objects: acpi_processor_ppc_init()
- User: by setting policyX/scaling_[min|max]_freq
The minimum of the max frequency constraints is used to compute
the resulting maximum allowed frequency.

When enabling boost frequencies, the same frequency request object
(policy->max_freq_req) as to handle requests from users is used.
As a result, when setting:
- scaling_max_freq
- boost
The last sysfs file used overwrites the request from the other
sysfs file.

To avoid this:
1. Create a per-policy boost_freq_req to save the boost
constraints instead of overwriting the last scaling_max_freq
constraint.

2. policy_set_boost() calls the cpufreq set_boost callback.
Update the newly added boost_freq_req request from there:
- whenever boost is toggled
- to cover all possible paths

3. In the existing set_boost() callbacks:
- Don't update policy->max as this is done through the qos notifier
  cpufreq_notifier_max() which calls cpufreq_set_policy().
- Remove freq_qos_update_request() calls as the qos request is now
  done in policy_set_boost() and updates the new boost_freq_req

---

E.g.:
On a Juno with available frequencies: 600.000, 1.000.000
Boost frequencies: 1.200.000
Using the cppc-cpufreq driver.

---
Without the patches:
# ## Init state
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

# echo 700000 > scaling_max_freq
scaling_max_freq:700000
cpuinfo_max_freq:1000000

# echo 1 > ../boost
scaling_max_freq:1200000
cpuinfo_max_freq:1200000

# echo 800000 > scaling_max_freq
scaling_max_freq:800000
cpuinfo_max_freq:1200000

# echo 0 > ../boost
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

---
With the patches:
# ## Init
scaling_max_freq:1000000
cpuinfo_max_freq:1000000

# echo 700000 > scaling_max_freq
scaling_max_freq:700000
cpuinfo_max_freq:1000000

# echo 1 > ../boost
scaling_max_freq:700000
cpuinfo_max_freq:1200000

# echo 800000 > scaling_max_freq
scaling_max_freq:800000
cpuinfo_max_freq:1200000

# echo 0 > ../boost
scaling_max_freq:800000
cpuinfo_max_freq:1000000

With the patches, the maximum scaling frequency requested is
conserved even though boosting is enabled/disabled.

---

Note:
It seems that there is a confusion in the cpufreq framework between:
- the min/max frequency requested by the user
- the min/max frequency constraint applied when selecting a frequency.

E.g:
A.
$ echo XXX > scaling_max_freq
updates the max_freq_req QoS request.

B.
$ cat scaling_max_freq
shows the content of policy->max, which is the not representing
the value of the max_freq_req QoS request.

C.
Whenever policy->max is accessed in the cpufreq framework,
the aggregation of all the requests on the maximum frequency should
be used instead.

cpufreq_set_policy() aggregates min/max constraints and
writes the resulting value in policy->min/max. These values
are then used in the cpufreq drivers.

Creating a clear distinction would be doable but quite invasive.
This patchset focuses on handling the boost frequency QoS request
first and should not change the current behaviour of policy->min
and max.

---

v1: https://lore.kernel.org/all/20251204101344.192678-1-pierre.gondois@arm.com/#t
v2: https://lore.kernel.org/all/20251208105933.1369125-1-pierre.gondois@arm.com/#t
Changes:
- Fixed error path
- Integrated [PATCH 1/4] Revert "cpufreq: Fix re-boost issue after hotplugging a CPU"
  to another patch
v3:
Changes:
- Fixed error path
- Extracted the revert of:
  "cpufreq: Fix re-boost issue after hotplugging a CPU"
  for clarity purpose
- Set cpuinfo.max_freq as a max_freq_req QoS constraint by default
New patches:
- "cpufreq: Allow decreasing cpuinfo.max_freq"
- "cpufreq: Set policy->min and max as QoS constraints"
v4:
- Correct reported issues
v5:
- Corrections
v6:
- Folded patches:
  - cpufreq: Centralize boost freq QoS requests
  - cpufreq: Update .set_boost() callbacks to rely on boost_freq_req
  inside:
  - cpufreq: Add boost_freq_req QoS request
- Simplified allocation handling of boost_freq_req
- Removed unnecessary bits
v7:
- Removed the following patches to submit them separately
  - cpufreq: Set policy->min and max as real QoS constraints
  - cpufreq/freq_table: Allow decreasing cpuinfo.max_freq
- Fixed blocking_notifier_call_chain() call order when removing
  a policy.
- Updated the commit message of:
  - cpufreq: Remove per-CPU QoS constraint
v8:
- Renamed first patch
- Small corrections to second patch.

Pierre Gondois (2):
  cpufreq: Remove max_freq_req update for pre-existing policy
  cpufreq: Add boost_freq_req QoS request

 drivers/cpufreq/amd-pstate.c   |  2 --
 drivers/cpufreq/cppc_cpufreq.c | 10 ++-----
 drivers/cpufreq/cpufreq.c      | 50 +++++++++++++++++++++-------------
 include/linux/cpufreq.h        |  1 +
 4 files changed, 34 insertions(+), 29 deletions(-)

--
2.43.0

^ permalink raw reply

* Re: [GIT PULL] Power management fixes for v7.0-rc6
From: pr-tracker-bot @ 2026-03-26 19:48 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linus Torvalds, Linux PM, Linux Kernel Mailing List, Viresh Kumar
In-Reply-To: <CAJZ5v0jqdP-n4H+RXvVPyHhtGLCWs2gma_FCrDOrtb0QqcKJHQ@mail.gmail.com>

The pull request you sent on Thu, 26 Mar 2026 19:12:09 +0100:

> git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git pm-7.0-rc6

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/d813f421930c5b01b9f61043932de02602dd6ae3

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply

* Re: [GIT PULL] Thermal control fix for v7.0-rc6
From: pr-tracker-bot @ 2026-03-26 19:48 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linus Torvalds, Linux PM, Linux Kernel Mailing List,
	Daniel Lezcano
In-Reply-To: <CAJZ5v0ggLdfPQZSThBgNhErY7n66quQoo_uhiMZVFawRGpfA9A@mail.gmail.com>

The pull request you sent on Thu, 26 Mar 2026 19:11:02 +0100:

> git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git thermal-7.0-rc6

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/9c2b23a275ceb52c7a0eeab6556f56e7257b2230

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply

* [PATCH v3] cpufreq/amd-pstate: Cache the max frequency in cpudata
From: Mario Limonciello @ 2026-03-26 19:36 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)

From: "Mario Limonciello (AMD)" <superm1@kernel.org>

The value of maximum frequency is fixed and never changes. Doing
calculations every time based off of perf is unnecessary.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
v3:
 * rebase on Gautham's CPPC perf priority series
 * add kdoc
---
 drivers/cpufreq/amd-pstate.c | 27 +++++++++------------------
 drivers/cpufreq/amd-pstate.h |  2 ++
 2 files changed, 11 insertions(+), 18 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index ed9fd4155a256..f207252eb5f5f 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -826,15 +826,13 @@ static void amd_pstate_adjust_perf(unsigned int cpu,
 static int amd_pstate_cpu_boost_update(struct cpufreq_policy *policy, bool on)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
-	union perf_cached perf = READ_ONCE(cpudata->perf);
-	u32 nominal_freq, max_freq;
+	u32 nominal_freq;
 	int ret = 0;
 
 	nominal_freq = READ_ONCE(cpudata->nominal_freq);
-	max_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.highest_perf);
 
 	if (on)
-		policy->cpuinfo.max_freq = max_freq;
+		policy->cpuinfo.max_freq = cpudata->max_freq;
 	else if (policy->cpuinfo.max_freq > nominal_freq)
 		policy->cpuinfo.max_freq = nominal_freq;
 
@@ -1021,13 +1019,15 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
 
 	WRITE_ONCE(cpudata->nominal_freq, nominal_freq);
 
+	/* max_freq is calculated according to (nominal_freq * highest_perf)/nominal_perf */
 	max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
+	WRITE_ONCE(cpudata->max_freq, max_freq);
+
 	lowest_nonlinear_freq = perf_to_freq(perf, nominal_freq, perf.lowest_nonlinear_perf);
 	WRITE_ONCE(cpudata->lowest_nonlinear_freq, lowest_nonlinear_freq);
 
 	/**
 	 * Below values need to be initialized correctly, otherwise driver will fail to load
-	 * max_freq is calculated according to (nominal_freq * highest_perf)/nominal_perf
 	 * lowest_nonlinear_freq is a value between [min_freq, nominal_freq]
 	 * Check _CPC in ACPI table objects if any values are incorrect
 	 */
@@ -1090,9 +1090,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
 	policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf,
 							      cpudata->nominal_freq,
 							      perf.lowest_perf);
-	policy->cpuinfo.max_freq = policy->max = perf_to_freq(perf,
-							      cpudata->nominal_freq,
-							      perf.highest_perf);
+	policy->cpuinfo.max_freq = policy->max = cpudata->max_freq;
 
 	policy->driver_data = cpudata;
 	ret = amd_pstate_cppc_enable(policy);
@@ -1167,14 +1165,9 @@ static void amd_pstate_cpu_exit(struct cpufreq_policy *policy)
 static ssize_t show_amd_pstate_max_freq(struct cpufreq_policy *policy,
 					char *buf)
 {
-	struct amd_cpudata *cpudata;
-	union perf_cached perf;
-
-	cpudata = policy->driver_data;
-	perf = READ_ONCE(cpudata->perf);
+	struct amd_cpudata *cpudata = policy->driver_data;
 
-	return sysfs_emit(buf, "%u\n",
-			  perf_to_freq(perf, cpudata->nominal_freq, perf.highest_perf));
+	return sysfs_emit(buf, "%u\n", cpudata->max_freq);
 }
 
 static ssize_t show_amd_pstate_lowest_nonlinear_freq(struct cpufreq_policy *policy,
@@ -1702,9 +1695,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 	policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf,
 							      cpudata->nominal_freq,
 							      perf.lowest_perf);
-	policy->cpuinfo.max_freq = policy->max = perf_to_freq(perf,
-							      cpudata->nominal_freq,
-							      perf.highest_perf);
+	policy->cpuinfo.max_freq = policy->max = cpudata->max_freq;
 	policy->driver_data = cpudata;
 
 	ret = amd_pstate_cppc_enable(policy);
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index faead0b19a8a4..32b8b26ce388f 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -73,6 +73,7 @@ struct amd_aperf_mperf {
  * @min_limit_freq: Cached value of policy->min (in khz)
  * @max_limit_freq: Cached value of policy->max (in khz)
  * @nominal_freq: the frequency (in khz) that mapped to nominal_perf
+ * @max_freq: in ideal conditions the maximum frequency (in khz) possible frequency
  * @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf
  * @floor_freq: Cached value of the user requested floor_freq
  * @cur: Difference of Aperf/Mperf/tsc count between last and current sample
@@ -103,6 +104,7 @@ struct amd_cpudata {
 	u32	min_limit_freq;
 	u32	max_limit_freq;
 	u32	nominal_freq;
+	u32	max_freq;
 	u32	lowest_nonlinear_freq;
 	u32	floor_freq;
 
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v4 09/12] amd-pstate-ut: Add a testcase to validate the visibility of driver attributes
From: Mario Limonciello @ 2026-03-26 19:34 UTC (permalink / raw)
  To: Gautham R. Shenoy, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm
In-Reply-To: <20260326114756.20374-10-gautham.shenoy@amd.com>



On 3/26/26 06:47, Gautham R. Shenoy wrote:
> amd-pstate driver has per-attribute visibility functions to
> dynamically control which sysfs freq_attrs are exposed based on the
> platform capabilities and the current amd_pstate mode. However, there
> is no test coverage to validate that the driver's live attribute list
> matches the expected visibility for each mode.
> 
> Add amd_pstate_ut_check_freq_attrs() to the amd-pstate unit test
> module. For each enabled mode (passive, active, guided), the test
> independently derives the expected visibility of each attribute:
>    - Core attributes (max_freq, lowest_nonlinear_freq, highest_perf)
>      are always expected.
>    - Prefcore attributes (prefcore_ranking, hw_prefcore) are expected
>      only when cpudata->hw_prefcore indicates platform support.
>    - EPP attributes (energy_performance_preference,
>      energy_performance_available_preferences) are expected only in
>      active mode.
>    - Floor frequency attributes (floor_freq, floor_count) are expected
>      only when X86_FEATURE_CPPC_PERF_PRIO is present.
> 
> Compare these independent expectations against the live driver's attr
> array, catching bugs such as attributes leaking into wrong modes or
> visibility functions checking incorrect conditions.
> 
> Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> ---
>   drivers/cpufreq/amd-pstate-ut.c | 139 ++++++++++++++++++++++++++++++--
>   drivers/cpufreq/amd-pstate.c    |   8 ++
>   drivers/cpufreq/amd-pstate.h    |   4 +
>   3 files changed, 146 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
> index 3dcdf56883a6..1f62ab6438b4 100644
> --- a/drivers/cpufreq/amd-pstate-ut.c
> +++ b/drivers/cpufreq/amd-pstate-ut.c
> @@ -23,6 +23,8 @@
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>   
>   #include <linux/bitfield.h>
> +#include <linux/cpufeature.h>
> +#include <linux/cpufreq.h>
>   #include <linux/kernel.h>
>   #include <linux/module.h>
>   #include <linux/moduleparam.h>
> @@ -53,13 +55,15 @@ static int amd_pstate_ut_check_enabled(u32 index);
>   static int amd_pstate_ut_check_perf(u32 index);
>   static int amd_pstate_ut_check_freq(u32 index);
>   static int amd_pstate_ut_check_driver(u32 index);
> +static int amd_pstate_ut_check_freq_attrs(u32 index);
>   
>   static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
> -	{"amd_pstate_ut_acpi_cpc_valid",   amd_pstate_ut_acpi_cpc_valid   },
> -	{"amd_pstate_ut_check_enabled",    amd_pstate_ut_check_enabled    },
> -	{"amd_pstate_ut_check_perf",       amd_pstate_ut_check_perf       },
> -	{"amd_pstate_ut_check_freq",       amd_pstate_ut_check_freq       },
> -	{"amd_pstate_ut_check_driver",	   amd_pstate_ut_check_driver     }
> +	{"amd_pstate_ut_acpi_cpc_valid",    amd_pstate_ut_acpi_cpc_valid   },
> +	{"amd_pstate_ut_check_enabled",     amd_pstate_ut_check_enabled    },
> +	{"amd_pstate_ut_check_perf",        amd_pstate_ut_check_perf       },
> +	{"amd_pstate_ut_check_freq",        amd_pstate_ut_check_freq       },
> +	{"amd_pstate_ut_check_driver",      amd_pstate_ut_check_driver     },
> +	{"amd_pstate_ut_check_freq_attrs",  amd_pstate_ut_check_freq_attrs },
>   };
>   
>   static bool test_in_list(const char *list, const char *name)
> @@ -293,6 +297,131 @@ static int amd_pstate_ut_check_driver(u32 index)
>   	return ret;
>   }
>   
> +enum attr_category {
> +	ATTR_ALWAYS,
> +	ATTR_PREFCORE,
> +	ATTR_EPP,
> +	ATTR_FLOOR_FREQ,
> +};
> +
> +static const struct {
> +	const char	*name;
> +	enum attr_category category;
> +} expected_freq_attrs[] = {
> +	{"amd_pstate_max_freq",				ATTR_ALWAYS},
> +	{"amd_pstate_lowest_nonlinear_freq",		ATTR_ALWAYS},
> +	{"amd_pstate_highest_perf",			ATTR_ALWAYS},
> +	{"amd_pstate_prefcore_ranking",			ATTR_PREFCORE},
> +	{"amd_pstate_hw_prefcore",			ATTR_PREFCORE},
> +	{"energy_performance_preference",		ATTR_EPP},
> +	{"energy_performance_available_preferences",	ATTR_EPP},
> +	{"amd_pstate_floor_freq",			ATTR_FLOOR_FREQ},
> +	{"amd_pstate_floor_count",			ATTR_FLOOR_FREQ},
> +};
> +
> +static bool attr_in_driver(struct freq_attr **driver_attrs, const char *name)
> +{
> +	int j;
> +
> +	for (j = 0; driver_attrs[j]; j++) {
> +		if (!strcmp(driver_attrs[j]->attr.name, name))
> +			return true;
> +	}
> +	return false;
> +}
> +
> +/*
> + * Verify that for each mode the driver's live ->attr array contains exactly
> + * the attributes that should be visible.  Expected visibility is derived
> + * independently from hw_prefcore, cpu features, and the current mode —
> + * not from the driver's own visibility functions.
> + */
> +static int amd_pstate_ut_check_freq_attrs(u32 index)
> +{
> +	enum amd_pstate_mode orig_mode = amd_pstate_get_status();
> +	static const enum amd_pstate_mode modes[] = {
> +		AMD_PSTATE_PASSIVE, AMD_PSTATE_ACTIVE, AMD_PSTATE_GUIDED,
> +	};
> +	bool has_prefcore, has_floor_freq;
> +	int m, i, ret;
> +
> +	has_floor_freq = cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO);
> +
> +	/*
> +	 * Determine prefcore support from any online CPU's cpudata.
> +	 * hw_prefcore reflects the platform-wide decision made at init.
> +	 */
> +	has_prefcore = false;
> +	for_each_online_cpu(i) {
> +		struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
> +		struct amd_cpudata *cpudata;
> +
> +		policy = cpufreq_cpu_get(i);
> +		if (!policy)
> +			continue;
> +		cpudata = policy->driver_data;
> +		has_prefcore = cpudata->hw_prefcore;
> +		break;
> +	}
> +
> +	for (m = 0; m < ARRAY_SIZE(modes); m++) {
> +		struct freq_attr **driver_attrs;
> +
> +		ret = amd_pstate_set_mode(modes[m]);
> +		if (ret)
> +			goto out;
> +
> +		driver_attrs = amd_pstate_get_current_attrs();
> +		if (!driver_attrs) {
> +			pr_err("%s: no driver attrs in mode %s\n",
> +			       __func__, amd_pstate_get_mode_string(modes[m]));
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +
> +		for (i = 0; i < ARRAY_SIZE(expected_freq_attrs); i++) {
> +			bool expected, found;
> +
> +			switch (expected_freq_attrs[i].category) {
> +			case ATTR_ALWAYS:
> +				expected = true;
> +				break;
> +			case ATTR_PREFCORE:
> +				expected = has_prefcore;
> +				break;
> +			case ATTR_EPP:
> +				expected = (modes[m] == AMD_PSTATE_ACTIVE);
> +				break;
> +			case ATTR_FLOOR_FREQ:
> +				expected = has_floor_freq;
> +				break;
> +			default:
> +				expected = false;
> +				break;
> +			}
> +
> +			found = attr_in_driver(driver_attrs,
> +					       expected_freq_attrs[i].name);
> +
> +			if (expected != found) {
> +				pr_err("%s: mode %s: attr %s expected %s but is %s\n",
> +				       __func__,
> +				       amd_pstate_get_mode_string(modes[m]),
> +				       expected_freq_attrs[i].name,
> +				       expected ? "visible" : "hidden",
> +				       found ? "visible" : "hidden");
> +				ret = -EINVAL;
> +				goto out;
> +			}
> +		}
> +	}
> +
> +	ret = 0;
> +out:
> +	amd_pstate_set_mode(orig_mode);
> +	return ret;
> +}
> +
>   static int __init amd_pstate_ut_init(void)
>   {
>   	u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 5eae74a67aeb..ed9fd4155a25 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -1390,6 +1390,14 @@ static struct freq_attr_visibility amd_pstate_attr_visibility[] = {
>   	{&amd_pstate_floor_count, floor_freq_visibility},
>   };
>   
> +struct freq_attr **amd_pstate_get_current_attrs(void)
> +{
> +	if (!current_pstate_driver)
> +		return NULL;
> +	return current_pstate_driver->attr;
> +}
> +EXPORT_SYMBOL_GPL(amd_pstate_get_current_attrs);
> +
>   static struct freq_attr **get_freq_attrs(void)
>   {
>   	bool attr_visible[ARRAY_SIZE(amd_pstate_attr_visibility)];
> diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
> index 453adfb445f8..faead0b19a8a 100644
> --- a/drivers/cpufreq/amd-pstate.h
> +++ b/drivers/cpufreq/amd-pstate.h
> @@ -134,4 +134,8 @@ const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode);
>   int amd_pstate_get_status(void);
>   int amd_pstate_update_status(const char *buf, size_t size);
>   
> +struct freq_attr;
> +
> +struct freq_attr **amd_pstate_get_current_attrs(void);
> +
>   #endif /* _LINUX_AMD_PSTATE_H */


^ permalink raw reply

* Re: [PATCH v1] thermal: core: Address thermal zone removal races with resume
From: Rafael J. Wysocki @ 2026-03-26 19:03 UTC (permalink / raw)
  To: Mauricio Faria de Oliveira
  Cc: Rafael J. Wysocki, Linux PM, LKML, Daniel Lezcano, Lukasz Luba
In-Reply-To: <4f1186a042ed78f06d8f2ce1eb6f3ce3@igalia.com>

On Thu, Mar 26, 2026 at 7:35 PM Mauricio Faria de Oliveira
<mfo@igalia.com> wrote:
>
> On 2026-03-26 08:45, Rafael J. Wysocki wrote:
> > Address the first failing scenario by ensuring that no thermal work
> > items will be running when thermal_pm_notify_complete() is called.
> > For this purpose, first move the cancel_delayed_work() call from
> > thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent
> > new work from entering the workqueue going forward.  Next, switch
> > over to using a dedicated workqueue for thermal events and update
> > the code in thermal_pm_notify() to flush that workqueue after
> > thermal_pm_notify_prepare() has returned which will take care of
> > all leftover thermal work already on the workqueue (that leftover
> > work would do nothing useful anyway because all of the thermal zones
> > have been flagged as suspended).
>
> Thanks for coming up with this alternative. I spent some time earlier
> today thinking of corner cases in that it might fail, and it held OK.
>
> However, slightly unrelated: apparently, flushing the workqueue in
> thermal_pm_notify() reintroduces the issue addressed by the Fixes:
> commit, but moving it from PM_POST_* to PM_*_PREPARE?

Note that the work in question will be thermal_zone_device_check(),
which simply calls thermal_zone_device_update() that essentially
invokes __thermal_zone_device_update() under tz->lock.

Thus thermal_zone_device_update() can only run as a whole before or
after thermal_zone_pm_complete() for the given zone because the latter
also acquires tz->lock and releases it at the end.  If it runs before
the latter, it will be waited for because the latter will block on the
lock, but that happens without the changes in the $subject patch.  If
it runs after the latter, __thermal_zone_device_update() will see that
tz->state is not TZ_STATE_READY (because TZ_STATE_FLAG_SUSPENDED is
set) and it will bail out immediately.

So I don't see the problem here.

PM_POST_* is different because thermal_zone_device_resume() calls
__thermal_zone_device_update() when tz->state is TZ_STATE_READY and
that may take time.

> IIIUC, that issue is __thermal_zone_device_update() might take long
> thus block other thermal zones and other PM notifiers after thermal.
>
> Apparently, at least the latter also applies to PM_*_PREPARE?

Not at the point when the flush_workqueue() is called.

> Say, a currently running work item (i.e., that cancel_delayed_work()
> cannot cancel) wins the race for tz->lock and doesn't see tz->state
> TZ_STATE_FLAG_SUSPENDED set, so it runs, and say it might take long.
>
> Now, the workqueue flush blocks on it, also taking long, which thus
> blocks other PM notifiers.
>
> > The second failing scenario is addressed by adding a tz->state check
> > to thermal_zone_device_resume() to prevent it from reinitializing
> > the poll_queue delayed work if the thermal zone is going away.
>
> This also held OK in the thinking of corner cases.

Thanks for the feedback!

^ permalink raw reply

* Re: [PATCH v4 08/12] amd-pstate-ut: Add module parameter to select testcases
From: Mario Limonciello @ 2026-03-26 18:46 UTC (permalink / raw)
  To: Gautham R. Shenoy, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm
In-Reply-To: <20260326114756.20374-9-gautham.shenoy@amd.com>



On 3/26/26 06:47, Gautham R. Shenoy wrote:
> Currently when amd-pstate-ut test module is loaded, it runs all the
> tests from amd_pstate_ut_cases[] array.
> 
> Add a module parameter named "test_list" that accepts a
> comma-delimited list of test names, allowing users to run a
> selected subset of tests. When the parameter is omitted or empty, all
> tests are run as before.
> 
> Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> ---
>   drivers/cpufreq/amd-pstate-ut.c | 31 ++++++++++++++++++++++++++++++-
>   1 file changed, 30 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
> index 447b9aa5ce40..3dcdf56883a6 100644
> --- a/drivers/cpufreq/amd-pstate-ut.c
> +++ b/drivers/cpufreq/amd-pstate-ut.c
> @@ -35,6 +35,10 @@
>   
>   #include "amd-pstate.h"
>   
> +static char *test_list;
> +module_param(test_list, charp, 0444);
> +MODULE_PARM_DESC(test_list,
> +	"Comma-delimited list of tests to run (empty means run all tests)");
>   
>   struct amd_pstate_ut_struct {
>   	const char *name;
> @@ -58,6 +62,25 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
>   	{"amd_pstate_ut_check_driver",	   amd_pstate_ut_check_driver     }
>   };
>   
> +static bool test_in_list(const char *list, const char *name)
> +{
> +	size_t name_len = strlen(name);
> +	const char *p = list;
> +
> +	while (*p) {
> +		const char *sep = strchr(p, ',');
> +		size_t token_len = sep ? sep - p : strlen(p);
> +
> +		if (token_len == name_len && !strncmp(p, name, token_len))
> +			return true;
> +		if (!sep)
> +			break;
> +		p = sep + 1;
> +	}
> +
> +	return false;
> +}
> +
>   static bool get_shared_mem(void)
>   {
>   	bool result = false;
> @@ -275,7 +298,13 @@ static int __init amd_pstate_ut_init(void)
>   	u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
>   
>   	for (i = 0; i < arr_size; i++) {
> -		int ret = amd_pstate_ut_cases[i].func(i);
> +		int ret;
> +
> +		if (test_list && *test_list &&
> +		    !test_in_list(test_list, amd_pstate_ut_cases[i].name))
> +			continue;
> +
> +		ret = amd_pstate_ut_cases[i].func(i);
>   
>   		if (ret)
>   			pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);


^ permalink raw reply

* Re: [PATCH v1] thermal: core: Address thermal zone removal races with resume
From: Mauricio Faria de Oliveira @ 2026-03-26 18:35 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM, LKML, Daniel Lezcano, Lukasz Luba
In-Reply-To: <12876512.O9o76ZdvQC@rafael.j.wysocki>

On 2026-03-26 08:45, Rafael J. Wysocki wrote:
> Address the first failing scenario by ensuring that no thermal work
> items will be running when thermal_pm_notify_complete() is called.
> For this purpose, first move the cancel_delayed_work() call from
> thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent
> new work from entering the workqueue going forward.  Next, switch
> over to using a dedicated workqueue for thermal events and update
> the code in thermal_pm_notify() to flush that workqueue after
> thermal_pm_notify_prepare() has returned which will take care of
> all leftover thermal work already on the workqueue (that leftover
> work would do nothing useful anyway because all of the thermal zones
> have been flagged as suspended).

Thanks for coming up with this alternative. I spent some time earlier
today thinking of corner cases in that it might fail, and it held OK.

However, slightly unrelated: apparently, flushing the workqueue in
thermal_pm_notify() reintroduces the issue addressed by the Fixes:
commit, but moving it from PM_POST_* to PM_*_PREPARE?

IIIUC, that issue is __thermal_zone_device_update() might take long
thus block other thermal zones and other PM notifiers after thermal.

Apparently, at least the latter also applies to PM_*_PREPARE?

Say, a currently running work item (i.e., that cancel_delayed_work() 
cannot cancel) wins the race for tz->lock and doesn't see tz->state
TZ_STATE_FLAG_SUSPENDED set, so it runs, and say it might take long.

Now, the workqueue flush blocks on it, also taking long, which thus
blocks other PM notifiers.
 
> The second failing scenario is addressed by adding a tz->state check
> to thermal_zone_device_resume() to prevent it from reinitializing
> the poll_queue delayed work if the thermal zone is going away.

This also held OK in the thinking of corner cases.

Thanks,

-- 
Mauricio

^ permalink raw reply

* [GIT PULL] Power management fixes for v7.0-rc6
From: Rafael J. Wysocki @ 2026-03-26 18:12 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Linux PM, Linux Kernel Mailing List, Viresh Kumar

Hi Linus,

Please pull from the tag

 git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git \
 pm-7.0-rc6

with top-most commit 042f99c493c2cef3b15de2df19c06df514d39ac7

 Merge branch 'pm-sleep'

on top of commit c369299895a591d96745d6492d4888259b004a9e

 Linux 7.0-rc5

to receive power management fixes for 7.0-rc6.

These fix two cpufreq issues, one in the core and one in the
conservative governor, and two issues related to system sleep:

 - Restore the cpufreq core behavior changed inadvertently during the
   6.19 development cycle to call cpufreq_frequency_table_cpuinfo()
   for cpufreq policies getting re-initialized which ensures that
   policy->max and policy->cpuinfo_max_freq will be valid going
   forward (Viresh Kumar)

 - Adjust the cached requested frequency in the conservative cpufreq
   governor on policy limits changes to prevent it from becoming stale
   in some cases (Viresh Kumar)

 - Prevent pm_restore_gfp_mask() from triggering a WARN_ON() in some
   code paths in which it is legitimately called without invoking
   pm_restrict_gfp_mask() previously (Youngjun Park)

 - Update snapshot_write_finalize() to take trailing zero pages into
   account properly which prevents user space restore from failing
   subsequently in some cases (Alberto Garcia)

Thanks!


---------------

Alberto Garcia (1):
      PM: hibernate: Drain trailing zero pages on userspace restore

Viresh Kumar (2):
      cpufreq: Don't skip cpufreq_frequency_table_cpuinfo()
      cpufreq: conservative: Reset requested_freq on limits change

Youngjun Park (1):
      PM: sleep: Drop spurious WARN_ON() from pm_restore_gfp_mask()

---------------

 drivers/cpufreq/cpufreq.c              |  9 +++------
 drivers/cpufreq/cpufreq_conservative.c | 12 ++++++++++++
 drivers/cpufreq/cpufreq_governor.c     |  3 +++
 drivers/cpufreq/cpufreq_governor.h     |  1 +
 drivers/cpufreq/freq_table.c           |  4 ++++
 kernel/power/main.c                    |  2 +-
 kernel/power/snapshot.c                | 11 +++++++++++
 7 files changed, 35 insertions(+), 7 deletions(-)

^ permalink raw reply

* [GIT PULL] Thermal control fix for v7.0-rc6
From: Rafael J. Wysocki @ 2026-03-26 18:11 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Linux PM, Linux Kernel Mailing List, Daniel Lezcano

Hi Linus,

Please pull from the tag

 git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git \
 thermal-7.0-rc6

with top-most commit 7dfe9846016b15816e287a4650be1ff1b48c5ab4

 thermal: intel: int340x: soc_slider: Set offset only for balanced mode

on top of commit c369299895a591d96745d6492d4888259b004a9e

 Linux 7.0-rc5

to receive a thermal control fix for 7.0-rc6.

This prevents the int340x thermal driver from taking the power slider
offset parameter into account incorrectly in some cases (Srinivas
Pandruvada).

Thanks!


---------------

Srinivas Pandruvada (1):
      thermal: intel: int340x: soc_slider: Set offset only for balanced mode

---------------

 .../thermal/intel/int340x_thermal/processor_thermal_soc_slider.c  | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

^ permalink raw reply

* Re: [PATCH] thermal: core: fix use-after-free due to init/cancel delayed_work race
From: Mauricio Faria de Oliveira @ 2026-03-26 17:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-pm, linux-kernel,
	kernel-dev, syzbot+3b3852c6031d0f30dfaf
In-Reply-To: <CAJZ5v0goUgYXwyR-G_5zu+wTfbCH5B4B0JL1pU9+cdHbKqT-Lw@mail.gmail.com>

On 2026-03-25 17:20, Rafael J. Wysocki wrote:
> On Wed, Mar 25, 2026 at 4:13 PM Mauricio Faria de Oliveira
> <mfo@igalia.com> wrote:
>>
>> On 2026-03-25 11:28, Mauricio Faria de Oliveira wrote:
>> > On 2026-03-25 11:17, Mauricio Faria de Oliveira wrote:
>> >> Thanks for looking into this.
>> >>
>> >> On 2026-03-25 09:47, Rafael J. Wysocki wrote:
>> >>> I can see the one between thermal_zone_device_unregister() and
>> >>> thermal_zone_device_resume(), but that can be addressed by adding a
>> >>> TZ_STATE_FLAG_EXIT check to the latter AFAICS.
>> >>
>> >
>> > Please disregard this paragraph; I incorrectly read/wrote _resume()
>> > as thermal_zone_pm_complete() discussed above. The rest should be
>> > right. I'll review this and get back shortly.
>> >
>> >> In the example describe above and detailed below, apparently that
>> >> is not sufficient, if I'm not missing anything. See, if _resume()
>> >> is reached with thermal_list_lock held, thermal_zone_device_exit()
>> >> is waiting for thermal_list_lock before setting TZ_STATE_FLAG_EXIT,
>> >> thus a check for it in _resume() would find it clear yet.
>>
>> Ok, similarly:
>>
>> Say, thermal_pm_notify() -> thermal_pm_notify_complete() ->
>> thermal_zone_pm_complete()
>> run before thermal_zone_device_unregister() is called;
>> thermal_zone_device_resume()
>> starts, and by now thermal_zone_device_unregister() is called.
>>
>> If thermal_zone_device_resume() wins the race over thermal_zone_exit()
>> for guard(thermal_zone(tz) (tz->lock), it sees TZ_STATE_FLAG_EXIT clear;
>> note its callees (eg, thermal_zone_device_init()) run with tz->lock
>> held,
>> so they see it clear as well.
>>
>> So, thermal_zone_device_init() calls INIT_DELAYED_WORK(), everything
>> returns, tz->lock is released and the thermal_zone_device_unregister()
>> -> thermal_zone_exit() path can continue to run.
>>
>> Only now thermal_zone_exit() sets TZ_STATE_FLAG_EXIT (too late),
>> returns.
>> cancel_delayed_work_sync() does not wait for
>> thermal_zone_device_resume()
>> due to INIT_DELAYED_WORK() in thermal_zone_device_init(); and kfree(tz).
> 
> Wait.
> 
> I'm not sure why this matters because the zone lock is dropped at the
> end of thermal_zone_device_resume() at which point it has done
> everything it had to do.  Then, the cancel_delayed_work_sync() in
> thermal_zone_device_unregister() should properly wait for the work
> item queued up by thermal_zone_device_resume().

Oh, indeed, you're right.

>> Then, thermal_zone_device_resume() accesses tz and hits use-after-free.
> 
> Say thermal_zone_device_resume() has lost the race for tz->lock to
> thermal_zone_exit() though.  It runs after the latter and
> reinitializes the delayed work.  Now, thermal_zone_device_unregister()
> continues and cancel_delayed_work_sync() doesn't see the
> thermal_zone_device_resume() running in parallel with it any more, so
> it doesn't wait and if thermal_zone_device_unregister() continues to
> run, it may free tz prematurely.
> 
> I claim that this can be prevented by adding a tz->state check to
> thermal_zone_device_resume() before the reinitialization of the
> delayed work.  Namely, if it loses the race tz->lock to
> thermal_zone_exit(), the new check will trigger and the delayed work
> will not be reinitialized, so the cancel_delayed_work_sync() will wait
> for it to complete.

I see it in this case now, thanks for clarifying.

-- 
Mauricio

^ permalink raw reply


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